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 073 log.debug("Building train ({})", getTrain().getName()); 074 075 getTrain().setStatusCode(Train.CODE_BUILDING); 076 getTrain().setBuilt(false); 077 getTrain().setLeadEngine(null); 078 079 createBuildReportFile(); // backup build report and create new 080 showBuildReportInfo(); // add the build report header information 081 setUpRoute(); // load route, departure and terminate locations 082 showTrainBuildOptions(); // show the build options 083 showSpecificTrainBuildOptions(); // show the train build options 084 showAndInitializeTrainRoute(); // show the train's route and initialize 085 showIfLocalSwitcher(); // show if this train a switcher 086 showTrainRequirements(); // show how many engines, caboose, FRED changes 087 showTrainServices(); // engine roads, owners, built dates, and types 088 getAndRemoveEnginesFromList(); // get a list of available engines 089 showEnginesByLocation(); // list available engines by location 090 determineIfTrainTerminatesIntoStaging(); // find staging terminus track 091 determineIfTrainDepartsStagingAndAddEngines(); // add engines if staging 092 addEnginesToTrain(); // 1st, 2nd and 3rd engine swaps in a train's route 093 showTrainCarRoads(); // show car roads that this train will service 094 showTrainCabooseRoads(); // show caboose roads that this train will service 095 showTrainCarTypes(); // show car types that this train will service 096 showTrainLoadNames(); // show load names that this train will service 097 createCarList(); // remove unwanted cars 098 adjustCarsInStaging(); // adjust for cars on one staging track 099 showCarsByLocation(); // list available cars by location 100 sortCarsOnFifoLifoTracks(); // sort cars on FIFO or LIFO tracks 101 saveCarFinalDestinations(); // save car's final dest and schedule id 102 addCabooseOrFredToTrain(); // caboose and FRED changes 103 removeCaboosesAndCarsWithFred(); // done with cabooses and FRED 104 blockCarsFromStaging(); // block cars from staging 105 showTracksNotQuickService(); // list tracks that aren't using quick service 106 107 addCarsToTrain(); // finds and adds cars to the train (main routine) 108 109 checkStuckCarsInStaging(); // determine if cars are stuck in staging 110 setTrainBuildStatus(); // show how well the build went 111 checkEngineHP(); // determine if train has appropriate engine HP 112 checkNumnberOfEnginesNeededHPT(); // check train engine requirements 113 showCarsNotRoutable(); // list cars that couldn't be routed 114 115 // done building 116 if (_warnings > 0) { 117 addLine(ONE, Bundle.getMessage("buildWarningMsg", getTrain().getName(), _warnings)); 118 } 119 addLine(FIVE, 120 Bundle.getMessage("buildTime", getTrain().getName(), new Date().getTime() - getStartTime().getTime())); 121 122 getBuildReport().flush(); 123 getBuildReport().close(); 124 125 createManifests(); // now make Manifests 126 127 // notify locations have been modified by this train's build 128 for (Location location : _modifiedLocations) { 129 location.setStatus(Location.MODIFIED); 130 } 131 132 // operations automations use wait for train built to create custom 133 // manifests and switch lists 134 getTrain().setPrinted(false); 135 getTrain().setSwitchListStatus(Train.UNKNOWN); 136 getTrain().setCurrentLocation(getTrain().getTrainDepartsRouteLocation()); 137 getTrain().setBuilt(true); 138 // create and place train icon 139 getTrain().moveTrainIcon(getTrain().getTrainDepartsRouteLocation()); 140 141 log.debug("Done building train ({})", getTrain().getName()); 142 showWarningMessage(); 143 } 144 145 /** 146 * Figures out if the train terminates into staging, and if true, sets the 147 * termination track. Note if the train is returning back to the same track 148 * in staging getTerminateStagingTrack() is null, and is loaded later when the 149 * departure track is determined. 150 * 151 * @throws BuildFailedException if staging track can't be found 152 */ 153 private void determineIfTrainTerminatesIntoStaging() throws BuildFailedException { 154 // does train terminate into staging? 155 setTerminateStagingTrack(null); 156 List<Track> stagingTracksTerminate = getTerminateLocation().getTracksByMoves(Track.STAGING); 157 if (stagingTracksTerminate.size() > 0) { 158 addLine(THREE, BLANK_LINE); 159 addLine(ONE, Bundle.getMessage("buildTerminateStaging", getTerminateLocation().getName(), 160 Integer.toString(stagingTracksTerminate.size()))); 161 if (stagingTracksTerminate.size() > 1 && Setup.isStagingPromptToEnabled()) { 162 setTerminateStagingTrack(promptToStagingDialog()); 163 setStartTime(new Date()); // reset build time since user can take 164 // awhile to pick 165 } else { 166 // is this train returning to the same staging in aggressive 167 // mode? 168 if (getDepartureLocation() == getTerminateLocation() && 169 Setup.isBuildAggressive() && 170 Setup.isStagingTrackImmediatelyAvail()) { 171 addLine(ONE, Bundle.getMessage("buildStagingReturn", getTerminateLocation().getName())); 172 } else { 173 for (Track track : stagingTracksTerminate) { 174 if (checkTerminateStagingTrack(track)) { 175 setTerminateStagingTrack(track); 176 addLine(ONE, Bundle.getMessage("buildStagingAvail", 177 getTerminateStagingTrack().getName(), getTerminateLocation().getName())); 178 break; 179 } 180 } 181 } 182 } 183 if (getTerminateStagingTrack() == null) { 184 // is this train returning to the same staging in aggressive 185 // mode? 186 if (getDepartureLocation() == getTerminateLocation() && 187 Setup.isBuildAggressive() && 188 Setup.isStagingTrackImmediatelyAvail()) { 189 log.debug("Train is returning to same track in staging"); 190 } else { 191 addLine(ONE, Bundle.getMessage("buildErrorStagingFullNote")); 192 throw new BuildFailedException( 193 Bundle.getMessage("buildErrorStagingFull", getTerminateLocation().getName())); 194 } 195 } 196 } 197 } 198 199 /** 200 * Figures out if the train is departing staging, and if true, sets the 201 * departure track. Also sets the arrival track if the train is returning to 202 * the same departure track in staging. 203 * 204 * @throws BuildFailedException if staging departure track not found 205 */ 206 private void determineIfTrainDepartsStagingAndAddEngines() throws BuildFailedException { 207 // allow up to two engine and caboose swaps in the train's route 208 RouteLocation engineTerminatesFirstLeg = getTrain().getTrainTerminatesRouteLocation(); 209 210 // Adjust where the locos will terminate 211 if ((getTrain().getSecondLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 212 getTrain().getSecondLegStartRouteLocation() != null) { 213 engineTerminatesFirstLeg = getTrain().getSecondLegStartRouteLocation(); 214 } else if ((getTrain().getThirdLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 215 getTrain().getThirdLegStartRouteLocation() != null) { 216 engineTerminatesFirstLeg = getTrain().getThirdLegStartRouteLocation(); 217 } 218 219 // determine if train is departing staging 220 List<Track> stagingTracks = getDepartureLocation().getTracksByMoves(Track.STAGING); 221 if (stagingTracks.size() > 0) { 222 addLine(THREE, BLANK_LINE); 223 addLine(ONE, Bundle.getMessage("buildDepartStaging", getDepartureLocation().getName(), 224 Integer.toString(stagingTracks.size()))); 225 if (stagingTracks.size() > 1 && Setup.isStagingPromptFromEnabled()) { 226 setDepartureStagingTrack(promptFromStagingDialog()); 227 setStartTime(new Date()); // restart build timer 228 if (getDepartureStagingTrack() == null) { 229 showTrainRequirements(); 230 throw new BuildFailedException( 231 Bundle.getMessage("buildErrorStagingEmpty", getDepartureLocation().getName())); 232 } 233 } else { 234 for (Track track : stagingTracks) { 235 // is the departure track available? 236 if (!checkDepartureStagingTrack(track)) { 237 addLine(SEVEN, 238 Bundle.getMessage("buildStagingTrackRestriction", track.getName(), getTrain().getName())); 239 continue; 240 } 241 setDepartureStagingTrack(track); 242 // try each departure track for the required engines 243 if (getEngines(getTrain().getNumberEngines(), getTrain().getEngineModel(), getTrain().getEngineRoad(), 244 getTrain().getTrainDepartsRouteLocation(), engineTerminatesFirstLeg)) { 245 addLine(SEVEN, Bundle.getMessage("buildDoneAssignEnginesStaging")); 246 break; // done! 247 } 248 setDepartureStagingTrack(null); 249 } 250 } 251 if (getDepartureStagingTrack() == null) { 252 showTrainRequirements(); 253 throw new BuildFailedException(Bundle.getMessage("buildErrorStagingEmpty", getDepartureLocation().getName())); 254 } 255 } 256 getTrain().setTerminationTrack(getTerminateStagingTrack()); 257 getTrain().setDepartureTrack(getDepartureStagingTrack()); 258 } 259 260 /** 261 * Adds and removes cabooses or car with FRED in the train's route. Up to 2 262 * caboose changes. 263 * 264 * @throws BuildFailedException 265 */ 266 private void addCabooseOrFredToTrain() throws BuildFailedException { 267 // allow up to two caboose swaps in the train's route 268 RouteLocation cabooseOrFredTerminatesFirstLeg = getTrain().getTrainTerminatesRouteLocation(); 269 RouteLocation cabooseOrFredTerminatesSecondLeg = getTrain().getTrainTerminatesRouteLocation(); 270 271 // determine if there are any caboose changes 272 if ((getTrain().getSecondLegOptions() & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE || 273 (getTrain().getSecondLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE) { 274 cabooseOrFredTerminatesFirstLeg = getTrain().getSecondLegStartRouteLocation(); 275 } else if ((getTrain().getThirdLegOptions() & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE || 276 (getTrain().getThirdLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE) { 277 cabooseOrFredTerminatesFirstLeg = getTrain().getThirdLegStartRouteLocation(); 278 } 279 if ((getTrain().getThirdLegOptions() & Train.REMOVE_CABOOSE) == Train.REMOVE_CABOOSE || 280 (getTrain().getThirdLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE) { 281 cabooseOrFredTerminatesSecondLeg = getTrain().getThirdLegStartRouteLocation(); 282 } 283 284 // Do caboose changes in reverse order in case there isn't enough track 285 // space second caboose change? 286 if ((getTrain().getThirdLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE && 287 getTrain().getThirdLegStartRouteLocation() != null && 288 getTrain().getTrainTerminatesRouteLocation() != null) { 289 getCaboose(getTrain().getThirdLegCabooseRoad(), _thirdLeadEngine, getTrain().getThirdLegStartRouteLocation(), 290 getTrain().getTrainTerminatesRouteLocation(), true); 291 } 292 293 // first caboose change? 294 if ((getTrain().getSecondLegOptions() & Train.ADD_CABOOSE) == Train.ADD_CABOOSE && 295 getTrain().getSecondLegStartRouteLocation() != null && 296 cabooseOrFredTerminatesSecondLeg != null) { 297 getCaboose(getTrain().getSecondLegCabooseRoad(), _secondLeadEngine, getTrain().getSecondLegStartRouteLocation(), 298 cabooseOrFredTerminatesSecondLeg, true); 299 } 300 301 // departure caboose or car with FRED 302 getCaboose(getTrain().getCabooseRoad(), getTrain().getLeadEngine(), getTrain().getTrainDepartsRouteLocation(), 303 cabooseOrFredTerminatesFirstLeg, getTrain().isCabooseNeeded()); 304 getCarWithFred(getTrain().getCabooseRoad(), getTrain().getTrainDepartsRouteLocation(), cabooseOrFredTerminatesFirstLeg); 305 } 306 307 /** 308 * Routine to find and add available cars to the train. In normal mode 309 * performs a single pass. In aggressive mode, will perform multiple passes. 310 * If train is departing staging and in aggressive mode, will try again 311 * using normal mode if there's a train build issue. 312 * 313 * @throws BuildFailedException 314 */ 315 private void addCarsToTrain() throws BuildFailedException { 316 addLine(THREE, 317 Bundle.getMessage("buildTrain", getTrain().getNumberCarsRequested(), getTrain().getName(), getCarList().size())); 318 319 if (Setup.isBuildAggressive() && !getTrain().isBuildTrainNormalEnabled()) { 320 // perform a multiple pass build for this train, default is two 321 // passes 322 int pass = 0; 323 while (pass++ < Setup.getNumberPasses()) { 324 addCarsToTrain(pass, false); 325 } 326 // are cars stuck in staging? 327 secondAttemptNormalBuild(); 328 } else { 329 addCarsToTrain(Setup.getNumberPasses(), true); // normal build one 330 // pass 331 } 332 } 333 334 /** 335 * If cars stuck in staging, try building again in normal mode. 336 * 337 * @throws BuildFailedException 338 */ 339 private void secondAttemptNormalBuild() throws BuildFailedException { 340 if (Setup.isStagingTryNormalBuildEnabled() && isCarStuckStaging()) { 341 addLine(ONE, Bundle.getMessage("buildFailedTryNormalMode")); 342 addLine(ONE, BLANK_LINE); 343 getTrain().reset(); 344 getTrain().setStatusCode(Train.CODE_BUILDING); 345 getTrain().setLeadEngine(null); 346 // using the same departure and termination tracks 347 getTrain().setDepartureTrack(getDepartureStagingTrack()); 348 getTrain().setTerminationTrack(getTerminateStagingTrack()); 349 showAndInitializeTrainRoute(); 350 getAndRemoveEnginesFromList(); 351 addEnginesToTrain(); 352 createCarList(); 353 adjustCarsInStaging(); 354 showCarsByLocation(); 355 addCabooseOrFredToTrain(); 356 removeCaboosesAndCarsWithFred(); 357 saveCarFinalDestinations(); // save final destination and schedule 358 // id 359 blockCarsFromStaging(); // block cars from staging 360 addCarsToTrain(Setup.getNumberPasses(), true); // try normal build 361 // one pass 362 } 363 } 364 365 /** 366 * Main routine to place cars into the train. Can be called multiple times. 367 * When departing staging, ignore staged cars on the first pass unless the 368 * option to build normal was selected by user. 369 * 370 * @param pass Which pass when there are multiple passes requested by 371 * user. 372 * @param normal True if single pass or normal mode is requested by user. 373 * @throws BuildFailedException 374 */ 375 private void addCarsToTrain(int pass, boolean normal) throws BuildFailedException { 376 addLine(THREE, BLANK_LINE); 377 if (normal) { 378 addLine(THREE, Bundle.getMessage("NormalModeWhenBuilding")); 379 } else { 380 addLine(THREE, Bundle.getMessage("buildMultiplePass", pass, Setup.getNumberPasses())); 381 } 382 // now go through each location starting at departure and place cars as 383 // requested 384 for (RouteLocation rl : getRouteList()) { 385 if (getTrain().isLocationSkipped(rl)) { 386 addLine(ONE, 387 Bundle.getMessage("buildLocSkipped", rl.getName(), rl.getId(), getTrain().getName())); 388 continue; 389 } 390 if (!rl.isPickUpAllowed() && !rl.isLocalMovesAllowed()) { 391 addLine(ONE, 392 Bundle.getMessage("buildLocNoPickups", getTrain().getRoute().getName(), rl.getId(), rl.getName())); 393 continue; 394 } 395 // no pick ups from staging unless at the start of the train's route 396 if (rl != getTrain().getTrainDepartsRouteLocation() && rl.getLocation().isStaging()) { 397 addLine(ONE, Bundle.getMessage("buildNoPickupsFromStaging", rl.getName())); 398 continue; 399 } 400 // the next check provides a build report message if there's an 401 // issue with the train direction 402 if (!checkPickUpTrainDirection(rl)) { 403 continue; 404 } 405 _completedMoves = 0; // moves completed for this location 406 _reqNumOfMoves = rl.getMaxCarMoves() - rl.getCarMoves(); 407 408 if (!normal) { 409 if (rl == getTrain().getTrainDepartsRouteLocation()) { 410 _reqNumOfMoves = (rl.getMaxCarMoves() - rl.getCarMoves()) * pass / Setup.getNumberPasses(); 411 } else if (pass == 1) { 412 _reqNumOfMoves = (rl.getMaxCarMoves() - rl.getCarMoves()) / 2; 413 // round up requested moves 414 int remainder = (rl.getMaxCarMoves() - rl.getCarMoves()) % 2; 415 if (remainder > 0) { 416 _reqNumOfMoves++; 417 } 418 } 419 } 420 421 // if departing staging make adjustments 422 if (rl == getTrain().getTrainDepartsRouteLocation()) { 423 if (pass == 1) { 424 makeAdjustmentsIfDepartingStaging(); 425 } else { 426 restoreCarsIfDepartingStaging(); 427 } 428 } 429 430 int saveReqMoves = _reqNumOfMoves; // save a copy for status message 431 addLine(ONE, 432 Bundle.getMessage("buildLocReqMoves", rl.getName(), rl.getId(), _reqNumOfMoves, 433 rl.getMaxCarMoves() - rl.getCarMoves(), rl.getMaxCarMoves())); 434 addLine(FIVE, BLANK_LINE); 435 436 // show the car load generation options for staging 437 if (rl == getTrain().getTrainDepartsRouteLocation()) { 438 showLoadGenerationOptionsStaging(); 439 } 440 441 _carIndex = 0; // see reportCarsNotMoved(rl) below 442 443 findDestinationsForCarsFromLocation(rl, false); // first pass 444 445 // perform 2nd pass if aggressive mode and there are requested 446 // moves. This will perform local moves at this location, services 447 // off spot tracks, only in aggressive mode and at least one car 448 // has a new destination 449 if (Setup.isBuildAggressive() && saveReqMoves != _reqNumOfMoves) { 450 log.debug("Perform extra pass at location ({})", rl.getName()); 451 // use up to half of the available moves left for this location 452 if (_reqNumOfMoves < (rl.getMaxCarMoves() - rl.getCarMoves()) / 2) { 453 _reqNumOfMoves = (rl.getMaxCarMoves() - rl.getCarMoves()) / 2; 454 } 455 findDestinationsForCarsFromLocation(rl, true); // second pass 456 457 // we might have freed up space at a spur that has an alternate 458 // track 459 if (redirectCarsFromAlternateTrack()) { 460 addLine(SEVEN, BLANK_LINE); 461 } 462 } 463 if (rl == getTrain().getTrainDepartsRouteLocation() && pass == Setup.getNumberPasses() && isCarStuckStaging()) { 464 return; // report ASAP that there are stuck cars 465 } 466 addLine(ONE, 467 Bundle.getMessage("buildStatusMsg", 468 (saveReqMoves <= _completedMoves ? Bundle.getMessage("Success") 469 : Bundle.getMessage("Partial")), 470 Integer.toString(_completedMoves), Integer.toString(saveReqMoves), rl.getName(), 471 getTrain().getName())); 472 473 if (_reqNumOfMoves <= 0 && pass == Setup.getNumberPasses()) { 474 showCarsNotMoved(rl); 475 } 476 } 477 } 478 479 private void setTrainBuildStatus() { 480 if (_numberCars < getTrain().getNumberCarsRequested()) { 481 getTrain().setStatusCode(Train.CODE_PARTIAL_BUILT); 482 addLine(ONE, 483 Train.PARTIAL_BUILT + 484 " " + 485 getTrain().getNumberCarsWorked() + 486 "/" + 487 getTrain().getNumberCarsRequested() + 488 " " + 489 Bundle.getMessage("cars")); 490 } else { 491 getTrain().setStatusCode(Train.CODE_BUILT); 492 addLine(ONE, 493 Train.BUILT + " " + getTrain().getNumberCarsWorked() + " " + Bundle.getMessage("cars")); 494 } 495 } 496 497 private void createManifests() throws BuildFailedException { 498 new TrainManifest(getTrain()); 499 try { 500 new JsonManifest(getTrain()).build(); 501 } catch (IOException ex) { 502 log.error("Unable to create JSON manifest: {}", ex.getLocalizedMessage()); 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}