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) { 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 String routeLocationName = splitString(rl.getName()); 059 String locationName = routeLocationName; 060 if (!routeLocationName.equals(previousRouteLocationName)) { 061 printLocationName(fileOut, locationName); 062 if (rl != train.getTrainDepartsRouteLocation()) { 063 fileOut.printRecord("AT", Bundle.getMessage("csvArrivalTime"), train.getExpectedArrivalTime(rl)); // NOI18N 064 } 065 if (rl == train.getTrainDepartsRouteLocation()) { 066 fileOut.printRecord("DT", Bundle.getMessage("csvDepartureTime"), train.getFormatedDepartureTime()); // NOI18N 067 } else if (!rl.getDepartureTime().equals(RouteLocation.NONE)) { 068 fileOut.printRecord("DTR", Bundle.getMessage("csvDepartureTimeRoute"), rl.getFormatedDepartureTime()); // NOI18N 069 } else { 070 fileOut.printRecord("EDT", Bundle.getMessage("csvEstimatedDepartureTime"), train.getExpectedDepartureTime(rl)); // NOI18N 071 } 072 printLocationComment(fileOut, rl.getLocation()); 073 if (Setup.isPrintTruncateManifestEnabled() && rl.getLocation().isSwitchListEnabled()) { 074 fileOut.printRecord("TRUN", Bundle.getMessage("csvTruncate")); 075 } 076 } 077 printRouteLocationComment(fileOut, rl); 078 printTrackComments(fileOut, rl, carList); 079 080 // engine change or helper service? 081 checkForEngineOrCabooseChange(fileOut, train, rl); 082 083 for (Engine engine : engineList) { 084 if (engine.getRouteLocation() == rl) { 085 printEngine(fileOut, engine, "PL", Bundle.getMessage("csvPickUpLoco")); 086 } 087 } 088 for (Engine engine : engineList) { 089 if (engine.getRouteDestination() == rl) { 090 printEngine(fileOut, engine, "SL", Bundle.getMessage("csvSetOutLoco")); 091 } 092 } 093 // block pick up cars 094 // caboose or FRED is placed at end of the train 095 // passenger cars are already blocked in the car list 096 // passenger cars with negative block numbers are placed at 097 // the front of the train, positive numbers at the end of 098 // the train. 099 for (RouteLocation rld : train.getTrainBlockingOrder()) { 100 for (Car car : carList) { 101 if (isNextCar(car, rl, rld)) { 102 newWork = true; 103 int count = 0; 104 if (car.isUtility()) { 105 count = countPickupUtilityCars(carList, car, IS_MANIFEST); 106 if (count == 0) { 107 continue; // already done this set of 108 // utility cars 109 } 110 } 111 printCar(fileOut, car, "PC", Bundle.getMessage("csvPickUpCar"), count); 112 } 113 } 114 } 115 // car set outs 116 for (Car car : carList) { 117 if (car.getRouteDestination() == rl) { 118 newWork = true; 119 int count = 0; 120 if (car.isUtility()) { 121 count = countSetoutUtilityCars(carList, car, false, IS_MANIFEST); 122 if (count == 0) { 123 continue; // already done this set of utility cars 124 } 125 } 126 printCar(fileOut, car, "SC", Bundle.getMessage("csvSetOutCar"), count); 127 } 128 } 129 // car holds 130 List<Car> rsByLocation = carManager.getByLocationList(); 131 List<Car> cList = new ArrayList<>(); 132 for (Car rs : rsByLocation) { 133 if (rs.getLocation() == rl.getLocation() && rs.getRouteLocation() == null && rs.getTrack() != null) { 134 cList.add(rs); 135 } 136 } 137 clearUtilityCarTypes(); // list utility cars by quantity 138 for (Car car : cList) { 139 // list cars on tracks that only this train can service 140 if (!car.getTrack().getLocation().isStaging() 141 && car.getTrack().isPickupTrainAccepted(train) && car.getTrack().getPickupIds().length == 1 142 && car.getTrack().getPickupOption().equals(Track.TRAINS)) { 143 int count = 0; 144 if (car.isUtility()) { 145 count = countPickupUtilityCars(cList, car, !IS_MANIFEST); 146 if (count == 0) { 147 continue; // already done this set of utility cars 148 } 149 } 150 printCar(fileOut, car, "HOLD", Bundle.getMessage("csvHoldCar"), count); 151 } 152 } 153 if (rl != train.getTrainTerminatesRouteLocation()) { 154 // Is the next location the same as the previous? 155 RouteLocation rlNext = train.getRoute().getNextRouteLocation(rl); 156 String nextRouteLocationName = splitString(rlNext.getName()); 157 if (!routeLocationName.equals(nextRouteLocationName)) { 158 if (newWork) { 159 printTrainDeparts(fileOut, locationName, rl.getTrainDirectionString()); 160 printTrainLength(fileOut, train.getTrainLength(rl), train.getNumberEmptyCarsInTrain(rl), 161 train.getNumberCarsInTrain(rl)); 162 printTrainWeight(fileOut, train.getTrainWeight(rl)); 163 newWork = false; 164 } else { 165 fileOut.printRecord("NW", Bundle.getMessage("csvNoWork")); 166 } 167 } 168 } else { 169 printTrainTerminates(fileOut, locationName); 170 } 171 previousRouteLocationName = routeLocationName; 172 } 173 // Are there any cars that need to be found? 174 listCarsLocationUnknown(fileOut); 175 176 fileOut.flush(); 177 fileOut.close(); 178 } catch (IOException e) { 179 log.error("Can not open CSV manifest file: {}", file.getName()); 180 } 181 } 182 183 private final static Logger log = LoggerFactory.getLogger(TrainCsvManifest.class); 184}