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