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