001package jmri.jmrit.operations.trains.trainbuilder; 002 003import java.io.IOException; 004import java.util.Date; 005import java.util.List; 006 007import jmri.jmrit.operations.locations.Location; 008import jmri.jmrit.operations.locations.Track; 009import jmri.jmrit.operations.routes.RouteLocation; 010import jmri.jmrit.operations.setup.Setup; 011import jmri.jmrit.operations.trains.*; 012import jmri.jmrit.operations.trains.csv.TrainCsvManifest; 013import jmri.util.swing.JmriJOptionPane; 014 015/** 016 * Builds a train and then creates the train's manifest. 017 * 018 * @author Daniel Boudreau Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 019 * 2014, 2015, 2021, 2026 020 */ 021public class TrainBuilder extends TrainBuilderCars { 022 023 /** 024 * Build rules: 025 * <ol> 026 * <li>Need at least one location in route to build train 027 * <li>Select only locos and cars that the train can service 028 * <li>If required, add caboose or car with FRED to train 029 * <li>When departing staging find a track matching train requirements 030 * <li>All cars and locos on one track must leave staging 031 * <li>Optionally block cars from staging 032 * <li>Route cars with home divisions 033 * <li>Route cars with custom loads or final destinations. 034 * <li>Service locations based on train direction, location car types, roads 035 * and loads. 036 * <li>Ignore track direction when train is a local (serves one location) 037 * </ol> 038 * <p> 039 * History: 040 * <p> 041 * First version of train builder found cars along a train's route and 042 * assigned destinations (tracks) willing to accept the car. This is called 043 * the random method as cars just bounce around the layout without purpose. 044 * Afterwards custom loads and routing was added to the program. Cars with 045 * custom loads or final destinations move with purpose as those cars are 046 * routed. The last major feature added was car divisions. Cars assigned a 047 * division are always routed. 048 * <p> 049 * The program was written around the concept of a build report. The report 050 * provides a description of the train build process and the steps taken to 051 * place rolling stock in a train. The goal was to help users understand why 052 * rolling stock was either assigned to the train or not, and which choices 053 * the program had available when determining an engine's or car's 054 * destination. 055 * 056 * @param train the train that is to be built 057 * @return True if successful. 058 */ 059 public boolean build(Train train) { 060 setTrain(train); 061 try { 062 build(); 063 return true; 064 } catch (BuildFailedException e) { 065 buildFailed(e); 066 return false; 067 } 068 } 069 070 private void build() throws BuildFailedException { 071 setStartTime(new Date()); 072 log.debug("Building train ({})", getTrain().getName()); 073 074 getTrain().setStatusCode(Train.CODE_BUILDING); 075 getTrain().setBuilt(false); 076 getTrain().setLeadEngine(null); 077 078 createBuildReportFile(); // backup build report and create new 079 showBuildReportInfo(); // add the build report header information 080 setUpRoute(); // load route, departure and terminate locations 081 showTrainBuildOptions(); // show the build options 082 showSpecificTrainBuildOptions(); // show the train build options 083 showAndInitializeTrainRoute(); // show the train's route and initialize 084 showIfLocalSwitcher(); // show if this train a switcher 085 showTrainRequirements(); // show how many engines, caboose, FRED changes 086 showTrainServices(); // engine roads, owners, built dates, and types 087 getAndRemoveEnginesFromList(); // get a list of available engines 088 showEnginesByLocation(); // list available engines by location 089 determineIfTrainTerminatesIntoStaging(); // find staging terminus track 090 determineIfTrainDepartsStagingAndAddEngines(); // add engines if staging 091 addEnginesToTrain(); // 1st, 2nd and 3rd engine swaps in a train's route 092 showTrainCarRoads(); // show car roads that this train will service 093 showTrainCabooseRoads(); // show caboose roads that this train will service 094 showTrainCarTypes(); // show car types that this train will service 095 showTrainLoadNames(); // show load names that this train will service 096 createCarList(); // remove unwanted cars 097 adjustCarsInStaging(); // adjust for cars on one staging track 098 showCarsByLocation(); // list available cars by location 099 sortCarsOnFifoLifoTracks(); // sort cars on FIFO or LIFO tracks 100 saveCarFinalDestinations(); // save car's final dest and schedule id 101 addCabooseOrFredToTrain(); // caboose and FRED changes 102 removeCaboosesAndCarsWithFred(); // done with cabooses and FRED 103 blockCarsFromStaging(); // block cars from staging 104 showTracksNotQuickService(); // list tracks that aren't using quick service 105 106 addCarsToTrain(); // finds and adds cars to the train (main routine) 107 108 checkStuckCarsInStaging(); // determine if cars are stuck in staging 109 setTrainBuildStatus(); // show how well the build went 110 checkEngineHP(); // determine if train has appropriate engine HP 111 checkNumnberOfEnginesNeededHPT(); // check train engine requirements 112 showCarsNotRoutable(); // list cars that couldn't be routed 113 finshBuildReport(); // number of warnings, build time 114 115 getBuildReport().flush(); 116 getBuildReport().close(); 117 118 createManifests(); // now make Manifests 119 120 // notify locations have been modified by this train's build 121 for (Location location : _modifiedLocations) { 122 location.setStatus(Location.MODIFIED); 123 } 124 125 // operations automations use wait for train built to create custom 126 // manifests and switch lists 127 getTrain().setPrinted(false); 128 getTrain().setSwitchListStatus(Train.UNKNOWN); 129 getTrain().setCurrentLocation(getTrain().getTrainDepartsRouteLocation()); 130 getTrain().setBuilt(true); 131 // create and place train icon 132 getTrain().moveTrainIcon(getTrain().getTrainDepartsRouteLocation()); 133 134 log.debug("Done building train ({})", getTrain().getName()); 135 showWarningMessage(); 136 } 137 138 /** 139 * Figures out if the train terminates into staging, and if true, sets the 140 * termination track. Note if the train is returning back to the same track 141 * in staging getTerminateStagingTrack() is null, and is loaded later when the 142 * departure track is determined. 143 * 144 * @throws BuildFailedException if staging track can't be found 145 */ 146 private void determineIfTrainTerminatesIntoStaging() throws BuildFailedException { 147 // does train terminate into staging? 148 setTerminateStagingTrack(null); 149 List<Track> stagingTracksTerminate = getTerminateLocation().getTracksByMoves(Track.STAGING); 150 if (stagingTracksTerminate.size() > 0) { 151 addLine(THREE, BLANK_LINE); 152 addLine(ONE, Bundle.getMessage("buildTerminateStaging", getTerminateLocation().getName(), 153 Integer.toString(stagingTracksTerminate.size()))); 154 if (stagingTracksTerminate.size() > 1 && Setup.isStagingPromptToEnabled()) { 155 setTerminateStagingTrack(promptToStagingDialog()); 156 setStartTime(new Date()); // reset build time since user can take 157 // awhile to pick 158 } else { 159 // is this train returning to the same staging in aggressive 160 // mode? 161 if (getDepartureLocation() == getTerminateLocation() && 162 Setup.isBuildAggressive() && 163 Setup.isStagingTrackImmediatelyAvail()) { 164 addLine(ONE, Bundle.getMessage("buildStagingReturn", getTerminateLocation().getName())); 165 } else { 166 for (Track track : stagingTracksTerminate) { 167 if (checkTerminateStagingTrack(track)) { 168 setTerminateStagingTrack(track); 169 addLine(ONE, Bundle.getMessage("buildStagingAvail", 170 getTerminateStagingTrack().getName(), getTerminateLocation().getName())); 171 break; 172 } 173 } 174 } 175 } 176 if (getTerminateStagingTrack() == null) { 177 // is this train returning to the same staging in aggressive 178 // mode? 179 if (getDepartureLocation() == getTerminateLocation() && 180 Setup.isBuildAggressive() && 181 Setup.isStagingTrackImmediatelyAvail()) { 182 log.debug("Train is returning to same track in staging"); 183 } else { 184 addLine(ONE, Bundle.getMessage("buildErrorStagingFullNote")); 185 throw new BuildFailedException( 186 Bundle.getMessage("buildErrorStagingFull", getTerminateLocation().getName())); 187 } 188 } 189 } 190 } 191 192 /** 193 * Figures out if the train is departing staging, and if true, sets the 194 * departure track. Also sets the arrival track if the train is returning to 195 * the same departure track in staging. 196 * 197 * @throws BuildFailedException if staging departure track not found 198 */ 199 private void determineIfTrainDepartsStagingAndAddEngines() throws BuildFailedException { 200 // allow up to two engine and caboose swaps in the train's route 201 RouteLocation engineTerminatesFirstLeg = getTrain().getTrainTerminatesRouteLocation(); 202 203 // Adjust where the locos will terminate 204 if ((getTrain().getSecondLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 205 getTrain().getSecondLegStartRouteLocation() != null) { 206 engineTerminatesFirstLeg = getTrain().getSecondLegStartRouteLocation(); 207 } else if ((getTrain().getThirdLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 208 getTrain().getThirdLegStartRouteLocation() != null) { 209 engineTerminatesFirstLeg = getTrain().getThirdLegStartRouteLocation(); 210 } 211 212 // determine if train is departing staging 213 List<Track> stagingTracks = getDepartureLocation().getTracksByMoves(Track.STAGING); 214 if (stagingTracks.size() > 0) { 215 addLine(THREE, BLANK_LINE); 216 addLine(ONE, Bundle.getMessage("buildDepartStaging", getDepartureLocation().getName(), 217 Integer.toString(stagingTracks.size()))); 218 if (stagingTracks.size() > 1 && Setup.isStagingPromptFromEnabled()) { 219 setDepartureStagingTrack(promptFromStagingDialog()); 220 setStartTime(new Date()); // restart build timer 221 if (getDepartureStagingTrack() == null) { 222 showTrainRequirements(); 223 throw new BuildFailedException( 224 Bundle.getMessage("buildErrorStagingEmpty", getDepartureLocation().getName())); 225 } 226 } else { 227 for (Track track : stagingTracks) { 228 // is the departure track available? 229 if (!checkDepartureStagingTrack(track)) { 230 addLine(SEVEN, 231 Bundle.getMessage("buildStagingTrackRestriction", track.getName(), getTrain().getName())); 232 continue; 233 } 234 setDepartureStagingTrack(track); 235 // try each departure track for the required engines 236 if (getEngines(getTrain().getNumberEngines(), getTrain().getEngineModel(), getTrain().getEngineRoad(), 237 getTrain().getTrainDepartsRouteLocation(), engineTerminatesFirstLeg)) { 238 addLine(SEVEN, Bundle.getMessage("buildDoneAssignEnginesStaging")); 239 break; // done! 240 } 241 setDepartureStagingTrack(null); 242 } 243 } 244 if (getDepartureStagingTrack() == null) { 245 showTrainRequirements(); 246 throw new BuildFailedException(Bundle.getMessage("buildErrorStagingEmpty", getDepartureLocation().getName())); 247 } 248 } 249 getTrain().setTerminationTrack(getTerminateStagingTrack()); 250 getTrain().setDepartureTrack(getDepartureStagingTrack()); 251 } 252 253 /** 254 * Adds and removes cabooses or car with FRED in the train's route. Up to 2 255 * caboose changes. 256 * 257 * @throws BuildFailedException 258 */ 259 private void addCabooseOrFredToTrain() throws BuildFailedException { 260 // allow up to two caboose swaps in the train's route 261 RouteLocation cabooseOrFredTerminatesFirstLeg = getTrain().getTrainTerminatesRouteLocation(); 262 RouteLocation cabooseOrFredTerminatesSecondLeg = getTrain().getTrainTerminatesRouteLocation(); 263 264 // determine if there are any caboose changes 265 if ((getTrain().getSecondLegOptions() & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE || 266 (getTrain().getSecondLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE) { 267 cabooseOrFredTerminatesFirstLeg = getTrain().getSecondLegStartRouteLocation(); 268 } else if ((getTrain().getThirdLegOptions() & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE || 269 (getTrain().getThirdLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE) { 270 cabooseOrFredTerminatesFirstLeg = getTrain().getThirdLegStartRouteLocation(); 271 } 272 if ((getTrain().getThirdLegOptions() & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE || 273 (getTrain().getThirdLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE) { 274 cabooseOrFredTerminatesSecondLeg = getTrain().getThirdLegStartRouteLocation(); 275 } 276 277 // Do caboose changes in reverse order in case there isn't enough track 278 // space second caboose change? 279 if ((getTrain().getThirdLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE && 280 getTrain().getThirdLegStartRouteLocation() != null && 281 getTrain().getTrainTerminatesRouteLocation() != null) { 282 getCaboose(getTrain().getThirdLegCabooseRoad(), _thirdLeadEngine, getTrain().getThirdLegStartRouteLocation(), 283 getTrain().getTrainTerminatesRouteLocation(), true); 284 } 285 286 // first caboose change? 287 if ((getTrain().getSecondLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE && 288 getTrain().getSecondLegStartRouteLocation() != null && 289 cabooseOrFredTerminatesSecondLeg != null) { 290 getCaboose(getTrain().getSecondLegCabooseRoad(), _secondLeadEngine, getTrain().getSecondLegStartRouteLocation(), 291 cabooseOrFredTerminatesSecondLeg, true); 292 } 293 294 // departure caboose or car with FRED 295 getCaboose(getTrain().getCabooseRoad(), getTrain().getLeadEngine(), getTrain().getTrainDepartsRouteLocation(), 296 cabooseOrFredTerminatesFirstLeg, getTrain().isCabooseNeeded()); 297 getCarWithFred(getTrain().getCabooseRoad(), getTrain().getTrainDepartsRouteLocation(), cabooseOrFredTerminatesFirstLeg); 298 } 299 300 /** 301 * Routine to find and add available cars to the train. In normal mode 302 * performs a single pass. In aggressive mode, will perform multiple passes. 303 * If train is departing staging and in aggressive mode, will try again 304 * using normal mode if there's a train build issue. 305 * 306 * @throws BuildFailedException 307 */ 308 private void addCarsToTrain() throws BuildFailedException { 309 addLine(THREE, 310 Bundle.getMessage("buildTrain", getTrain().getNumberCarsRequested(), getTrain().getName(), getCarList().size())); 311 312 if (Setup.isBuildAggressive() && !getTrain().isBuildTrainNormalEnabled()) { 313 // perform a multiple pass build for this train, default is two 314 // passes 315 int pass = 0; 316 while (pass++ < Setup.getNumberPasses()) { 317 addCarsToTrain(pass, false); 318 } 319 // are cars stuck in staging? 320 secondAttemptNormalBuild(); 321 } else { 322 addCarsToTrain(Setup.getNumberPasses(), true); // normal build one 323 // pass 324 } 325 } 326 327 /** 328 * If cars stuck in staging, try building again in normal mode. 329 * 330 * @throws BuildFailedException 331 */ 332 private void secondAttemptNormalBuild() throws BuildFailedException { 333 if (Setup.isStagingTryNormalBuildEnabled() && isCarStuckStaging()) { 334 addLine(ONE, Bundle.getMessage("buildFailedTryNormalMode")); 335 addLine(ONE, BLANK_LINE); 336 getTrain().reset(); 337 getTrain().setStatusCode(Train.CODE_BUILDING); 338 getTrain().setLeadEngine(null); 339 // using the same departure and termination tracks 340 getTrain().setDepartureTrack(getDepartureStagingTrack()); 341 getTrain().setTerminationTrack(getTerminateStagingTrack()); 342 showAndInitializeTrainRoute(); 343 getAndRemoveEnginesFromList(); 344 addEnginesToTrain(); 345 createCarList(); 346 adjustCarsInStaging(); 347 showCarsByLocation(); 348 addCabooseOrFredToTrain(); 349 removeCaboosesAndCarsWithFred(); 350 saveCarFinalDestinations(); // save final destination and schedule 351 // id 352 blockCarsFromStaging(); // block cars from staging 353 addCarsToTrain(Setup.getNumberPasses(), true); // try normal build 354 // one pass 355 } 356 } 357 358 /** 359 * Main routine to place cars into the train. Can be called multiple times. 360 * When departing staging, ignore staged cars on the first pass unless the 361 * option to build normal was selected by user. 362 * 363 * @param pass Which pass when there are multiple passes requested by 364 * user. 365 * @param normal True if single pass or normal mode is requested by user. 366 * @throws BuildFailedException 367 */ 368 private void addCarsToTrain(int pass, boolean normal) throws BuildFailedException { 369 addLine(THREE, BLANK_LINE); 370 if (normal) { 371 addLine(THREE, Bundle.getMessage("NormalModeWhenBuilding")); 372 } else { 373 addLine(THREE, Bundle.getMessage("buildMultiplePass", pass, Setup.getNumberPasses())); 374 } 375 // now go through each location starting at departure and place cars as 376 // requested 377 for (RouteLocation rl : getRouteList()) { 378 if (getTrain().isLocationSkipped(rl)) { 379 addLine(ONE, 380 Bundle.getMessage("buildLocSkipped", rl.getName(), rl.getId(), getTrain().getName())); 381 continue; 382 } 383 if (!rl.isPickUpAllowed() && !rl.isLocalMovesAllowed()) { 384 addLine(ONE, 385 Bundle.getMessage("buildLocNoPickups", getTrain().getRoute().getName(), rl.getId(), rl.getName())); 386 continue; 387 } 388 // no pick ups from staging unless at the start of the train's route 389 if (rl != getTrain().getTrainDepartsRouteLocation() && rl.getLocation().isStaging()) { 390 addLine(ONE, Bundle.getMessage("buildNoPickupsFromStaging", rl.getName())); 391 continue; 392 } 393 // the next check provides a build report message if there's an 394 // issue with the train direction 395 if (!checkPickUpTrainDirection(rl)) { 396 continue; 397 } 398 _completedMoves = 0; // moves completed for this location 399 _reqNumOfMoves = rl.getMaxCarMoves() - rl.getCarMoves(); 400 401 if (!normal) { 402 if (rl == getTrain().getTrainDepartsRouteLocation()) { 403 _reqNumOfMoves = (rl.getMaxCarMoves() - rl.getCarMoves()) * pass / Setup.getNumberPasses(); 404 } else if (pass == 1) { 405 _reqNumOfMoves = (rl.getMaxCarMoves() - rl.getCarMoves()) / 2; 406 // round up requested moves 407 int remainder = (rl.getMaxCarMoves() - rl.getCarMoves()) % 2; 408 if (remainder > 0) { 409 _reqNumOfMoves++; 410 } 411 } 412 } 413 414 // if departing staging make adjustments 415 if (rl == getTrain().getTrainDepartsRouteLocation()) { 416 if (pass == 1) { 417 makeAdjustmentsIfDepartingStaging(); 418 } else { 419 restoreCarsIfDepartingStaging(); 420 } 421 } 422 423 int saveReqMoves = _reqNumOfMoves; // save a copy for status message 424 addLine(ONE, 425 Bundle.getMessage("buildLocReqMoves", rl.getName(), rl.getId(), _reqNumOfMoves, 426 rl.getMaxCarMoves() - rl.getCarMoves(), rl.getMaxCarMoves())); 427 addLine(FIVE, BLANK_LINE); 428 429 // show the car load generation options for staging 430 if (rl == getTrain().getTrainDepartsRouteLocation()) { 431 showLoadGenerationOptionsStaging(); 432 } 433 434 _carIndex = 0; // see reportCarsNotMoved(rl) below 435 436 findDestinationsForCarsFromLocation(rl, false); // first pass 437 438 // perform 2nd pass if aggressive mode and there are requested 439 // moves. This will perform local moves at this location, services 440 // off spot tracks, only in aggressive mode and at least one car 441 // has a new destination 442 if (Setup.isBuildAggressive() && saveReqMoves != _reqNumOfMoves) { 443 log.debug("Perform extra pass at location ({})", rl.getName()); 444 // use up to half of the available moves left for this location 445 if (_reqNumOfMoves < (rl.getMaxCarMoves() - rl.getCarMoves()) / 2) { 446 _reqNumOfMoves = (rl.getMaxCarMoves() - rl.getCarMoves()) / 2; 447 } 448 findDestinationsForCarsFromLocation(rl, true); // second pass 449 450 // we might have freed up space at a spur that has an alternate 451 // track 452 if (redirectCarsFromAlternateTrack()) { 453 addLine(SEVEN, BLANK_LINE); 454 } 455 } 456 if (rl == getTrain().getTrainDepartsRouteLocation() && pass == Setup.getNumberPasses() && isCarStuckStaging()) { 457 return; // report ASAP that there are stuck cars 458 } 459 addLine(ONE, 460 Bundle.getMessage("buildStatusMsg", 461 (saveReqMoves <= _completedMoves ? Bundle.getMessage("Success") 462 : Bundle.getMessage("Partial")), 463 Integer.toString(_completedMoves), Integer.toString(saveReqMoves), rl.getName(), 464 getTrain().getName())); 465 466 if (_reqNumOfMoves <= 0 && pass == Setup.getNumberPasses()) { 467 showCarsNotMoved(rl); 468 } 469 } 470 } 471 472 private void setTrainBuildStatus() { 473 if (_numberCars < getTrain().getNumberCarsRequested()) { 474 getTrain().setStatusCode(Train.CODE_PARTIAL_BUILT); 475 addLine(ONE, 476 Train.PARTIAL_BUILT + 477 " " + 478 getTrain().getNumberCarsWorked() + 479 "/" + 480 getTrain().getNumberCarsRequested() + 481 " " + 482 Bundle.getMessage("cars")); 483 } else { 484 getTrain().setStatusCode(Train.CODE_BUILT); 485 addLine(ONE, 486 Train.BUILT + " " + getTrain().getNumberCarsWorked() + " " + Bundle.getMessage("cars")); 487 } 488 } 489 490 private void createManifests() throws BuildFailedException { 491 new TrainManifest(getTrain()); 492 try { 493 new JsonManifest(getTrain()).build(); 494 } catch (IOException | RuntimeException ex) { 495 log.error("Unable to create JSON manifest: {}", ex.getLocalizedMessage()); 496 log.error("JSON manifest stack trace:", ex); 497 throw new BuildFailedException(ex); 498 } 499 new TrainCsvManifest(getTrain()); 500 } 501 502 private void showWarningMessage() { 503 if (trainManager.isBuildMessagesEnabled() && _warnings > 0) { 504 JmriJOptionPane.showMessageDialog(null, 505 Bundle.getMessage("buildCheckReport", getTrain().getName(), getTrain().getDescription()), 506 Bundle.getMessage("buildWarningMsg", getTrain().getName(), _warnings), 507 JmriJOptionPane.WARNING_MESSAGE); 508 } 509 } 510 511 private void buildFailed(BuildFailedException e) { 512 String msg = e.getMessage(); 513 getTrain().setBuildFailedMessage(msg); 514 getTrain().setBuildFailed(true); 515 log.debug(msg); 516 517 if (trainManager.isBuildMessagesEnabled()) { 518 // don't pass the object getTrain() to the GUI, can cause thread lock 519 String trainName = getTrain().getName(); 520 String trainDescription = getTrain().getDescription(); 521 if (e.getExceptionType().equals(BuildFailedException.NORMAL)) { 522 JmriJOptionPane.showMessageDialog(null, msg, 523 Bundle.getMessage("buildErrorMsg", trainName, trainDescription), JmriJOptionPane.ERROR_MESSAGE); 524 } else { 525 // build error, could not find destinations for cars departing 526 // staging 527 Object[] options = {Bundle.getMessage("buttonRemoveCars"), Bundle.getMessage("ButtonOK")}; 528 int results = JmriJOptionPane.showOptionDialog(null, msg, 529 Bundle.getMessage("buildErrorMsg", trainName, trainDescription), 530 JmriJOptionPane.DEFAULT_OPTION, JmriJOptionPane.ERROR_MESSAGE, null, options, options[1]); 531 if (results == 0) { 532 log.debug("User requested that cars be removed from staging track"); 533 removeCarsFromStaging(); 534 } 535 } 536 int size = carManager.getList(getTrain()).size(); 537 if (size > 0) { 538 if (JmriJOptionPane.showConfirmDialog(null, 539 Bundle.getMessage("buildCarsResetTrain", size, trainName), 540 Bundle.getMessage("buildResetTrain"), 541 JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) { 542 getTrain().setStatusCode(Train.CODE_TRAIN_RESET); 543 } 544 } else if ((size = engineManager.getList(getTrain()).size()) > 0) { 545 if (JmriJOptionPane.showConfirmDialog(null, 546 Bundle.getMessage("buildEnginesResetTrain", size, trainName), 547 Bundle.getMessage("buildResetTrain"), 548 JmriJOptionPane.YES_NO_OPTION) == JmriJOptionPane.YES_OPTION) { 549 getTrain().setStatusCode(Train.CODE_TRAIN_RESET); 550 } 551 } 552 } else { 553 // build messages disabled 554 // remove cars and engines from this train via property change 555 getTrain().setStatusCode(Train.CODE_TRAIN_RESET); 556 } 557 558 getTrain().setStatusCode(Train.CODE_BUILD_FAILED); 559 560 if (getBuildReport() != null) { 561 addLine(ONE, msg); 562 // Write to disk and close buildReport 563 addLine(ONE, 564 Bundle.getMessage("buildFailedMsg", getTrain().getName())); 565 getBuildReport().flush(); 566 getBuildReport().close(); 567 } 568 } 569 570 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrainBuilder.class); 571 572}