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 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 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, BLANK_LINE); 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}