001package jmri.jmrit.operations.trains; 002 003import java.io.*; 004import java.nio.charset.StandardCharsets; 005import java.util.ArrayList; 006import java.util.List; 007 008import org.apache.commons.csv.CSVFormat; 009import org.apache.commons.csv.CSVPrinter; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013import jmri.InstanceManager; 014import jmri.jmrit.operations.locations.Track; 015import jmri.jmrit.operations.rollingstock.cars.Car; 016import jmri.jmrit.operations.rollingstock.engines.Engine; 017import jmri.jmrit.operations.routes.RouteLocation; 018import jmri.jmrit.operations.setup.Setup; 019 020/** 021 * Builds a train's manifest using Comma Separated Values (csv). 022 * 023 * @author Daniel Boudreau Copyright (C) 2011, 2015 024 * 025 */ 026public class TrainCsvManifest extends TrainCsvCommon { 027 028 public TrainCsvManifest(Train train) throws BuildFailedException { 029 if (!Setup.isGenerateCsvManifestEnabled()) { 030 return; 031 } 032 // create comma separated value manifest file 033 File file = InstanceManager.getDefault(TrainManagerXml.class).createTrainCsvManifestFile(train.getName()); 034 035 try (CSVPrinter fileOut = new CSVPrinter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)), 036 CSVFormat.DEFAULT)) { 037 // build header 038 printHeader(fileOut); 039 printRailroadName(fileOut, 040 train.getRailroadName().isEmpty() ? Setup.getRailroadName() : train.getRailroadName()); 041 printTrainName(fileOut, train.getName()); 042 printTrainDescription(fileOut, train.getDescription()); 043 printPrinterName(fileOut, locationManager.getLocationByName(train.getTrainDepartsName()).getDefaultPrinterName()); 044 printLogoURL(fileOut, train); 045 printValidity(fileOut, getDate(true)); 046 printTrainComment(fileOut, train); 047 printRouteComment(fileOut, train); 048 049 // get engine and car lists 050 List<Engine> engineList = engineManager.getByTrainBlockingList(train); 051 List<Car> carList = carManager.getByTrainDestinationList(train); 052 053 boolean newWork = false; 054 String previousRouteLocationName = null; 055 List<RouteLocation> routeList = train.getRoute().getLocationsBySequenceList(); 056 for (RouteLocation rl : routeList) { 057 // print info only if new location 058 if (!rl.getSplitName().equals(previousRouteLocationName)) { 059 printLocationName(fileOut, rl.getSplitName()); 060 if (rl != train.getTrainDepartsRouteLocation()) { 061 fileOut.printRecord("AT", Bundle.getMessage("csvArrivalTime"), train.getExpectedArrivalTime(rl)); // NOI18N 062 } 063 if (rl == train.getTrainDepartsRouteLocation()) { 064 fileOut.printRecord("DT", Bundle.getMessage("csvDepartureTime"), train.getFormatedDepartureTime()); // NOI18N 065 } else if (!rl.getDepartureTime().equals(RouteLocation.NONE)) { 066 fileOut.printRecord("DTR", Bundle.getMessage("csvDepartureTimeRoute"), rl.getFormatedDepartureTime()); // NOI18N 067 } else { 068 fileOut.printRecord("EDT", Bundle.getMessage("csvEstimatedDepartureTime"), train.getExpectedDepartureTime(rl)); // NOI18N 069 } 070 printLocationComment(fileOut, rl.getLocation()); 071 if (Setup.isPrintTruncateManifestEnabled() && rl.getLocation().isSwitchListEnabled()) { 072 fileOut.printRecord("TRUN", Bundle.getMessage("csvTruncate")); 073 } 074 } 075 printRouteLocationComment(fileOut, rl); 076 printTrackComments(fileOut, rl, carList); 077 078 // engine change or helper service? 079 checkForEngineOrCabooseChange(fileOut, train, rl); 080 081 for (Engine engine : engineList) { 082 if (engine.getRouteLocation() == rl) { 083 printEngine(fileOut, engine, "PL", Bundle.getMessage("csvPickUpLoco")); 084 } 085 } 086 for (Engine engine : engineList) { 087 if (engine.getRouteDestination() == rl) { 088 printEngine(fileOut, engine, "SL", Bundle.getMessage("csvSetOutLoco")); 089 } 090 } 091 // block pick up cars 092 // caboose or FRED is placed at end of the train 093 // passenger cars are already blocked in the car list 094 // passenger cars with negative block numbers are placed at 095 // the front of the train, positive numbers at the end of 096 // the train. 097 for (RouteLocation rld : train.getTrainBlockingOrder()) { 098 for (Car car : carList) { 099 if (isNextCar(car, rl, rld)) { 100 newWork = true; 101 int count = 0; 102 if (car.isUtility()) { 103 count = countPickupUtilityCars(carList, car, IS_MANIFEST); 104 if (count == 0) { 105 continue; // already done this set of 106 // utility cars 107 } 108 } 109 printCar(fileOut, car, "PC", Bundle.getMessage("csvPickUpCar"), count); 110 } 111 } 112 } 113 // car set outs 114 for (Car car : carList) { 115 if (car.getRouteDestination() == rl) { 116 newWork = true; 117 int count = 0; 118 if (car.isUtility()) { 119 count = countSetoutUtilityCars(carList, car, false, IS_MANIFEST); 120 if (count == 0) { 121 continue; // already done this set of utility cars 122 } 123 } 124 printCar(fileOut, car, "SC", Bundle.getMessage("csvSetOutCar"), count); 125 } 126 } 127 // car holds 128 List<Car> carsByLocation = carManager.getByLocationList(); 129 List<Car> cList = new ArrayList<>(); 130 for (Car car : carsByLocation) { 131 if (car.getLocation() == rl.getLocation() && car.getRouteLocation() == null && car.getTrack() != null) { 132 cList.add(car); 133 } 134 } 135 clearUtilityCarTypes(); // list utility cars by quantity 136 for (Car car : cList) { 137 // list cars on tracks that only this train can service 138 if (!car.getTrack().getLocation().isStaging() 139 && car.getTrack().isPickupTrainAccepted(train) && car.getTrack().getPickupIds().length == 1 140 && car.getTrack().getPickupOption().equals(Track.TRAINS)) { 141 int count = 0; 142 if (car.isUtility()) { 143 count = countPickupUtilityCars(cList, car, !IS_MANIFEST); 144 if (count == 0) { 145 continue; // already done this set of utility cars 146 } 147 } 148 printCar(fileOut, car, "HOLD", Bundle.getMessage("csvHoldCar"), count); 149 } 150 } 151 if (rl != train.getTrainTerminatesRouteLocation()) { 152 // Is the next location the same as the previous? 153 RouteLocation rlNext = train.getRoute().getNextRouteLocation(rl); 154 if (!rl.getSplitName().equals(rlNext.getSplitName())) { 155 if (newWork) { 156 printTrainDeparts(fileOut, rl.getSplitName(), rl.getTrainDirectionString()); 157 printTrainLength(fileOut, train.getTrainLength(rl), train.getNumberEmptyCarsInTrain(rl), 158 train.getNumberCarsInTrain(rl)); 159 printTrainWeight(fileOut, train.getTrainWeight(rl)); 160 newWork = false; 161 } else { 162 fileOut.printRecord("NW", Bundle.getMessage("csvNoWork")); 163 } 164 } 165 } else { 166 printTrainTerminates(fileOut, rl.getSplitName()); 167 } 168 previousRouteLocationName = rl.getSplitName(); 169 } 170 // Are there any cars that need to be found? 171 listCarsLocationUnknown(fileOut); 172 173 fileOut.flush(); 174 fileOut.close(); 175 } catch (IOException e) { 176 log.error("Can not open CSV manifest file: {}", e.getLocalizedMessage()); 177 throw new BuildFailedException(e); 178 } 179 } 180 181 private final static Logger log = LoggerFactory.getLogger(TrainCsvManifest.class); 182}