001package jmri.jmrit.operations.trains; 002 003import java.awt.Color; 004import java.beans.PropertyChangeListener; 005import java.io.*; 006import java.text.MessageFormat; 007import java.util.*; 008 009import org.jdom2.Element; 010 011import jmri.InstanceManager; 012import jmri.beans.Identifiable; 013import jmri.beans.PropertyChangeSupport; 014import jmri.jmrit.display.Editor; 015import jmri.jmrit.display.EditorManager; 016import jmri.jmrit.operations.locations.*; 017import jmri.jmrit.operations.rollingstock.RollingStock; 018import jmri.jmrit.operations.rollingstock.RollingStockManager; 019import jmri.jmrit.operations.rollingstock.cars.*; 020import jmri.jmrit.operations.rollingstock.engines.*; 021import jmri.jmrit.operations.routes.*; 022import jmri.jmrit.operations.setup.Control; 023import jmri.jmrit.operations.setup.Setup; 024import jmri.jmrit.operations.trains.excel.TrainCustomManifest; 025import jmri.jmrit.roster.RosterEntry; 026import jmri.script.JmriScriptEngineManager; 027import jmri.util.FileUtil; 028import jmri.util.swing.JmriJOptionPane; 029 030/** 031 * Represents a train on the layout 032 * 033 * @author Daniel Boudreau Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 034 * 2014, 2015 035 * @author Rodney Black Copyright (C) 2011 036 */ 037public class Train extends PropertyChangeSupport implements Identifiable, PropertyChangeListener { 038 039 /* 040 * WARNING DO NOT LOAD CAR OR ENGINE MANAGERS WHEN Train.java IS CREATED IT 041 * CAUSES A RECURSIVE LOOP AT LOAD TIME, SEE EXAMPLES BELOW CarManager 042 * carManager = InstanceManager.getDefault(CarManager.class); EngineManager 043 * engineManager = InstanceManager.getDefault(EngineManager.class); 044 */ 045 046 // The release date for JMRI operations 10/29/2008 047 048 public static final String NONE = ""; 049 050 protected String _id = NONE; 051 protected String _name = NONE; 052 protected String _description = NONE; 053 protected RouteLocation _current = null;// where the train is located in its route 054 protected String _buildFailedMessage = NONE; // the build failed message for this train 055 protected boolean _built = false; // when true, a train manifest has been built 056 protected boolean _modified = false; // when true, user has modified train after being built 057 protected boolean _build = true; // when true, build this train 058 protected boolean _buildFailed = false; // when true, build for this train failed 059 protected boolean _printed = false; // when true, manifest has been printed 060 protected boolean _sendToTerminal = false; // when true, cars picked up by train only go to terminal 061 protected boolean _allowLocalMoves = true; // when true, cars with custom loads can be moved locally 062 protected boolean _allowThroughCars = true; // when true, cars from the origin can be sent to the terminal 063 protected boolean _buildNormal = false; // when true build this train in normal mode 064 protected boolean _allowCarsReturnStaging = false; // when true allow cars to return to staging 065 protected boolean _serviceAllCarsWithFinalDestinations = false; // when true, service cars with final destinations 066 protected boolean _buildConsist = false; // when true, build a consist for this train using single locomotives 067 protected boolean _sendCarsWithCustomLoadsToStaging = false; // when true, send cars to staging if spurs full 068 protected Route _route = null; 069 protected Track _departureTrack; // the departure track from staging 070 protected Track _terminationTrack; // the termination track into staging 071 protected String _carRoadOption = ALL_ROADS;// train car road name restrictions 072 protected String _locoRoadOption = ALL_ROADS;// train engine road name restrictions 073 protected int _requires = NO_CABOOSE_OR_FRED; // train requirements, caboose, FRED 074 protected String _numberEngines = "0"; // number of engines this train requires 075 protected String _engineRoad = NONE; // required road name for engines assigned to this train 076 protected String _engineModel = NONE; // required model of engines assigned to this train 077 protected String _cabooseRoad = NONE; // required road name for cabooses assigned to this train 078 protected String _departureTime = "00:00"; // NOI18N departure time for this train 079 protected String _leadEngineId = NONE; // lead engine for train icon info 080 protected String _builtStartYear = NONE; // built start year 081 protected String _builtEndYear = NONE; // built end year 082 protected String _loadOption = ALL_LOADS;// train load restrictions 083 protected String _ownerOption = ALL_OWNERS;// train owner name restrictions 084 protected List<String> _buildScripts = new ArrayList<>(); // list of script pathnames to run before train is built 085 protected List<String> _afterBuildScripts = new ArrayList<>(); // list of script pathnames to run after train is 086 // built 087 protected List<String> _moveScripts = new ArrayList<>(); // list of script pathnames to run when train is moved 088 protected List<String> _terminationScripts = new ArrayList<>(); // list of script pathnames to run when train is 089 // terminated 090 protected String _railroadName = NONE; // optional railroad name for this train 091 protected String _logoPathName = NONE; // optional manifest logo for this train 092 protected boolean _showTimes = true; // when true, show arrival and departure times for this train 093 protected Engine _leadEngine = null; // lead engine for icon 094 protected String _switchListStatus = UNKNOWN; // print switch list status 095 protected String _comment = NONE; 096 protected String _serviceStatus = NONE; // status only if train is being built 097 protected int _statusCode = CODE_UNKNOWN; 098 protected int _oldStatusCode = CODE_UNKNOWN; 099 protected String _statusTerminatedDate = NONE; 100 protected int _statusCarsRequested = 0; 101 protected String _tableRowColorName = NONE; // color of row in Trains table 102 protected String _tableRowColorResetName = NONE; // color of row in Trains table when reset 103 104 // Engine change and helper engines 105 protected int _leg2Options = NO_CABOOSE_OR_FRED; // options 106 protected RouteLocation _leg2Start = null; // route location when 2nd leg begins 107 protected RouteLocation _end2Leg = null; // route location where 2nd leg ends 108 protected String _leg2Engines = "0"; // number of engines 2nd leg 109 protected String _leg2Road = NONE; // engine road name 2nd leg 110 protected String _leg2Model = NONE; // engine model 2nd leg 111 protected String _leg2CabooseRoad = NONE; // road name for caboose 2nd leg 112 113 protected int _leg3Options = NO_CABOOSE_OR_FRED; // options 114 protected RouteLocation _leg3Start = null; // route location when 3rd leg begins 115 protected RouteLocation _leg3End = null; // route location where 3rd leg ends 116 protected String _leg3Engines = "0"; // number of engines 3rd leg 117 protected String _leg3Road = NONE; // engine road name 3rd leg 118 protected String _leg3Model = NONE; // engine model 3rd leg 119 protected String _leg3CabooseRoad = NONE; // road name for caboose 3rd leg 120 121 // engine change and helper options 122 public static final int CHANGE_ENGINES = 1; // change engines 123 public static final int HELPER_ENGINES = 2; // add helper engines 124 public static final int ADD_CABOOSE = 4; // add caboose 125 public static final int REMOVE_CABOOSE = 8; // remove caboose 126 127 // property change names 128 public static final String DISPOSE_CHANGED_PROPERTY = "TrainDispose"; // NOI18N 129 public static final String STOPS_CHANGED_PROPERTY = "TrainStops"; // NOI18N 130 public static final String TYPES_CHANGED_PROPERTY = "TrainTypes"; // NOI18N 131 public static final String BUILT_CHANGED_PROPERTY = "TrainBuilt"; // NOI18N 132 public static final String BUILT_YEAR_CHANGED_PROPERTY = "TrainBuiltYear"; // NOI18N 133 public static final String BUILD_CHANGED_PROPERTY = "TrainBuild"; // NOI18N 134 public static final String ROADS_CHANGED_PROPERTY = "TrainRoads"; // NOI18N 135 public static final String LOADS_CHANGED_PROPERTY = "TrainLoads"; // NOI18N 136 public static final String OWNERS_CHANGED_PROPERTY = "TrainOwners"; // NOI18N 137 public static final String NAME_CHANGED_PROPERTY = "TrainName"; // NOI18N 138 public static final String DESCRIPTION_CHANGED_PROPERTY = "TrainDescription"; // NOI18N 139 public static final String STATUS_CHANGED_PROPERTY = "TrainStatus"; // NOI18N 140 public static final String DEPARTURETIME_CHANGED_PROPERTY = "TrainDepartureTime"; // NOI18N 141 public static final String TRAIN_LOCATION_CHANGED_PROPERTY = "TrainLocation"; // NOI18N 142 public static final String TRAIN_ROUTE_CHANGED_PROPERTY = "TrainRoute"; // NOI18N 143 public static final String TRAIN_REQUIREMENTS_CHANGED_PROPERTY = "TrainRequirements"; // NOI18N 144 public static final String TRAIN_MOVE_COMPLETE_CHANGED_PROPERTY = "TrainMoveComplete"; // NOI18N 145 public static final String TRAIN_ROW_COLOR_CHANGED_PROPERTY = "TrianRowColor"; // NOI18N 146 public static final String TRAIN_ROW_COLOR_RESET_CHANGED_PROPERTY = "TrianRowColorReset"; // NOI18N 147 public static final String TRAIN_MODIFIED_CHANGED_PROPERTY = "TrainModified"; // NOI18N 148 149 // Train status 150 public static final String TRAIN_RESET = Bundle.getMessage("TrainReset"); 151 public static final String RUN_SCRIPTS = Bundle.getMessage("RunScripts"); 152 public static final String BUILDING = Bundle.getMessage("Building"); 153 public static final String BUILD_FAILED = Bundle.getMessage("BuildFailed"); 154 public static final String BUILT = Bundle.getMessage("Built"); 155 public static final String PARTIAL_BUILT = Bundle.getMessage("Partial"); 156 public static final String TRAIN_EN_ROUTE = Bundle.getMessage("TrainEnRoute"); 157 public static final String TERMINATED = Bundle.getMessage("Terminated"); 158 public static final String MANIFEST_MODIFIED = Bundle.getMessage("Modified"); 159 160 // Train status codes 161 public static final int CODE_TRAIN_RESET = 0; 162 public static final int CODE_RUN_SCRIPTS = 0x100; 163 public static final int CODE_BUILDING = 0x01; 164 public static final int CODE_BUILD_FAILED = 0x02; 165 public static final int CODE_BUILT = 0x10; 166 public static final int CODE_PARTIAL_BUILT = CODE_BUILT + 0x04; 167 public static final int CODE_TRAIN_EN_ROUTE = CODE_BUILT + 0x08; 168 public static final int CODE_TERMINATED = 0x80; 169 public static final int CODE_MANIFEST_MODIFIED = 0x200; 170 public static final int CODE_UNKNOWN = 0xFFFF; 171 172 // train requirements 173 public static final int NO_CABOOSE_OR_FRED = 0; // default 174 public static final int CABOOSE = 1; 175 public static final int FRED = 2; 176 177 // road options 178 public static final String ALL_ROADS = Bundle.getMessage("All"); 179 public static final String INCLUDE_ROADS = Bundle.getMessage("Include"); 180 public static final String EXCLUDE_ROADS = Bundle.getMessage("Exclude"); 181 182 // owner options 183 public static final String ALL_OWNERS = Bundle.getMessage("All"); 184 public static final String INCLUDE_OWNERS = Bundle.getMessage("Include"); 185 public static final String EXCLUDE_OWNERS = Bundle.getMessage("Exclude"); 186 187 // load options 188 public static final String ALL_LOADS = Bundle.getMessage("All"); 189 public static final String INCLUDE_LOADS = Bundle.getMessage("Include"); 190 public static final String EXCLUDE_LOADS = Bundle.getMessage("Exclude"); 191 192 // Switch list status 193 public static final String UNKNOWN = ""; 194 public static final String PRINTED = Bundle.getMessage("Printed"); 195 196 public static final String AUTO = Bundle.getMessage("Auto"); 197 public static final String AUTO_HPT = Bundle.getMessage("AutoHPT"); 198 199 public Train(String id, String name) { 200 // log.debug("New train ({}) id: {}", name, id); 201 _name = name; 202 _id = id; 203 // a new train accepts all types 204 setTypeNames(InstanceManager.getDefault(CarTypes.class).getNames()); 205 setTypeNames(InstanceManager.getDefault(EngineTypes.class).getNames()); 206 addPropertyChangeListerners(); 207 } 208 209 @Override 210 public String getId() { 211 return _id; 212 } 213 214 /** 215 * Sets the name of this train, normally a short name that can fit within the 216 * train icon. 217 * 218 * @param name the train's name. 219 */ 220 public void setName(String name) { 221 String old = _name; 222 _name = name; 223 if (!old.equals(name)) { 224 setDirtyAndFirePropertyChange(NAME_CHANGED_PROPERTY, old, name); 225 } 226 } 227 228 // for combo boxes 229 /** 230 * Get's a train's name 231 * 232 * @return train's name 233 */ 234 @Override 235 public String toString() { 236 return _name; 237 } 238 239 /** 240 * Get's a train's name 241 * 242 * @return train's name 243 */ 244 public String getName() { 245 return _name; 246 } 247 248 /** 249 * @return The name of the color when highlighting the train's row 250 */ 251 public String getTableRowColorName() { 252 return _tableRowColorName; 253 } 254 255 public void setTableRowColorName(String colorName) { 256 String old = _tableRowColorName; 257 _tableRowColorName = colorName; 258 if (!old.equals(colorName)) { 259 setDirtyAndFirePropertyChange(TRAIN_ROW_COLOR_CHANGED_PROPERTY, old, colorName); 260 } 261 } 262 263 /** 264 * @return The name of the train row color when the train is reset 265 */ 266 public String getTableRowColorNameReset() { 267 return _tableRowColorResetName; 268 } 269 270 public void setTableRowColorNameReset(String colorName) { 271 String old = _tableRowColorResetName; 272 _tableRowColorResetName = colorName; 273 if (!old.equals(colorName)) { 274 setDirtyAndFirePropertyChange(TRAIN_ROW_COLOR_RESET_CHANGED_PROPERTY, old, colorName); 275 } 276 } 277 278 /** 279 * @return The color when highlighting the train's row 280 */ 281 public Color getTableRowColor() { 282 String colorName = getTableRowColorName(); 283 if (colorName.equals(NONE)) { 284 return null; 285 } else { 286 return Setup.getColor(colorName); 287 } 288 } 289 290 /** 291 * Get's train's departure time 292 * 293 * @return train's departure time in the String format hh:mm 294 */ 295 public String getDepartureTime() { 296 // check to see if the route has a departure time 297 RouteLocation rl = getTrainDepartsRouteLocation(); 298 if (rl != null) { 299 rl.removePropertyChangeListener(this); 300 rl.addPropertyChangeListener(this); 301 if (!rl.getDepartureTime().equals(RouteLocation.NONE)) { 302 return rl.getDepartureTime(); 303 } 304 } 305 return _departureTime; 306 } 307 308 /** 309 * Get's train's departure time in 12hr or 24hr format 310 * 311 * @return train's departure time in the String format hh:mm or hh:mm(AM/PM) 312 */ 313 public String getFormatedDepartureTime() { 314 // check to see if the route has a departure time 315 RouteLocation rl = getTrainDepartsRouteLocation(); 316 if (rl != null && !rl.getDepartureTime().equals(RouteLocation.NONE)) { 317 // need to forward any changes to departure time 318 rl.removePropertyChangeListener(this); 319 rl.addPropertyChangeListener(this); 320 return rl.getFormatedDepartureTime(); 321 } 322 return (parseTime(getDepartTimeMinutes())); 323 } 324 325 /** 326 * Get train's departure time in minutes from midnight for sorting 327 * 328 * @return int hh*60+mm 329 */ 330 public int getDepartTimeMinutes() { 331 int hour = Integer.parseInt(getDepartureTimeHour()); 332 int minute = Integer.parseInt(getDepartureTimeMinute()); 333 return (hour * 60) + minute; 334 } 335 336 public void setDepartureTime(String hour, String minute) { 337 String old = _departureTime; 338 int h = Integer.parseInt(hour); 339 if (h < 10) { 340 hour = "0" + h; 341 } 342 int m = Integer.parseInt(minute); 343 if (m < 10) { 344 minute = "0" + m; 345 } 346 String time = hour + ":" + minute; 347 _departureTime = time; 348 if (!old.equals(time)) { 349 setDirtyAndFirePropertyChange(DEPARTURETIME_CHANGED_PROPERTY, old, _departureTime); 350 setModified(true); 351 } 352 } 353 354 public String getDepartureTimeHour() { 355 String[] time = getDepartureTime().split(":"); 356 return time[0]; 357 } 358 359 public String getDepartureTimeMinute() { 360 String[] time = getDepartureTime().split(":"); 361 return time[1]; 362 } 363 364 public static final String ALREADY_SERVICED = "-1"; // NOI18N 365 366 /** 367 * Gets the expected time when this train will arrive at the location rl. 368 * Expected arrival time is based on the number of car pick up and set outs for 369 * this train. TODO Doesn't provide expected arrival time if train is in route, 370 * instead provides relative time. If train is at or has passed the location 371 * return -1. 372 * 373 * @param routeLocation The RouteLocation. 374 * @return expected arrival time in minutes (append AM or PM if 12 hour format) 375 */ 376 public String getExpectedArrivalTime(RouteLocation routeLocation) { 377 int minutes = getExpectedTravelTimeInMinutes(routeLocation); 378 if (minutes == -1) { 379 return ALREADY_SERVICED; 380 } 381 log.debug("Expected arrival time for train ({}) at ({}), {} minutes", getName(), routeLocation.getName(), 382 minutes); 383 // TODO use fast clock to get current time vs departure time 384 // for now use relative 385 return parseTime(minutes); 386 } 387 388 public String getExpectedDepartureTime(RouteLocation routeLocation) { 389 int minutes = getExpectedTravelTimeInMinutes(routeLocation); 390 if (minutes == -1) { 391 return ALREADY_SERVICED; 392 } 393 if (!routeLocation.getDepartureTime().equals(RouteLocation.NONE)) { 394 return routeLocation.getFormatedDepartureTime(); 395 } 396 // figure out the work at this location, note that there can be 397 // consecutive locations with the same name 398 if (getRoute() != null) { 399 boolean foundRouteLocation = false; 400 for (RouteLocation rl : getRoute().getLocationsBySequenceList()) { 401 if (rl == routeLocation) { 402 foundRouteLocation = true; 403 } 404 if (foundRouteLocation) { 405 if (rl.getSplitName() 406 .equals(routeLocation.getSplitName())) { 407 minutes = minutes + getWorkTimeAtLocation(rl); 408 } else { 409 break; // done 410 } 411 } 412 } 413 } 414 log.debug("Expected departure time {} for train ({}) at ({})", minutes, getName(), routeLocation.getName()); 415 return parseTime(minutes); 416 } 417 418 public int getWorkTimeAtLocation(RouteLocation routeLocation) { 419 int minutes = 0; 420 // departure? 421 if (routeLocation == getTrainDepartsRouteLocation()) { 422 return minutes; 423 } 424 // add any work at this location 425 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 426 if (rs.getRouteLocation() == routeLocation && !rs.getTrackName().equals(RollingStock.NONE)) { 427 minutes += Setup.getSwitchTime(); 428 } 429 if (rs.getRouteDestination() == routeLocation) { 430 minutes += Setup.getSwitchTime(); 431 } 432 } 433 return minutes; 434 } 435 436 public int getExpectedTravelTimeInMinutes(RouteLocation routeLocation) { 437 int minutes = 0; 438 if (!isTrainEnRoute()) { 439 minutes += Integer.parseInt(getDepartureTimeMinute()); 440 minutes += 60 * Integer.parseInt(getDepartureTimeHour()); 441 } else { 442 minutes = -1; // -1 means train has already served the location 443 } 444 // boolean trainAt = false; 445 boolean trainLocFound = false; 446 if (getRoute() != null) { 447 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 448 for (int i = 0; i < routeList.size(); i++) { 449 RouteLocation rl = routeList.get(i); 450 if (rl == routeLocation) { 451 break; // done 452 } 453 // start recording time after finding where the train is 454 if (!trainLocFound && isTrainEnRoute()) { 455 if (rl == getCurrentRouteLocation()) { 456 trainLocFound = true; 457 // add travel time 458 minutes = Setup.getTravelTime(); 459 } 460 continue; 461 } 462 // is there a departure time from this location? 463 if (!rl.getDepartureTime().equals(RouteLocation.NONE)) { 464 String dt = rl.getDepartureTime(); 465 log.debug("Location {} departure time {}", rl.getName(), dt); 466 String[] time = dt.split(":"); 467 minutes = 60 * Integer.parseInt(time[0]) + Integer.parseInt(time[1]); 468 // log.debug("New minutes: "+minutes); 469 } 470 // add wait time 471 minutes += rl.getWait(); 472 // add travel time if new location 473 RouteLocation next = routeList.get(i + 1); 474 if (next != null && 475 !rl.getSplitName().equals(next.getSplitName())) { 476 minutes += Setup.getTravelTime(); 477 } 478 // don't count work if there's a departure time 479 if (i == 0 || !rl.getDepartureTime().equals(RouteLocation.NONE)) { 480 continue; 481 } 482 // now add the work at the location 483 minutes = minutes + getWorkTimeAtLocation(rl); 484 } 485 } 486 return minutes; 487 } 488 489 /** 490 * Returns time in hour:minute format 491 * 492 * @param minutes number of minutes from midnight 493 * @return hour:minute (optionally AM:PM format) 494 */ 495 private String parseTime(int minutes) { 496 int hours = 0; 497 int days = 0; 498 499 if (minutes >= 60) { 500 int h = minutes / 60; 501 minutes = minutes - h * 60; 502 hours += h; 503 } 504 505 String d = ""; 506 if (hours >= 24) { 507 int nd = hours / 24; 508 hours = hours - nd * 24; 509 days += nd; 510 d = Integer.toString(days) + ":"; 511 } 512 513 // AM_PM field 514 String am_pm = ""; 515 if (Setup.is12hrFormatEnabled()) { 516 am_pm = " " + Bundle.getMessage("AM"); 517 if (hours >= 12) { 518 hours = hours - 12; 519 am_pm = " " + Bundle.getMessage("PM"); 520 } 521 if (hours == 0) { 522 hours = 12; 523 } 524 } 525 526 String h = Integer.toString(hours); 527 if (hours < 10) { 528 h = "0" + h; 529 } 530 if (minutes < 10) { 531 return d + h + ":0" + minutes + am_pm; // NOI18N 532 } 533 return d + h + ":" + minutes + am_pm; 534 } 535 536 /** 537 * Set train requirements. If NO_CABOOSE_OR_FRED, then train doesn't require a 538 * caboose or car with FRED. 539 * 540 * @param requires NO_CABOOSE_OR_FRED, CABOOSE, FRED 541 */ 542 public void setRequirements(int requires) { 543 int old = _requires; 544 _requires = requires; 545 if (old != requires) { 546 setDirtyAndFirePropertyChange(TRAIN_REQUIREMENTS_CHANGED_PROPERTY, Integer.toString(old), 547 Integer.toString(requires)); 548 } 549 } 550 551 /** 552 * Get a train's requirements with regards to the last car in the train. 553 * 554 * @return NONE CABOOSE FRED 555 */ 556 public int getRequirements() { 557 return _requires; 558 } 559 560 public boolean isCabooseNeeded() { 561 return (getRequirements() & CABOOSE) == CABOOSE; 562 } 563 564 public boolean isFredNeeded() { 565 return (getRequirements() & FRED) == FRED; 566 } 567 568 public void setRoute(Route route) { 569 Route old = _route; 570 String oldRoute = NONE; 571 String newRoute = NONE; 572 if (old != null) { 573 old.removePropertyChangeListener(this); 574 oldRoute = old.toString(); 575 } 576 if (route != null) { 577 route.addPropertyChangeListener(this); 578 newRoute = route.toString(); 579 } 580 _route = route; 581 _skipLocationsList.clear(); 582 if (old == null || !old.equals(route)) { 583 setDirtyAndFirePropertyChange(TRAIN_ROUTE_CHANGED_PROPERTY, oldRoute, newRoute); 584 } 585 } 586 587 /** 588 * Gets the train's route 589 * 590 * @return train's route 591 */ 592 public Route getRoute() { 593 return _route; 594 } 595 596 /** 597 * Get's the train's route name. 598 * 599 * @return Train's route name. 600 */ 601 public String getTrainRouteName() { 602 if (getRoute() == null) { 603 return NONE; 604 } 605 return getRoute().getName(); 606 } 607 608 /** 609 * Get the train's departure location's name 610 * 611 * @return train's departure location's name 612 */ 613 public String getTrainDepartsName() { 614 if (getTrainDepartsRouteLocation() != null) { 615 return getTrainDepartsRouteLocation().getName(); 616 } 617 return NONE; 618 } 619 620 public RouteLocation getTrainDepartsRouteLocation() { 621 if (getRoute() == null) { 622 return null; 623 } 624 return getRoute().getDepartsRouteLocation(); 625 } 626 627 public String getTrainDepartsDirection() { 628 String direction = NONE; 629 if (getTrainDepartsRouteLocation() != null) { 630 direction = getTrainDepartsRouteLocation().getTrainDirectionString(); 631 } 632 return direction; 633 } 634 635 /** 636 * Get train's final location's name 637 * 638 * @return train's final location's name 639 */ 640 public String getTrainTerminatesName() { 641 if (getTrainTerminatesRouteLocation() != null) { 642 return getTrainTerminatesRouteLocation().getName(); 643 } 644 return NONE; 645 } 646 647 public RouteLocation getTrainTerminatesRouteLocation() { 648 if (getRoute() == null) { 649 return null; 650 } 651 return getRoute().getTerminatesRouteLocation(); 652 } 653 654 /** 655 * Returns the order the train should be blocked. 656 * 657 * @return routeLocations for this train. 658 */ 659 public List<RouteLocation> getTrainBlockingOrder() { 660 if (getRoute() == null) { 661 return null; 662 } 663 return getRoute().getBlockingOrder(); 664 } 665 666 /** 667 * Set train's current route location 668 * 669 * @param location The current RouteLocation. 670 */ 671 protected void setCurrentLocation(RouteLocation location) { 672 RouteLocation old = _current; 673 _current = location; 674 if ((old != null && !old.equals(location)) || (old == null && location != null)) { 675 setDirtyAndFirePropertyChange("current", old, location); // NOI18N 676 } 677 } 678 679 /** 680 * Get train's current location name 681 * 682 * @return Train's current route location name 683 */ 684 public String getCurrentLocationName() { 685 if (getCurrentRouteLocation() == null) { 686 return NONE; 687 } 688 return getCurrentRouteLocation().getName(); 689 } 690 691 /** 692 * Get train's current route location 693 * 694 * @return Train's current route location 695 */ 696 public RouteLocation getCurrentRouteLocation() { 697 if (getRoute() == null) { 698 return null; 699 } 700 if (_current == null) { 701 return null; 702 } 703 // this will verify that the current location still exists 704 return getRoute().getLocationById(_current.getId()); 705 } 706 707 /** 708 * Get the train's next location name 709 * 710 * @return Train's next route location name 711 */ 712 public String getNextLocationName() { 713 return getNextLocationName(1); 714 } 715 716 /** 717 * Get a location name in a train's route from the current train's location. A 718 * number of "1" means get the next location name in a train's route. 719 * 720 * @param number The stop number, must be greater than 0 721 * @return Name of the location that is the number of stops away from the 722 * train's current location. 723 */ 724 public String getNextLocationName(int number) { 725 RouteLocation rl = getCurrentRouteLocation(); 726 while (number-- > 0) { 727 rl = getNextRouteLocation(rl); 728 if (rl == null) { 729 return NONE; 730 } 731 } 732 return rl.getName(); 733 } 734 735 public RouteLocation getNextRouteLocation(RouteLocation currentRouteLocation) { 736 if (getRoute() == null) { 737 return null; 738 } 739 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 740 for (int i = 0; i < routeList.size(); i++) { 741 RouteLocation rl = routeList.get(i); 742 if (rl == currentRouteLocation) { 743 i++; 744 if (i < routeList.size()) { 745 return routeList.get(i); 746 } 747 break; 748 } 749 } 750 return null; // At end of route 751 } 752 753 public void setDepartureTrack(Track track) { 754 Track old = _departureTrack; 755 _departureTrack = track; 756 if (old != track) { 757 setDirtyAndFirePropertyChange("DepartureTrackChanged", old, track); // NOI18N 758 } 759 } 760 761 public Track getDepartureTrack() { 762 return _departureTrack; 763 } 764 765 public boolean isDepartingStaging() { 766 return getDepartureTrack() != null; 767 } 768 769 public void setTerminationTrack(Track track) { 770 Track old = _terminationTrack; 771 _terminationTrack = track; 772 if (old != track) { 773 setDirtyAndFirePropertyChange("TerminationTrackChanged", old, track); // NOI18N 774 } 775 } 776 777 public Track getTerminationTrack() { 778 return _terminationTrack; 779 } 780 781 /** 782 * Set the train's machine readable status. Calls update train table row color. 783 * 784 * @param code machine readable 785 */ 786 public void setStatusCode(int code) { 787 String oldStatus = getStatus(); 788 int oldCode = getStatusCode(); 789 _statusCode = code; 790 // always fire property change for train en route 791 if (oldCode != getStatusCode() || code == CODE_TRAIN_EN_ROUTE) { 792 setDirtyAndFirePropertyChange(STATUS_CHANGED_PROPERTY, oldStatus, getStatus()); 793 } 794 updateTrainTableRowColor(); 795 } 796 797 public void updateTrainTableRowColor() { 798 if (!InstanceManager.getDefault(TrainManager.class).isRowColorManual()) { 799 switch (getStatusCode()) { 800 case CODE_TRAIN_RESET: 801 String color = getTableRowColorNameReset(); 802 if (color.equals(NONE)) { 803 color = InstanceManager.getDefault(TrainManager.class).getRowColorNameForReset(); 804 } 805 setTableRowColorName(color); 806 break; 807 case CODE_BUILT: 808 case CODE_PARTIAL_BUILT: 809 setTableRowColorName(InstanceManager.getDefault(TrainManager.class).getRowColorNameForBuilt()); 810 break; 811 case CODE_BUILD_FAILED: 812 setTableRowColorName( 813 InstanceManager.getDefault(TrainManager.class).getRowColorNameForBuildFailed()); 814 break; 815 case CODE_TRAIN_EN_ROUTE: 816 setTableRowColorName( 817 InstanceManager.getDefault(TrainManager.class).getRowColorNameForTrainEnRoute()); 818 break; 819 case CODE_TERMINATED: 820 setTableRowColorName(InstanceManager.getDefault(TrainManager.class).getRowColorNameForTerminated()); 821 break; 822 default: // all other cases do nothing 823 break; 824 } 825 } 826 } 827 828 /** 829 * Get train's status in the default locale. 830 * 831 * @return Human-readable status 832 */ 833 public String getStatus() { 834 return this.getStatus(Locale.getDefault()); 835 } 836 837 /** 838 * Get train's status in the specified locale. 839 * 840 * @param locale The Locale. 841 * @return Human-readable status 842 */ 843 public String getStatus(Locale locale) { 844 return this.getStatus(locale, this.getStatusCode()); 845 } 846 847 /** 848 * Get the human-readable status for the requested status code. 849 * 850 * @param locale The Locale. 851 * @param code requested status 852 * @return Human-readable status 853 */ 854 public String getStatus(Locale locale, int code) { 855 switch (code) { 856 case CODE_RUN_SCRIPTS: 857 return RUN_SCRIPTS; 858 case CODE_BUILDING: 859 return BUILDING; 860 case CODE_BUILD_FAILED: 861 return BUILD_FAILED; 862 case CODE_BUILT: 863 return Bundle.getMessage(locale, "StatusBuilt", this.getNumberCarsWorked()); // NOI18N 864 case CODE_PARTIAL_BUILT: 865 return Bundle.getMessage(locale, "StatusPartialBuilt", this.getNumberCarsWorked(), 866 this.getNumberCarsRequested()); // NOI18N 867 case CODE_TERMINATED: 868 return Bundle.getMessage(locale, "StatusTerminated", this.getTerminationDate()); // NOI18N 869 case CODE_TRAIN_EN_ROUTE: 870 return Bundle.getMessage(locale, "StatusEnRoute", this.getNumberCarsInTrain(), this.getTrainLength(), 871 Setup.getLengthUnit().toLowerCase(), this.getTrainWeight()); // NOI18N 872 case CODE_TRAIN_RESET: 873 return TRAIN_RESET; 874 case CODE_MANIFEST_MODIFIED: 875 return MANIFEST_MODIFIED; 876 case CODE_UNKNOWN: 877 default: 878 return UNKNOWN; 879 } 880 } 881 882 public String getMRStatus() { 883 switch (getStatusCode()) { 884 case CODE_PARTIAL_BUILT: 885 return getStatusCode() + "||" + this.getNumberCarsRequested(); // NOI18N 886 case CODE_TERMINATED: 887 return getStatusCode() + "||" + this.getTerminationDate(); // NOI18N 888 default: 889 return Integer.toString(getStatusCode()); 890 } 891 } 892 893 public int getStatusCode() { 894 return _statusCode; 895 } 896 897 protected void setOldStatusCode(int code) { 898 _oldStatusCode = code; 899 } 900 901 protected int getOldStatusCode() { 902 return _oldStatusCode; 903 } 904 905 /** 906 * Used to determine if train has departed the first location in the train's 907 * route 908 * 909 * @return true if train has departed 910 */ 911 public boolean isTrainEnRoute() { 912 return !getCurrentLocationName().equals(NONE) && getTrainDepartsRouteLocation() != getCurrentRouteLocation(); 913 } 914 915 /** 916 * Used to determine if train is a local switcher serving one location. Note the 917 * train can have more than location in its route, but all location names must 918 * be "same". See TrainCommon.splitString(String name) for the definition of the 919 * "same" name. 920 * 921 * @return true if local switcher 922 */ 923 public boolean isLocalSwitcher() { 924 String departureName = TrainCommon.splitString(getTrainDepartsName()); 925 Route route = getRoute(); 926 if (route != null) { 927 for (RouteLocation rl : route.getLocationsBySequenceList()) { 928 if (!departureName.equals(rl.getSplitName())) { 929 return false; // not a local switcher 930 } 931 } 932 } 933 return true; 934 } 935 936 public boolean isTurn() { 937 return !isLocalSwitcher() && 938 TrainCommon.splitString(getTrainDepartsName()) 939 .equals(TrainCommon.splitString(getTrainTerminatesName())); 940 } 941 942 /** 943 * Used to determine if train is carrying only passenger cars. 944 * 945 * @return true if only passenger cars have been assigned to this train. 946 */ 947 public boolean isOnlyPassengerCars() { 948 for (Car car : InstanceManager.getDefault(CarManager.class).getList(this)) { 949 if (!car.isPassenger()) { 950 return false; 951 } 952 } 953 return true; 954 } 955 956 List<String> _skipLocationsList = new ArrayList<>(); 957 958 protected String[] getTrainSkipsLocations() { 959 String[] locationIds = new String[_skipLocationsList.size()]; 960 for (int i = 0; i < _skipLocationsList.size(); i++) { 961 locationIds[i] = _skipLocationsList.get(i); 962 } 963 return locationIds; 964 } 965 966 protected void setTrainSkipsLocations(String[] locationIds) { 967 if (locationIds.length > 0) { 968 Arrays.sort(locationIds); 969 for (String id : locationIds) { 970 _skipLocationsList.add(id); 971 } 972 } 973 } 974 975 /** 976 * Train will skip the RouteLocation 977 * 978 * @param routelocationId RouteLocation Id 979 */ 980 public void addTrainSkipsLocation(String routelocationId) { 981 // insert at start of _skipLocationsList, sort later 982 if (!_skipLocationsList.contains(routelocationId)) { 983 _skipLocationsList.add(0, routelocationId); 984 log.debug("train does not stop at {}", routelocationId); 985 setDirtyAndFirePropertyChange(STOPS_CHANGED_PROPERTY, _skipLocationsList.size() - 1, 986 _skipLocationsList.size()); 987 } 988 } 989 990 public void deleteTrainSkipsLocation(String locationId) { 991 _skipLocationsList.remove(locationId); 992 log.debug("train will stop at {}", locationId); 993 setDirtyAndFirePropertyChange(STOPS_CHANGED_PROPERTY, _skipLocationsList.size() + 1, _skipLocationsList.size()); 994 } 995 996 /** 997 * Determines if this train skips a location (doesn't service the location). 998 * 999 * @param locationId The route location id. 1000 * @return true if the train will not service the location. 1001 */ 1002 public boolean isLocationSkipped(String locationId) { 1003 return _skipLocationsList.contains(locationId); 1004 } 1005 1006 List<String> _typeList = new ArrayList<>(); 1007 1008 /** 1009 * Get's the type names of rolling stock this train will service 1010 * 1011 * @return The type names for cars and or engines 1012 */ 1013 protected String[] getTypeNames() { 1014 return _typeList.toArray(new String[0]); 1015 } 1016 1017 public String[] getCarTypeNames() { 1018 List<String> list = new ArrayList<>(); 1019 for (String type : _typeList) { 1020 if (InstanceManager.getDefault(CarTypes.class).containsName(type)) { 1021 list.add(type); 1022 } 1023 } 1024 return list.toArray(new String[0]); 1025 } 1026 1027 public String[] getLocoTypeNames() { 1028 List<String> list = new ArrayList<>(); 1029 for (String type : _typeList) { 1030 if (InstanceManager.getDefault(EngineTypes.class).containsName(type)) { 1031 list.add(type); 1032 } 1033 } 1034 return list.toArray(new String[0]); 1035 } 1036 1037 /** 1038 * Set the type of cars or engines this train will service, see types in Cars 1039 * and Engines. 1040 * 1041 * @param types The type names for cars and or engines 1042 */ 1043 protected void setTypeNames(String[] types) { 1044 if (types.length > 0) { 1045 Arrays.sort(types); 1046 for (String type : types) { 1047 _typeList.add(type); 1048 } 1049 } 1050 } 1051 1052 /** 1053 * Add a car or engine type name that this train will service. 1054 * 1055 * @param type The new type name to service. 1056 */ 1057 public void addTypeName(String type) { 1058 // insert at start of list, sort later 1059 if (type == null || _typeList.contains(type)) { 1060 return; 1061 } 1062 _typeList.add(0, type); 1063 log.debug("Train ({}) add car type ({})", getName(), type); 1064 setDirtyAndFirePropertyChange(TYPES_CHANGED_PROPERTY, _typeList.size() - 1, _typeList.size()); 1065 } 1066 1067 public void deleteTypeName(String type) { 1068 if (_typeList.remove(type)) { 1069 log.debug("Train ({}) delete car type ({})", getName(), type); 1070 setDirtyAndFirePropertyChange(TYPES_CHANGED_PROPERTY, _typeList.size() + 1, _typeList.size()); 1071 } 1072 } 1073 1074 /** 1075 * Returns true if this train will service the type of car or engine. 1076 * 1077 * @param type The car or engine type name. 1078 * @return true if this train will service the particular type. 1079 */ 1080 public boolean isTypeNameAccepted(String type) { 1081 return _typeList.contains(type); 1082 } 1083 1084 protected void replaceType(String oldType, String newType) { 1085 if (isTypeNameAccepted(oldType)) { 1086 deleteTypeName(oldType); 1087 addTypeName(newType); 1088 // adjust loads with type in them 1089 for (String load : getLoadNames()) { 1090 String[] splitLoad = load.split(CarLoad.SPLIT_CHAR); 1091 if (splitLoad.length > 1) { 1092 if (splitLoad[0].equals(oldType)) { 1093 deleteLoadName(load); 1094 if (newType != null) { 1095 load = newType + CarLoad.SPLIT_CHAR + splitLoad[1]; 1096 addLoadName(load); 1097 } 1098 } 1099 } 1100 } 1101 } 1102 } 1103 1104 /** 1105 * Get how this train deals with car road names. 1106 * 1107 * @return ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1108 */ 1109 public String getCarRoadOption() { 1110 return _carRoadOption; 1111 } 1112 1113 /** 1114 * Set how this train deals with car road names. 1115 * 1116 * @param option ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1117 */ 1118 public void setCarRoadOption(String option) { 1119 String old = _carRoadOption; 1120 _carRoadOption = option; 1121 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, old, option); 1122 } 1123 1124 List<String> _carRoadList = new ArrayList<>(); 1125 1126 protected void setCarRoadNames(String[] roads) { 1127 setRoadNames(roads, _carRoadList); 1128 } 1129 1130 /** 1131 * Provides a list of car road names that the train will either service or exclude. 1132 * See setCarRoadOption 1133 * 1134 * @return Array of sorted road names as Strings 1135 */ 1136 public String[] getCarRoadNames() { 1137 String[] roads = _carRoadList.toArray(new String[0]); 1138 if (_carRoadList.size() > 0) { 1139 Arrays.sort(roads); 1140 } 1141 return roads; 1142 } 1143 1144 /** 1145 * Add a car road name that the train will either service or exclude. See 1146 * setCarRoadOption 1147 * 1148 * @param road The string road name. 1149 * @return true if road name was added, false if road name wasn't in the list. 1150 */ 1151 public boolean addCarRoadName(String road) { 1152 if (_carRoadList.contains(road)) { 1153 return false; 1154 } 1155 _carRoadList.add(road); 1156 log.debug("train ({}) add car road {}", getName(), road); 1157 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _carRoadList.size() - 1, _carRoadList.size()); 1158 return true; 1159 } 1160 1161 /** 1162 * Delete a car road name that the train will either service or exclude. See 1163 * setRoadOption 1164 * 1165 * @param road The string road name to delete. 1166 * @return true if road name was removed, false if road name wasn't in the list. 1167 */ 1168 public boolean deleteCarRoadName(String road) { 1169 if (_carRoadList.remove(road)) { 1170 log.debug("train ({}) delete car road {}", getName(), road); 1171 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _carRoadList.size() + 1, _carRoadList.size()); 1172 return true; 1173 } 1174 return false; 1175 } 1176 1177 /** 1178 * Determine if train will service a specific road name for a car. 1179 * 1180 * @param road the road name to check. 1181 * @return true if train will service this road name. 1182 */ 1183 public boolean isCarRoadNameAccepted(String road) { 1184 if (_carRoadOption.equals(ALL_ROADS)) { 1185 return true; 1186 } 1187 if (_carRoadOption.equals(INCLUDE_ROADS)) { 1188 return _carRoadList.contains(road); 1189 } 1190 // exclude! 1191 return !_carRoadList.contains(road); 1192 } 1193 1194 /** 1195 * Get how this train deals with locomotive road names. 1196 * 1197 * @return ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1198 */ 1199 public String getLocoRoadOption() { 1200 return _locoRoadOption; 1201 } 1202 1203 /** 1204 * Set how this train deals with locomotive road names. 1205 * 1206 * @param option ALL_ROADS INCLUDE_ROADS EXCLUDE_ROADS 1207 */ 1208 public void setLocoRoadOption(String option) { 1209 String old = _locoRoadOption; 1210 _locoRoadOption = option; 1211 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, old, option); 1212 } 1213 1214 List<String> _locoRoadList = new ArrayList<>(); 1215 1216 protected void setLocoRoadNames(String[] roads) { 1217 setRoadNames(roads, _locoRoadList); 1218 } 1219 1220 private void setRoadNames(String[] roads, List<String> list) { 1221 if (roads.length > 0) { 1222 Arrays.sort(roads); 1223 for (String road : roads) { 1224 if (!road.isEmpty()) { 1225 list.add(road); 1226 } 1227 } 1228 } 1229 } 1230 1231 /** 1232 * Provides a list of engine road names that the train will either service or exclude. 1233 * See setLocoRoadOption 1234 * 1235 * @return Array of sorted road names as Strings 1236 */ 1237 public String[] getLocoRoadNames() { 1238 String[] roads = _locoRoadList.toArray(new String[0]); 1239 if (_locoRoadList.size() > 0) { 1240 Arrays.sort(roads); 1241 } 1242 return roads; 1243 } 1244 1245 /** 1246 * Add a engine road name that the train will either service or exclude. See 1247 * setLocoRoadOption 1248 * 1249 * @param road The string road name. 1250 * @return true if road name was added, false if road name wasn't in the list. 1251 */ 1252 public boolean addLocoRoadName(String road) { 1253 if (_locoRoadList.contains(road)) { 1254 return false; 1255 } 1256 _locoRoadList.add(road); 1257 log.debug("train ({}) add engine road {}", getName(), road); 1258 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _locoRoadList.size() - 1, _locoRoadList.size()); 1259 return true; 1260 } 1261 1262 /** 1263 * Delete a engine road name that the train will either service or exclude. See 1264 * setLocoRoadOption 1265 * 1266 * @param road The string road name to delete. 1267 * @return true if road name was removed, false if road name wasn't in the list. 1268 */ 1269 public boolean deleteLocoRoadName(String road) { 1270 if (_locoRoadList.remove(road)) { 1271 log.debug("train ({}) delete engine road {}", getName(), road); 1272 setDirtyAndFirePropertyChange(ROADS_CHANGED_PROPERTY, _locoRoadList.size() + 1, _locoRoadList.size()); 1273 return true; 1274 } 1275 return false; 1276 } 1277 1278 /** 1279 * Determine if train will service a specific road name for an engine. 1280 * 1281 * @param road the road name to check. 1282 * @return true if train will service this road name. 1283 */ 1284 public boolean isLocoRoadNameAccepted(String road) { 1285 if (_locoRoadOption.equals(ALL_ROADS)) { 1286 return true; 1287 } 1288 if (_locoRoadOption.equals(INCLUDE_ROADS)) { 1289 return _locoRoadList.contains(road); 1290 } 1291 // exclude! 1292 return !_locoRoadList.contains(road); 1293 } 1294 1295 protected void replaceRoad(String oldRoad, String newRoad) { 1296 if (newRoad != null) { 1297 if (deleteCarRoadName(oldRoad)) { 1298 addCarRoadName(newRoad); 1299 } 1300 if (deleteLocoRoadName(oldRoad)) { 1301 addLocoRoadName(newRoad); 1302 } 1303 if (getEngineRoad().equals(oldRoad)) { 1304 setEngineRoad(newRoad); 1305 } 1306 if (getCabooseRoad().equals(oldRoad)) { 1307 setCabooseRoad(newRoad); 1308 } 1309 if (getSecondLegEngineRoad().equals(oldRoad)) { 1310 setSecondLegEngineRoad(newRoad); 1311 } 1312 if (getSecondLegCabooseRoad().equals(oldRoad)) { 1313 setSecondLegCabooseRoad(newRoad); 1314 } 1315 if (getThirdLegEngineRoad().equals(oldRoad)) { 1316 setThirdLegEngineRoad(newRoad); 1317 } 1318 if (getThirdLegCabooseRoad().equals(oldRoad)) { 1319 setThirdLegCabooseRoad(newRoad); 1320 } 1321 } 1322 } 1323 1324 /** 1325 * Gets the car load option for this train. 1326 * 1327 * @return ALL_LOADS INCLUDE_LOADS EXCLUDE_LOADS 1328 */ 1329 public String getLoadOption() { 1330 return _loadOption; 1331 } 1332 1333 /** 1334 * Set how this train deals with car loads 1335 * 1336 * @param option ALL_LOADS INCLUDE_LOADS EXCLUDE_LOADS 1337 */ 1338 public void setLoadOption(String option) { 1339 String old = _loadOption; 1340 _loadOption = option; 1341 setDirtyAndFirePropertyChange(LOADS_CHANGED_PROPERTY, old, option); 1342 } 1343 1344 List<String> _loadList = new ArrayList<>(); 1345 1346 protected void setLoadNames(String[] loads) { 1347 if (loads.length > 0) { 1348 Arrays.sort(loads); 1349 for (String load : loads) { 1350 if (!load.isEmpty()) { 1351 _loadList.add(load); 1352 } 1353 } 1354 } 1355 } 1356 1357 /** 1358 * Provides a list of loads that the train will either service or exclude. See 1359 * setLoadOption 1360 * 1361 * @return Array of load names as Strings 1362 */ 1363 public String[] getLoadNames() { 1364 String[] loads = _loadList.toArray(new String[0]); 1365 if (_loadList.size() > 0) { 1366 Arrays.sort(loads); 1367 } 1368 return loads; 1369 } 1370 1371 /** 1372 * Add a load that the train will either service or exclude. See setLoadOption 1373 * 1374 * @param load The string load name. 1375 * @return true if load name was added, false if load name wasn't in the list. 1376 */ 1377 public boolean addLoadName(String load) { 1378 if (_loadList.contains(load)) { 1379 return false; 1380 } 1381 _loadList.add(load); 1382 log.debug("train ({}) add car load {}", getName(), load); 1383 setDirtyAndFirePropertyChange(LOADS_CHANGED_PROPERTY, _loadList.size() - 1, _loadList.size()); 1384 return true; 1385 } 1386 1387 /** 1388 * Delete a load name that the train will either service or exclude. See 1389 * setLoadOption 1390 * 1391 * @param load The string load name. 1392 * @return true if load name was removed, false if load name wasn't in the list. 1393 */ 1394 public boolean deleteLoadName(String load) { 1395 if (_loadList.remove(load)) { 1396 log.debug("train ({}) delete car load {}", getName(), load); 1397 setDirtyAndFirePropertyChange(LOADS_CHANGED_PROPERTY, _loadList.size() + 1, _loadList.size()); 1398 return true; 1399 } 1400 return false; 1401 } 1402 1403 /** 1404 * Determine if train will service a specific load name. 1405 * 1406 * @param load the load name to check. 1407 * @return true if train will service this load. 1408 */ 1409 public boolean isLoadNameAccepted(String load) { 1410 if (_loadOption.equals(ALL_LOADS)) { 1411 return true; 1412 } 1413 if (_loadOption.equals(INCLUDE_LOADS)) { 1414 return _loadList.contains(load); 1415 } 1416 // exclude! 1417 return !_loadList.contains(load); 1418 } 1419 1420 /** 1421 * Determine if train will service a specific load and car type. 1422 * 1423 * @param load the load name to check. 1424 * @param type the type of car used to carry the load. 1425 * @return true if train will service this load. 1426 */ 1427 public boolean isLoadNameAccepted(String load, String type) { 1428 if (_loadOption.equals(ALL_LOADS)) { 1429 return true; 1430 } 1431 if (_loadOption.equals(INCLUDE_LOADS)) { 1432 return _loadList.contains(load) || _loadList.contains(type + CarLoad.SPLIT_CHAR + load); 1433 } 1434 // exclude! 1435 return !_loadList.contains(load) && !_loadList.contains(type + CarLoad.SPLIT_CHAR + load); 1436 } 1437 1438 public String getOwnerOption() { 1439 return _ownerOption; 1440 } 1441 1442 /** 1443 * Set how this train deals with car owner names 1444 * 1445 * @param option ALL_OWNERS INCLUDE_OWNERS EXCLUDE_OWNERS 1446 */ 1447 public void setOwnerOption(String option) { 1448 String old = _ownerOption; 1449 _ownerOption = option; 1450 setDirtyAndFirePropertyChange(OWNERS_CHANGED_PROPERTY, old, option); 1451 } 1452 1453 List<String> _ownerList = new ArrayList<>(); 1454 1455 protected void setOwnerNames(String[] owners) { 1456 if (owners.length > 0) { 1457 Arrays.sort(owners); 1458 for (String owner : owners) { 1459 if (!owner.isEmpty()) { 1460 _ownerList.add(owner); 1461 } 1462 } 1463 } 1464 } 1465 1466 /** 1467 * Provides a list of owner names that the train will either service or exclude. 1468 * See setOwnerOption 1469 * 1470 * @return Array of owner names as Strings 1471 */ 1472 public String[] getOwnerNames() { 1473 String[] owners = _ownerList.toArray(new String[0]); 1474 if (_ownerList.size() > 0) { 1475 Arrays.sort(owners); 1476 } 1477 return owners; 1478 } 1479 1480 /** 1481 * Add a owner name that the train will either service or exclude. See 1482 * setOwnerOption 1483 * 1484 * @param owner The string representing the owner's name. 1485 * @return true if owner name was added, false if owner name wasn't in the list. 1486 */ 1487 public boolean addOwnerName(String owner) { 1488 if (_ownerList.contains(owner)) { 1489 return false; 1490 } 1491 _ownerList.add(owner); 1492 log.debug("train ({}) add car owner {}", getName(), owner); 1493 setDirtyAndFirePropertyChange(OWNERS_CHANGED_PROPERTY, _ownerList.size() - 1, _ownerList.size()); 1494 return true; 1495 } 1496 1497 /** 1498 * Delete a owner name that the train will either service or exclude. See 1499 * setOwnerOption 1500 * 1501 * @param owner The string representing the owner's name. 1502 * @return true if owner name was removed, false if owner name wasn't in the 1503 * list. 1504 */ 1505 public boolean deleteOwnerName(String owner) { 1506 if (_ownerList.remove(owner)) { 1507 log.debug("train ({}) delete car owner {}", getName(), owner); 1508 setDirtyAndFirePropertyChange(OWNERS_CHANGED_PROPERTY, _ownerList.size() + 1, _ownerList.size()); 1509 return true; 1510 } 1511 return false; 1512 } 1513 1514 /** 1515 * Determine if train will service a specific owner name. 1516 * 1517 * @param owner the owner name to check. 1518 * @return true if train will service this owner name. 1519 */ 1520 public boolean isOwnerNameAccepted(String owner) { 1521 if (_ownerOption.equals(ALL_OWNERS)) { 1522 return true; 1523 } 1524 if (_ownerOption.equals(INCLUDE_OWNERS)) { 1525 return _ownerList.contains(owner); 1526 } 1527 // exclude! 1528 return !_ownerList.contains(owner); 1529 } 1530 1531 protected void replaceOwner(String oldName, String newName) { 1532 if (deleteOwnerName(oldName)) { 1533 addOwnerName(newName); 1534 } 1535 } 1536 1537 /** 1538 * Only rolling stock built in or after this year will be used. 1539 * 1540 * @param year A string representing a year. 1541 */ 1542 public void setBuiltStartYear(String year) { 1543 String old = _builtStartYear; 1544 _builtStartYear = year; 1545 if (!old.equals(year)) { 1546 setDirtyAndFirePropertyChange(BUILT_YEAR_CHANGED_PROPERTY, old, year); 1547 } 1548 } 1549 1550 public String getBuiltStartYear() { 1551 return _builtStartYear; 1552 } 1553 1554 /** 1555 * Only rolling stock built in or before this year will be used. 1556 * 1557 * @param year A string representing a year. 1558 */ 1559 public void setBuiltEndYear(String year) { 1560 String old = _builtEndYear; 1561 _builtEndYear = year; 1562 if (!old.equals(year)) { 1563 setDirtyAndFirePropertyChange(BUILT_YEAR_CHANGED_PROPERTY, old, year); 1564 } 1565 } 1566 1567 public String getBuiltEndYear() { 1568 return _builtEndYear; 1569 } 1570 1571 /** 1572 * Determine if train will service rolling stock by built date. 1573 * 1574 * @param date A string representing the built date for a car or engine. 1575 * @return true is built date is in the acceptable range. 1576 */ 1577 public boolean isBuiltDateAccepted(String date) { 1578 if (getBuiltStartYear().equals(NONE) && getBuiltEndYear().equals(NONE)) { 1579 return true; // range dates not defined 1580 } 1581 int startYear = 0; // default start year; 1582 int endYear = 99999; // default end year; 1583 int builtYear = -1900; 1584 if (!getBuiltStartYear().equals(NONE)) { 1585 try { 1586 startYear = Integer.parseInt(getBuiltStartYear()); 1587 } catch (NumberFormatException e) { 1588 log.debug("Train ({}) built start date not initialized, start: {}", getName(), getBuiltStartYear()); 1589 } 1590 } 1591 if (!getBuiltEndYear().equals(NONE)) { 1592 try { 1593 endYear = Integer.parseInt(getBuiltEndYear()); 1594 } catch (NumberFormatException e) { 1595 log.debug("Train ({}) built end date not initialized, end: {}", getName(), getBuiltEndYear()); 1596 } 1597 } 1598 try { 1599 builtYear = Integer.parseInt(RollingStockManager.convertBuildDate(date)); 1600 } catch (NumberFormatException e) { 1601 log.debug("Unable to parse car built date {}", date); 1602 } 1603 if (startYear < builtYear && builtYear < endYear) { 1604 return true; 1605 } 1606 return false; 1607 } 1608 1609 private final boolean debugFlag = false; 1610 1611 /** 1612 * Determines if this train will service this car. Note this code doesn't check 1613 * the location or tracks that needs to be done separately. See Router.java. 1614 * 1615 * @param car The car to be tested. 1616 * @return true if this train can service the car. 1617 */ 1618 public boolean isServiceable(Car car) { 1619 return isServiceable(null, car); 1620 } 1621 1622 /** 1623 * Note that this code was written after TrainBuilder. It does pretty much the 1624 * same as TrainBuilder but with much fewer build report messages. 1625 * 1626 * @param buildReport PrintWriter 1627 * @param car the car to be tested 1628 * @return true if this train can service the car. 1629 */ 1630 public boolean isServiceable(PrintWriter buildReport, Car car) { 1631 setServiceStatus(NONE); 1632 // check to see if train can carry car 1633 if (!isTypeNameAccepted(car.getTypeName())) { 1634 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCarType", 1635 getName(), car.toString(), car.getTypeName())); 1636 return false; 1637 } 1638 if (!isLoadNameAccepted(car.getLoadName(), car.getTypeName())) { 1639 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCarLoad", 1640 getName(), car.toString(), car.getTypeName(), car.getLoadName())); 1641 return false; 1642 } 1643 if (!isBuiltDateAccepted(car.getBuilt()) || 1644 !isOwnerNameAccepted(car.getOwnerName()) || 1645 !isCarRoadNameAccepted(car.getRoadName())) { 1646 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCar", 1647 getName(), car.toString())); 1648 return false; 1649 } 1650 1651 Route route = getRoute(); 1652 if (route == null) { 1653 return false; 1654 } 1655 1656 if (car.getLocation() == null || car.getTrack() == null) { 1657 return false; 1658 } 1659 1660 // determine if the car's location is serviced by this train 1661 if (route.getLastLocationByName(car.getLocationName()) == null) { 1662 addLine(buildReport, Bundle.getMessage("trainNotThisLocation", 1663 getName(), car.getLocationName())); 1664 return false; 1665 } 1666 // determine if the car's destination is serviced by this train 1667 // check to see if destination is staging and is also the last location in the train's route 1668 if (car.getDestination() != null && 1669 (route.getLastLocationByName(car.getDestinationName()) == null || 1670 (car.getDestination().isStaging() && 1671 getTrainTerminatesRouteLocation().getLocation() != car.getDestination()))) { 1672 addLine(buildReport, Bundle.getMessage("trainNotThisLocation", 1673 getName(), car.getDestinationName())); 1674 return false; 1675 } 1676 // now find the car in the train's route 1677 List<RouteLocation> rLocations = route.getLocationsBySequenceList(); 1678 for (RouteLocation rLoc : rLocations) { 1679 if (rLoc.getName().equals(car.getLocationName()) && 1680 rLoc.isPickUpAllowed() && 1681 rLoc.getMaxCarMoves() > 0 && 1682 !isLocationSkipped(rLoc.getId()) && 1683 ((car.getLocation().getTrainDirections() & rLoc.getTrainDirection()) != 0 || isLocalSwitcher())) { 1684 1685 if (((car.getTrack().getTrainDirections() & rLoc.getTrainDirection()) == 0 && !isLocalSwitcher()) || 1686 !car.getTrack().isPickupTrainAccepted(this)) { 1687 addLine(buildReport, 1688 Bundle.getMessage("trainCanNotServiceCarFrom", 1689 getName(), car.toString(), car.getLocationName(), car.getTrackName(), 1690 rLoc.getId())); 1691 continue; 1692 } 1693 if (debugFlag) { 1694 log.debug("Car ({}) can be picked up by train ({}) location ({}, {}) destination ({}, {})", 1695 car.toString(), getName(), car.getLocationName(), car.getTrackName(), 1696 car.getDestinationName(), car.getDestinationTrackName()); 1697 } 1698 addLine(buildReport, Bundle.getMessage("trainCanPickUpCar", 1699 getName(), car.toString(), car.getLocationName(), car.getTrackName(), rLoc.getId())); 1700 if (car.getDestination() == null) { 1701 if (debugFlag) { 1702 log.debug("Car ({}) does not have a destination", car.toString()); 1703 } 1704 return true; 1705 } 1706 // now check car's destination 1707 return isServiceableDestination(buildReport, car, rLoc, rLocations); 1708 } else if (rLoc.getName().equals(car.getLocationName())) { 1709 addLine(buildReport, Bundle.getMessage("trainCanNotServiceCarFrom", 1710 getName(), car.toString(), car.getLocationName(), car.getTrackName(), rLoc.getId())); 1711 } 1712 } 1713 if (debugFlag) { 1714 log.debug("Train ({}) can't service car ({}) from ({}, {})", getName(), car.toString(), 1715 car.getLocationName(), car.getTrackName()); 1716 } 1717 return false; 1718 } 1719 1720 /** 1721 * Second step in determining if train can service car, check to see if car's 1722 * destination is serviced by this train's route. 1723 * 1724 * @param buildReport add messages if needed to build report 1725 * @param car The test car 1726 * @param rLoc Where in the train's route the car was found 1727 * @param rLocations The ordered routeLocations in this train's route 1728 * @return true if car's destination can be serviced 1729 */ 1730 private boolean isServiceableDestination(PrintWriter buildReport, Car car, RouteLocation rLoc, 1731 List<RouteLocation> rLocations) { 1732 // need the car's length when building train 1733 int length = car.getTotalLength(); 1734 // car can be a kernel so get total length 1735 if (car.getKernel() != null) { 1736 length = car.getKernel().getTotalLength(); 1737 } 1738 // now see if the train's route services the car's destination 1739 for (int k = rLocations.indexOf(rLoc); k < rLocations.size(); k++) { 1740 RouteLocation rldest = rLocations.get(k); 1741 if (rldest.getName().equals(car.getDestinationName()) && 1742 rldest.isDropAllowed() && 1743 rldest.getMaxCarMoves() > 0 && 1744 !isLocationSkipped(rldest.getId()) && 1745 ((car.getDestination().getTrainDirections() & rldest.getTrainDirection()) != 0 || 1746 isLocalSwitcher()) && 1747 (!Setup.isCheckCarDestinationEnabled() || 1748 car.getTrack().isDestinationAccepted(car.getDestination()))) { 1749 // found a destination, now check destination track 1750 if (car.getDestinationTrack() != null) { 1751 if (!isServicableTrack(buildReport, car, rldest, car.getDestinationTrack())) { 1752 continue; 1753 } 1754 } else if (rldest.getLocation().isStaging() && 1755 getStatusCode() == CODE_BUILDING && 1756 getTerminationTrack() != null && 1757 getTerminationTrack().getLocation() == rldest.getLocation()) { 1758 if (debugFlag) { 1759 log.debug("Car ({}) destination is staging, check train ({}) termination track ({})", 1760 car.toString(), getName(), getTerminationTrack().getName()); 1761 } 1762 String status = car.checkDestination(getTerminationTrack().getLocation(), getTerminationTrack()); 1763 if (!status.equals(Track.OKAY)) { 1764 addLine(buildReport, 1765 Bundle.getMessage("trainCanNotDeliverToStaging", 1766 getName(), car.toString(), 1767 getTerminationTrack().getLocation().getName(), 1768 getTerminationTrack().getName(), status)); 1769 setServiceStatus(status); 1770 continue; 1771 } 1772 } else { 1773 if (debugFlag) { 1774 log.debug("Find track for car ({}) at destination ({})", car.toString(), 1775 car.getDestinationName()); 1776 } 1777 // determine if there's a destination track that is willing to accept this car 1778 String status = ""; 1779 List<Track> tracks = rldest.getLocation().getTracksList(); 1780 for (Track track : tracks) { 1781 if (!isServicableTrack(buildReport, car, rldest, track)) { 1782 continue; 1783 } 1784 // will the track accept this car? 1785 status = track.isRollingStockAccepted(car); 1786 if (status.equals(Track.OKAY) || status.startsWith(Track.LENGTH)) { 1787 if (debugFlag) { 1788 log.debug("Found track ({}) for car ({})", track.getName(), car.toString()); 1789 } 1790 break; // found track 1791 } 1792 } 1793 if (!status.equals(Track.OKAY) && !status.startsWith(Track.LENGTH)) { 1794 if (debugFlag) { 1795 log.debug("Destination ({}) can not service car ({}) using train ({}) no track available", 1796 car.getDestinationName(), car.toString(), getName()); // NOI18N 1797 } 1798 addLine(buildReport, Bundle.getMessage("trainCanNotDeliverNoTracks", 1799 getName(), car.toString(), car.getDestinationName(), rldest.getId())); 1800 continue; 1801 } 1802 } 1803 // restriction to only carry cars to terminal? 1804 // ignore send to terminal if a local move 1805 if (isSendCarsToTerminalEnabled() && 1806 !car.isLocalMove() && 1807 !car.getSplitLocationName() 1808 .equals(TrainCommon.splitString(getTrainDepartsName())) && 1809 !car.getSplitDestinationName() 1810 .equals(TrainCommon.splitString(getTrainTerminatesName()))) { 1811 if (debugFlag) { 1812 log.debug("option send cars to terminal is enabled"); 1813 } 1814 addLine(buildReport, 1815 Bundle.getMessage("trainCanNotCarryCarOption", 1816 getName(), car.toString(), car.getLocationName(), 1817 car.getTrackName(), car.getDestinationName(), 1818 car.getDestinationTrackName())); 1819 continue; 1820 } 1821 // don't allow local move when car is in staging 1822 if (!isTurn() && car.getTrack().isStaging() && 1823 rldest.getLocation() == car.getLocation()) { 1824 log.debug( 1825 "Car ({}) at ({}, {}) not allowed to perform local move in staging ({})", 1826 car.toString(), car.getLocationName(), car.getTrackName(), rldest.getName()); 1827 continue; 1828 } 1829 // allow car to return to staging? 1830 if (isAllowReturnToStagingEnabled() && 1831 car.getTrack().isStaging() && 1832 rldest.getLocation() == car.getLocation()) { 1833 addLine(buildReport, 1834 Bundle.getMessage("trainCanReturnCarToStaging", 1835 getName(), car.toString(), car.getDestinationName(), 1836 car.getDestinationTrackName())); 1837 return true; 1838 } 1839 // is this a local move? 1840 if (!isLocalSwitcher() && 1841 !isAllowLocalMovesEnabled() && 1842 !car.isCaboose() && 1843 !car.hasFred() && 1844 !car.isPassenger() && 1845 car.isLocalMove()) { 1846 if (debugFlag) { 1847 log.debug("Local move not allowed"); 1848 } 1849 addLine(buildReport, Bundle.getMessage("trainCanNotPerformLocalMove", 1850 getName(), car.toString(), car.getLocationName())); 1851 continue; 1852 } 1853 // Can cars travel from origin to terminal? 1854 if (!isAllowThroughCarsEnabled() && 1855 TrainCommon.splitString(getTrainDepartsName()) 1856 .equals(rLoc.getSplitName()) && 1857 TrainCommon.splitString(getTrainTerminatesName()) 1858 .equals(rldest.getSplitName()) && 1859 !TrainCommon.splitString(getTrainDepartsName()) 1860 .equals(TrainCommon.splitString(getTrainTerminatesName())) && 1861 !isLocalSwitcher() && 1862 !car.isCaboose() && 1863 !car.hasFred() && 1864 !car.isPassenger()) { 1865 if (debugFlag) { 1866 log.debug("Through car ({}) not allowed", car.toString()); 1867 } 1868 addLine(buildReport, Bundle.getMessage("trainDoesNotCarryOriginTerminal", 1869 getName(), car.getLocationName(), car.getDestinationName())); 1870 continue; 1871 } 1872 // check to see if moves are available 1873 if (getStatusCode() == CODE_BUILDING && rldest.getMaxCarMoves() - rldest.getCarMoves() <= 0) { 1874 setServiceStatus(Bundle.getMessage("trainNoMoves", 1875 getName(), getRoute().getName(), rldest.getId(), rldest.getName())); 1876 if (debugFlag) { 1877 log.debug("No available moves for destination {}", rldest.getName()); 1878 } 1879 addLine(buildReport, getServiceStatus()); 1880 continue; 1881 } 1882 if (debugFlag) { 1883 log.debug("Car ({}) can be dropped by train ({}) to ({}, {})", car.toString(), getName(), 1884 car.getDestinationName(), car.getDestinationTrackName()); 1885 } 1886 return true; 1887 } 1888 // check to see if train length is okay 1889 if (getStatusCode() == CODE_BUILDING && rldest.getTrainLength() + length > rldest.getMaxTrainLength()) { 1890 setServiceStatus(Bundle.getMessage("trainExceedsMaximumLength", 1891 getName(), getRoute().getName(), rldest.getId(), rldest.getMaxTrainLength(), 1892 Setup.getLengthUnit().toLowerCase(), rldest.getName(), car.toString(), 1893 rldest.getTrainLength() + length - rldest.getMaxTrainLength())); 1894 if (debugFlag) { 1895 log.debug("Car ({}) exceeds maximum train length {} when departing ({})", car.toString(), 1896 rldest.getMaxTrainLength(), rldest.getName()); 1897 } 1898 addLine(buildReport, getServiceStatus()); 1899 return false; 1900 } 1901 } 1902 addLine(buildReport, Bundle.getMessage("trainCanNotDeliverToDestination", 1903 getName(), car.toString(), car.getDestinationName(), car.getDestinationTrackName())); 1904 return false; 1905 } 1906 1907 private boolean isServicableTrack(PrintWriter buildReport, Car car, RouteLocation rldest, Track track) { 1908 if ((track.getTrainDirections() & rldest.getTrainDirection()) == 0 && !isLocalSwitcher()) { 1909 addLine(buildReport, Bundle.getMessage("buildCanNotDropRsUsingTrain", 1910 car.toString(), rldest.getTrainDirectionString(), track.getName())); 1911 return false; 1912 } 1913 if (!track.isDropTrainAccepted(this)) { 1914 addLine(buildReport, Bundle.getMessage("buildCanNotDropTrain", 1915 car.toString(), getName(), track.getTrackTypeName(), track.getLocation().getName(), 1916 track.getName())); 1917 return false; 1918 } 1919 return true; 1920 } 1921 1922 protected static final String SEVEN = Setup.BUILD_REPORT_VERY_DETAILED; 1923 1924 private void addLine(PrintWriter buildReport, String string) { 1925 if (Setup.getRouterBuildReportLevel().equals(SEVEN)) { 1926 TrainCommon.addLine(buildReport, SEVEN, string); 1927 } 1928 } 1929 1930 protected void setServiceStatus(String status) { 1931 _serviceStatus = status; 1932 } 1933 1934 /** 1935 * Returns the statusCode of the "isServiceable(Car)" routine. There are two 1936 * statusCodes that need special consideration when the train is being built, 1937 * the moves in a train's route and the maximum train length. NOTE: The code 1938 * using getServiceStatus() currently assumes that if there's a service status 1939 * that the issue is either route moves or maximum train length. 1940 * 1941 * @return The statusCode. 1942 */ 1943 public String getServiceStatus() { 1944 return _serviceStatus; 1945 } 1946 1947 /** 1948 * @return The number of cars worked by this train 1949 */ 1950 public int getNumberCarsWorked() { 1951 int count = 0; 1952 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 1953 if (rs.getRouteLocation() != null) { 1954 count++; 1955 } 1956 } 1957 return count; 1958 } 1959 1960 public void setNumberCarsRequested(int number) { 1961 _statusCarsRequested = number; 1962 } 1963 1964 public int getNumberCarsRequested() { 1965 return _statusCarsRequested; 1966 } 1967 1968 public void setTerminationDate(String date) { 1969 _statusTerminatedDate = date; 1970 } 1971 1972 public String getTerminationDate() { 1973 return _statusTerminatedDate; 1974 } 1975 1976 /** 1977 * Gets the number of cars in the train at the current location in the train's 1978 * route. 1979 * 1980 * @return The number of cars currently in the train 1981 */ 1982 public int getNumberCarsInTrain() { 1983 return getNumberCarsInTrain(getCurrentRouteLocation()); 1984 } 1985 1986 /** 1987 * Gets the number of cars in the train when train departs the route location. 1988 * 1989 * @param routeLocation The RouteLocation. 1990 * @return The number of cars in the train departing the route location. 1991 */ 1992 public int getNumberCarsInTrain(RouteLocation routeLocation) { 1993 int number = 0; 1994 Route route = getRoute(); 1995 if (route != null) { 1996 for (RouteLocation rl : route.getLocationsBySequenceList()) { 1997 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 1998 if (rs.getRouteLocation() == rl) { 1999 number++; 2000 } 2001 if (rs.getRouteDestination() == rl) { 2002 number--; 2003 } 2004 } 2005 if (rl == routeLocation) { 2006 break; 2007 } 2008 } 2009 } 2010 return number; 2011 } 2012 2013 /** 2014 * Gets the number of empty cars in the train when train departs the route 2015 * location. 2016 * 2017 * @param routeLocation The RouteLocation. 2018 * @return The number of empty cars in the train departing the route location. 2019 */ 2020 public int getNumberEmptyCarsInTrain(RouteLocation routeLocation) { 2021 int number = 0; 2022 Route route = getRoute(); 2023 if (route != null) { 2024 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2025 for (Car car : InstanceManager.getDefault(CarManager.class).getList(this)) { 2026 if (!car.getLoadType().equals(CarLoad.LOAD_TYPE_EMPTY)) { 2027 continue; 2028 } 2029 if (car.getRouteLocation() == rl) { 2030 number++; 2031 } 2032 if (car.getRouteDestination() == rl) { 2033 number--; 2034 } 2035 } 2036 if (rl == routeLocation) { 2037 break; 2038 } 2039 } 2040 } 2041 2042 return number; 2043 } 2044 2045 public int getNumberLoadedCarsInTrain(RouteLocation routeLocation) { 2046 return getNumberCarsInTrain(routeLocation) - getNumberEmptyCarsInTrain(routeLocation); 2047 } 2048 2049 /** 2050 * Gets the number of cars pulled from a location 2051 * 2052 * @param routeLocation the location 2053 * @return number of pick ups 2054 */ 2055 public int getNumberCarsPickedUp(RouteLocation routeLocation) { 2056 int number = 0; 2057 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2058 if (rs.getRouteLocation() == routeLocation) { 2059 number++; 2060 } 2061 } 2062 return number; 2063 } 2064 2065 /** 2066 * Gets the number of cars delivered to a location 2067 * 2068 * @param routeLocation the location 2069 * @return number of set outs 2070 */ 2071 public int getNumberCarsSetout(RouteLocation routeLocation) { 2072 int number = 0; 2073 for (Car rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2074 if (rs.getRouteDestination() == routeLocation) { 2075 number++; 2076 } 2077 } 2078 return number; 2079 } 2080 2081 /** 2082 * Gets the train's length at the current location in the train's route. 2083 * 2084 * @return The train length at the train's current location 2085 */ 2086 public int getTrainLength() { 2087 return getTrainLength(getCurrentRouteLocation()); 2088 } 2089 2090 /** 2091 * Gets the train's length at the route location specified 2092 * 2093 * @param routeLocation The route location 2094 * @return The train length at the route location 2095 */ 2096 public int getTrainLength(RouteLocation routeLocation) { 2097 int length = 0; 2098 Route route = getRoute(); 2099 if (route != null) { 2100 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2101 for (RollingStock rs : InstanceManager.getDefault(EngineManager.class).getList(this)) { 2102 if (rs.getRouteLocation() == rl) { 2103 length += rs.getTotalLength(); 2104 } 2105 if (rs.getRouteDestination() == rl) { 2106 length += -rs.getTotalLength(); 2107 } 2108 } 2109 for (RollingStock rs : InstanceManager.getDefault(CarManager.class).getList(this)) { 2110 if (rs.getRouteLocation() == rl) { 2111 length += rs.getTotalLength(); 2112 } 2113 if (rs.getRouteDestination() == rl) { 2114 length += -rs.getTotalLength(); 2115 } 2116 } 2117 if (rl == routeLocation) { 2118 break; 2119 } 2120 } 2121 } 2122 return length; 2123 } 2124 2125 /** 2126 * Get the train's weight at the current location. 2127 * 2128 * @return Train's weight in tons. 2129 */ 2130 public int getTrainWeight() { 2131 return getTrainWeight(getCurrentRouteLocation()); 2132 } 2133 2134 public int getTrainWeight(RouteLocation routeLocation) { 2135 int weight = 0; 2136 Route route = getRoute(); 2137 if (route != null) { 2138 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2139 for (RollingStock rs : InstanceManager.getDefault(EngineManager.class).getList(this)) { 2140 if (rs.getRouteLocation() == rl) { 2141 weight += rs.getAdjustedWeightTons(); 2142 } 2143 if (rs.getRouteDestination() == rl) { 2144 weight += -rs.getAdjustedWeightTons(); 2145 } 2146 } 2147 for (Car car : InstanceManager.getDefault(CarManager.class).getList(this)) { 2148 if (car.getRouteLocation() == rl) { 2149 weight += car.getAdjustedWeightTons(); // weight depends 2150 // on car load 2151 } 2152 if (car.getRouteDestination() == rl) { 2153 weight += -car.getAdjustedWeightTons(); 2154 } 2155 } 2156 if (rl == routeLocation) { 2157 break; 2158 } 2159 } 2160 } 2161 return weight; 2162 } 2163 2164 /** 2165 * Gets the train's locomotive horsepower at the route location specified 2166 * 2167 * @param routeLocation The route location 2168 * @return The train's locomotive horsepower at the route location 2169 */ 2170 public int getTrainHorsePower(RouteLocation routeLocation) { 2171 int hp = 0; 2172 Route route = getRoute(); 2173 if (route != null) { 2174 for (RouteLocation rl : route.getLocationsBySequenceList()) { 2175 for (Engine eng : InstanceManager.getDefault(EngineManager.class).getList(this)) { 2176 if (eng.getRouteLocation() == rl) { 2177 hp += eng.getHpInteger(); 2178 } 2179 if (eng.getRouteDestination() == rl) { 2180 hp += -eng.getHpInteger(); 2181 } 2182 } 2183 if (rl == routeLocation) { 2184 break; 2185 } 2186 } 2187 } 2188 return hp; 2189 } 2190 2191 /** 2192 * Gets the current caboose road and number if there's one assigned to the 2193 * train. 2194 * 2195 * @return Road and number of caboose. 2196 */ 2197 public String getCabooseRoadAndNumber() { 2198 String cabooseRoadNumber = NONE; 2199 RouteLocation rl = getCurrentRouteLocation(); 2200 List<Car> cars = InstanceManager.getDefault(CarManager.class).getByTrainList(this); 2201 for (Car car : cars) { 2202 if (car.getRouteLocation() == rl && car.isCaboose()) { 2203 cabooseRoadNumber = 2204 car.getRoadName().split(TrainCommon.HYPHEN)[0] + " " + TrainCommon.splitString(car.getNumber()); 2205 } 2206 } 2207 return cabooseRoadNumber; 2208 } 2209 2210 public void setDescription(String description) { 2211 String old = _description; 2212 _description = description; 2213 if (!old.equals(description)) { 2214 setDirtyAndFirePropertyChange(DESCRIPTION_CHANGED_PROPERTY, old, description); 2215 } 2216 } 2217 2218 public String getRawDescription() { 2219 return _description; 2220 } 2221 2222 /** 2223 * Returns a formated string providing the train's description. {0} = lead 2224 * engine number, {1} = train's departure direction {2} = lead engine road {3} = 2225 * DCC address of lead engine. 2226 * 2227 * @return The train's description. 2228 */ 2229 public String getDescription() { 2230 try { 2231 String description = MessageFormat.format(getRawDescription(), new Object[]{getLeadEngineNumber(), 2232 getTrainDepartsDirection(), getLeadEngineRoadName(), getLeadEngineDccAddress()}); 2233 return description; 2234 } catch (IllegalArgumentException e) { 2235 return "ERROR IN FORMATTING: " + getRawDescription(); 2236 } 2237 } 2238 2239 public void setNumberEngines(String number) { 2240 String old = _numberEngines; 2241 _numberEngines = number; 2242 if (!old.equals(number)) { 2243 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2244 } 2245 } 2246 2247 /** 2248 * Get the number of engines that this train requires. 2249 * 2250 * @return The number of engines that this train requires. 2251 */ 2252 public String getNumberEngines() { 2253 return _numberEngines; 2254 } 2255 2256 /** 2257 * Get the number of engines needed for the second set. 2258 * 2259 * @return The number of engines needed in route 2260 */ 2261 public String getSecondLegNumberEngines() { 2262 return _leg2Engines; 2263 } 2264 2265 public void setSecondLegNumberEngines(String number) { 2266 String old = _leg2Engines; 2267 _leg2Engines = number; 2268 if (!old.equals(number)) { 2269 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2270 } 2271 } 2272 2273 /** 2274 * Get the number of engines needed for the third set. 2275 * 2276 * @return The number of engines needed in route 2277 */ 2278 public String getThirdLegNumberEngines() { 2279 return _leg3Engines; 2280 } 2281 2282 public void setThirdLegNumberEngines(String number) { 2283 String old = _leg3Engines; 2284 _leg3Engines = number; 2285 if (!old.equals(number)) { 2286 setDirtyAndFirePropertyChange("trainNmberEngines", old, number); // NOI18N 2287 } 2288 } 2289 2290 /** 2291 * Set the road name of engines servicing this train. 2292 * 2293 * @param road The road name of engines servicing this train. 2294 */ 2295 public void setEngineRoad(String road) { 2296 String old = _engineRoad; 2297 _engineRoad = road; 2298 if (!old.equals(road)) { 2299 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2300 } 2301 } 2302 2303 /** 2304 * Get the road name of engines servicing this train. 2305 * 2306 * @return The road name of engines servicing this train. 2307 */ 2308 public String getEngineRoad() { 2309 return _engineRoad; 2310 } 2311 2312 /** 2313 * Set the road name of engines servicing this train 2nd leg. 2314 * 2315 * @param road The road name of engines servicing this train. 2316 */ 2317 public void setSecondLegEngineRoad(String road) { 2318 String old = _leg2Road; 2319 _leg2Road = road; 2320 if (!old.equals(road)) { 2321 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2322 } 2323 } 2324 2325 /** 2326 * Get the road name of engines servicing this train 2nd leg. 2327 * 2328 * @return The road name of engines servicing this train. 2329 */ 2330 public String getSecondLegEngineRoad() { 2331 return _leg2Road; 2332 } 2333 2334 /** 2335 * Set the road name of engines servicing this train 3rd leg. 2336 * 2337 * @param road The road name of engines servicing this train. 2338 */ 2339 public void setThirdLegEngineRoad(String road) { 2340 String old = _leg3Road; 2341 _leg3Road = road; 2342 if (!old.equals(road)) { 2343 setDirtyAndFirePropertyChange("trainEngineRoad", old, road); // NOI18N 2344 } 2345 } 2346 2347 /** 2348 * Get the road name of engines servicing this train 3rd leg. 2349 * 2350 * @return The road name of engines servicing this train. 2351 */ 2352 public String getThirdLegEngineRoad() { 2353 return _leg3Road; 2354 } 2355 2356 /** 2357 * Set the model name of engines servicing this train. 2358 * 2359 * @param model The model name of engines servicing this train. 2360 */ 2361 public void setEngineModel(String model) { 2362 String old = _engineModel; 2363 _engineModel = model; 2364 if (!old.equals(model)) { 2365 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2366 } 2367 } 2368 2369 public String getEngineModel() { 2370 return _engineModel; 2371 } 2372 2373 /** 2374 * Set the model name of engines servicing this train's 2nd leg. 2375 * 2376 * @param model The model name of engines servicing this train. 2377 */ 2378 public void setSecondLegEngineModel(String model) { 2379 String old = _leg2Model; 2380 _leg2Model = model; 2381 if (!old.equals(model)) { 2382 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2383 } 2384 } 2385 2386 public String getSecondLegEngineModel() { 2387 return _leg2Model; 2388 } 2389 2390 /** 2391 * Set the model name of engines servicing this train's 3rd leg. 2392 * 2393 * @param model The model name of engines servicing this train. 2394 */ 2395 public void setThirdLegEngineModel(String model) { 2396 String old = _leg3Model; 2397 _leg3Model = model; 2398 if (!old.equals(model)) { 2399 setDirtyAndFirePropertyChange("trainEngineModel", old, model); // NOI18N 2400 } 2401 } 2402 2403 public String getThirdLegEngineModel() { 2404 return _leg3Model; 2405 } 2406 2407 protected void replaceModel(String oldModel, String newModel) { 2408 if (getEngineModel().equals(oldModel)) { 2409 setEngineModel(newModel); 2410 } 2411 if (getSecondLegEngineModel().equals(oldModel)) { 2412 setSecondLegEngineModel(newModel); 2413 } 2414 if (getThirdLegEngineModel().equals(oldModel)) { 2415 setThirdLegEngineModel(newModel); 2416 } 2417 } 2418 2419 /** 2420 * Set the road name of the caboose servicing this train. 2421 * 2422 * @param road The road name of the caboose servicing this train. 2423 */ 2424 public void setCabooseRoad(String road) { 2425 String old = _cabooseRoad; 2426 _cabooseRoad = road; 2427 if (!old.equals(road)) { 2428 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2429 } 2430 } 2431 2432 public String getCabooseRoad() { 2433 return _cabooseRoad; 2434 } 2435 2436 /** 2437 * Set the road name of the second leg caboose servicing this train. 2438 * 2439 * @param road The road name of the caboose servicing this train's 2nd leg. 2440 */ 2441 public void setSecondLegCabooseRoad(String road) { 2442 String old = _leg2CabooseRoad; 2443 _leg2CabooseRoad = road; 2444 if (!old.equals(road)) { 2445 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2446 } 2447 } 2448 2449 public String getSecondLegCabooseRoad() { 2450 return _leg2CabooseRoad; 2451 } 2452 2453 /** 2454 * Set the road name of the third leg caboose servicing this train. 2455 * 2456 * @param road The road name of the caboose servicing this train's 3rd leg. 2457 */ 2458 public void setThirdLegCabooseRoad(String road) { 2459 String old = _leg3CabooseRoad; 2460 _leg3CabooseRoad = road; 2461 if (!old.equals(road)) { 2462 setDirtyAndFirePropertyChange("trainCabooseRoad", old, road); // NOI18N 2463 } 2464 } 2465 2466 public String getThirdLegCabooseRoad() { 2467 return _leg3CabooseRoad; 2468 } 2469 2470 public void setSecondLegStartRouteLocation(RouteLocation rl) { 2471 _leg2Start = rl; 2472 } 2473 2474 public RouteLocation getSecondLegStartRouteLocation() { 2475 return _leg2Start; 2476 } 2477 2478 public String getSecondLegStartLocationName() { 2479 if (getSecondLegStartRouteLocation() == null) { 2480 return NONE; 2481 } 2482 return getSecondLegStartRouteLocation().getName(); 2483 } 2484 2485 public void setThirdLegStartRouteLocation(RouteLocation rl) { 2486 _leg3Start = rl; 2487 } 2488 2489 public RouteLocation getThirdLegStartRouteLocation() { 2490 return _leg3Start; 2491 } 2492 2493 public String getThirdLegStartLocationName() { 2494 if (getThirdLegStartRouteLocation() == null) { 2495 return NONE; 2496 } 2497 return getThirdLegStartRouteLocation().getName(); 2498 } 2499 2500 public void setSecondLegEndRouteLocation(RouteLocation rl) { 2501 _end2Leg = rl; 2502 } 2503 2504 public String getSecondLegEndLocationName() { 2505 if (getSecondLegEndRouteLocation() == null) { 2506 return NONE; 2507 } 2508 return getSecondLegEndRouteLocation().getName(); 2509 } 2510 2511 public RouteLocation getSecondLegEndRouteLocation() { 2512 return _end2Leg; 2513 } 2514 2515 public void setThirdLegEndRouteLocation(RouteLocation rl) { 2516 _leg3End = rl; 2517 } 2518 2519 public RouteLocation getThirdLegEndRouteLocation() { 2520 return _leg3End; 2521 } 2522 2523 public String getThirdLegEndLocationName() { 2524 if (getThirdLegEndRouteLocation() == null) { 2525 return NONE; 2526 } 2527 return getThirdLegEndRouteLocation().getName(); 2528 } 2529 2530 /** 2531 * Optional changes to train while en route. 2532 * 2533 * @param options NO_CABOOSE_OR_FRED, CHANGE_ENGINES, ADD_CABOOSE, 2534 * HELPER_ENGINES, REMOVE_CABOOSE 2535 */ 2536 public void setSecondLegOptions(int options) { 2537 int old = _leg2Options; 2538 _leg2Options = options; 2539 if (old != options) { 2540 setDirtyAndFirePropertyChange("trainLegOptions", old, options); // NOI18N 2541 } 2542 } 2543 2544 public int getSecondLegOptions() { 2545 return _leg2Options; 2546 } 2547 2548 /** 2549 * Optional changes to train while en route. 2550 * 2551 * @param options NO_CABOOSE_OR_FRED, CHANGE_ENGINES, ADD_CABOOSE, 2552 * HELPER_ENGINES, REMOVE_CABOOSE 2553 */ 2554 public void setThirdLegOptions(int options) { 2555 int old = _leg3Options; 2556 _leg3Options = options; 2557 if (old != options) { 2558 setDirtyAndFirePropertyChange("trainLegOptions", old, options); // NOI18N 2559 } 2560 } 2561 2562 public int getThirdLegOptions() { 2563 return _leg3Options; 2564 } 2565 2566 public void setComment(String comment) { 2567 String old = _comment; 2568 _comment = comment; 2569 if (!old.equals(comment)) { 2570 setDirtyAndFirePropertyChange("trainComment", old, comment); // NOI18N 2571 } 2572 } 2573 2574 public String getComment() { 2575 return TrainCommon.getTextColorString(getCommentWithColor()); 2576 } 2577 2578 public String getCommentWithColor() { 2579 return _comment; 2580 } 2581 2582 /** 2583 * Add a script to run before a train is built 2584 * 2585 * @param pathname The script's pathname 2586 */ 2587 public void addBuildScript(String pathname) { 2588 _buildScripts.add(pathname); 2589 setDirtyAndFirePropertyChange("addBuildScript", pathname, null); // NOI18N 2590 } 2591 2592 public void deleteBuildScript(String pathname) { 2593 _buildScripts.remove(pathname); 2594 setDirtyAndFirePropertyChange("deleteBuildScript", null, pathname); // NOI18N 2595 } 2596 2597 /** 2598 * Gets a list of pathnames (scripts) to run before this train is built 2599 * 2600 * @return A list of pathnames to run before this train is built 2601 */ 2602 public List<String> getBuildScripts() { 2603 return _buildScripts; 2604 } 2605 2606 /** 2607 * Add a script to run after a train is built 2608 * 2609 * @param pathname The script's pathname 2610 */ 2611 public void addAfterBuildScript(String pathname) { 2612 _afterBuildScripts.add(pathname); 2613 setDirtyAndFirePropertyChange("addAfterBuildScript", pathname, null); // NOI18N 2614 } 2615 2616 public void deleteAfterBuildScript(String pathname) { 2617 _afterBuildScripts.remove(pathname); 2618 setDirtyAndFirePropertyChange("deleteAfterBuildScript", null, pathname); // NOI18N 2619 } 2620 2621 /** 2622 * Gets a list of pathnames (scripts) to run after this train is built 2623 * 2624 * @return A list of pathnames to run after this train is built 2625 */ 2626 public List<String> getAfterBuildScripts() { 2627 return _afterBuildScripts; 2628 } 2629 2630 /** 2631 * Add a script to run when train is moved 2632 * 2633 * @param pathname The script's pathname 2634 */ 2635 public void addMoveScript(String pathname) { 2636 _moveScripts.add(pathname); 2637 setDirtyAndFirePropertyChange("addMoveScript", pathname, null); // NOI18N 2638 } 2639 2640 public void deleteMoveScript(String pathname) { 2641 _moveScripts.remove(pathname); 2642 setDirtyAndFirePropertyChange("deleteMoveScript", null, pathname); // NOI18N 2643 } 2644 2645 /** 2646 * Gets a list of pathnames (scripts) to run when this train moved 2647 * 2648 * @return A list of pathnames to run when this train moved 2649 */ 2650 public List<String> getMoveScripts() { 2651 return _moveScripts; 2652 } 2653 2654 /** 2655 * Add a script to run when train is terminated 2656 * 2657 * @param pathname The script's pathname 2658 */ 2659 public void addTerminationScript(String pathname) { 2660 _terminationScripts.add(pathname); 2661 setDirtyAndFirePropertyChange("addTerminationScript", pathname, null); // NOI18N 2662 } 2663 2664 public void deleteTerminationScript(String pathname) { 2665 _terminationScripts.remove(pathname); 2666 setDirtyAndFirePropertyChange("deleteTerminationScript", null, pathname); // NOI18N 2667 } 2668 2669 /** 2670 * Gets a list of pathnames (scripts) to run when this train terminates 2671 * 2672 * @return A list of pathnames to run when this train terminates 2673 */ 2674 public List<String> getTerminationScripts() { 2675 return _terminationScripts; 2676 } 2677 2678 /** 2679 * Gets the optional railroad name for this train. 2680 * 2681 * @return Train's railroad name. 2682 */ 2683 public String getRailroadName() { 2684 return _railroadName; 2685 } 2686 2687 /** 2688 * Overrides the default railroad name for this train. 2689 * 2690 * @param name The railroad name for this train. 2691 */ 2692 public void setRailroadName(String name) { 2693 String old = _railroadName; 2694 _railroadName = name; 2695 if (!old.equals(name)) { 2696 setDirtyAndFirePropertyChange("trainRailroadName", old, name); // NOI18N 2697 } 2698 } 2699 2700 public String getManifestLogoPathName() { 2701 return _logoPathName; 2702 } 2703 2704 /** 2705 * Overrides the default logo for this train. 2706 * 2707 * @param pathName file location for the logo. 2708 */ 2709 public void setManifestLogoPathName(String pathName) { 2710 _logoPathName = pathName; 2711 } 2712 2713 public boolean isShowArrivalAndDepartureTimesEnabled() { 2714 return _showTimes; 2715 } 2716 2717 public void setShowArrivalAndDepartureTimes(boolean enable) { 2718 boolean old = _showTimes; 2719 _showTimes = enable; 2720 if (old != enable) { 2721 setDirtyAndFirePropertyChange("showArrivalAndDepartureTimes", old ? "true" : "false", // NOI18N 2722 enable ? "true" : "false"); // NOI18N 2723 } 2724 } 2725 2726 public boolean isSendCarsToTerminalEnabled() { 2727 return _sendToTerminal; 2728 } 2729 2730 public void setSendCarsToTerminalEnabled(boolean enable) { 2731 boolean old = _sendToTerminal; 2732 _sendToTerminal = enable; 2733 if (old != enable) { 2734 setDirtyAndFirePropertyChange("send cars to terminal", old ? "true" : "false", enable ? "true" // NOI18N 2735 : "false"); // NOI18N 2736 } 2737 } 2738 2739 /** 2740 * Allow local moves if car has a custom load or Final Destination 2741 * 2742 * @return true if local move is allowed 2743 */ 2744 public boolean isAllowLocalMovesEnabled() { 2745 return _allowLocalMoves; 2746 } 2747 2748 public void setAllowLocalMovesEnabled(boolean enable) { 2749 boolean old = _allowLocalMoves; 2750 _allowLocalMoves = enable; 2751 if (old != enable) { 2752 setDirtyAndFirePropertyChange("allow local moves", old ? "true" : "false", enable ? "true" // NOI18N 2753 : "false"); // NOI18N 2754 } 2755 } 2756 2757 public boolean isAllowThroughCarsEnabled() { 2758 return _allowThroughCars; 2759 } 2760 2761 public void setAllowThroughCarsEnabled(boolean enable) { 2762 boolean old = _allowThroughCars; 2763 _allowThroughCars = enable; 2764 if (old != enable) { 2765 setDirtyAndFirePropertyChange("allow through cars", old ? "true" : "false", enable ? "true" // NOI18N 2766 : "false"); // NOI18N 2767 } 2768 } 2769 2770 public boolean isBuildTrainNormalEnabled() { 2771 return _buildNormal; 2772 } 2773 2774 public void setBuildTrainNormalEnabled(boolean enable) { 2775 boolean old = _buildNormal; 2776 _buildNormal = enable; 2777 if (old != enable) { 2778 setDirtyAndFirePropertyChange("build train normal", old ? "true" : "false", enable ? "true" // NOI18N 2779 : "false"); // NOI18N 2780 } 2781 } 2782 2783 /** 2784 * When true allow a turn to return cars to staging. A turn is a train that 2785 * departs and terminates at the same location. 2786 * 2787 * @return true if cars can return to staging 2788 */ 2789 public boolean isAllowReturnToStagingEnabled() { 2790 return _allowCarsReturnStaging; 2791 } 2792 2793 public void setAllowReturnToStagingEnabled(boolean enable) { 2794 boolean old = _allowCarsReturnStaging; 2795 _allowCarsReturnStaging = enable; 2796 if (old != enable) { 2797 setDirtyAndFirePropertyChange("allow cars to return to staging", old ? "true" : "false", // NOI18N 2798 enable ? "true" : "false"); // NOI18N 2799 } 2800 } 2801 2802 public boolean isServiceAllCarsWithFinalDestinationsEnabled() { 2803 return _serviceAllCarsWithFinalDestinations; 2804 } 2805 2806 public void setServiceAllCarsWithFinalDestinationsEnabled(boolean enable) { 2807 boolean old = _serviceAllCarsWithFinalDestinations; 2808 _serviceAllCarsWithFinalDestinations = enable; 2809 if (old != enable) { 2810 setDirtyAndFirePropertyChange("TrainServiceAllCarsWithFinalDestinations", old ? "true" : "false", // NOI18N 2811 enable ? "true" : "false"); // NOI18N 2812 } 2813 } 2814 2815 public boolean isBuildConsistEnabled() { 2816 return _buildConsist; 2817 } 2818 2819 public void setBuildConsistEnabled(boolean enable) { 2820 boolean old = _buildConsist; 2821 _buildConsist = enable; 2822 if (old != enable) { 2823 setDirtyAndFirePropertyChange("TrainBuildConsist", old ? "true" : "false", // NOI18N 2824 enable ? "true" : "false"); // NOI18N 2825 } 2826 } 2827 2828 public boolean isSendCarsWithCustomLoadsToStagingEnabled() { 2829 return _sendCarsWithCustomLoadsToStaging; 2830 } 2831 2832 public void setSendCarsWithCustomLoadsToStagingEnabled(boolean enable) { 2833 boolean old = _sendCarsWithCustomLoadsToStaging; 2834 _sendCarsWithCustomLoadsToStaging = enable; 2835 if (old != enable) { 2836 setDirtyAndFirePropertyChange("SendCarsWithCustomLoadsToStaging", old ? "true" : "false", // NOI18N 2837 enable ? "true" : "false"); // NOI18N 2838 } 2839 } 2840 2841 protected void setBuilt(boolean built) { 2842 boolean old = _built; 2843 _built = built; 2844 if (old != built) { 2845 setDirtyAndFirePropertyChange(BUILT_CHANGED_PROPERTY, old, built); // NOI18N 2846 } 2847 } 2848 2849 /** 2850 * Used to determine if this train has been built. 2851 * 2852 * @return true if the train was successfully built. 2853 */ 2854 public boolean isBuilt() { 2855 return _built; 2856 } 2857 2858 /** 2859 * Set true whenever the train's manifest has been modified. For example adding 2860 * or removing a car from a train, or changing the manifest format. Once the 2861 * manifest has been regenerated (modified == false), the old status for the 2862 * train is restored. 2863 * 2864 * @param modified True if train's manifest has been modified. 2865 */ 2866 public void setModified(boolean modified) { 2867 log.debug("Set modified {}", modified); 2868 if (!isBuilt()) { 2869 _modified = false; 2870 return; // there isn't a manifest to modify 2871 } 2872 boolean old = _modified; 2873 _modified = modified; 2874 if (modified) { 2875 setPrinted(false); 2876 } 2877 if (old != modified) { 2878 if (modified) { 2879 // scripts can call setModified() for a train 2880 if (getStatusCode() != CODE_RUN_SCRIPTS) { 2881 setOldStatusCode(getStatusCode()); 2882 } 2883 setStatusCode(CODE_MANIFEST_MODIFIED); 2884 } else { 2885 setStatusCode(getOldStatusCode()); // restore previous train 2886 // status 2887 } 2888 } 2889 setDirtyAndFirePropertyChange(TRAIN_MODIFIED_CHANGED_PROPERTY, null, modified); // NOI18N 2890 } 2891 2892 public boolean isModified() { 2893 return _modified; 2894 } 2895 2896 /** 2897 * Control flag used to decide if this train is to be built. 2898 * 2899 * @param build When true, build this train. 2900 */ 2901 public void setBuildEnabled(boolean build) { 2902 boolean old = _build; 2903 _build = build; 2904 if (old != build) { 2905 setDirtyAndFirePropertyChange(BUILD_CHANGED_PROPERTY, old, build); // NOI18N 2906 } 2907 } 2908 2909 /** 2910 * Used to determine if train is to be built. 2911 * 2912 * @return true if train is to be built. 2913 */ 2914 public boolean isBuildEnabled() { 2915 return _build; 2916 } 2917 2918 /** 2919 * Build this train if the build control flag is true. 2920 * 2921 * @return True only if train is successfully built. 2922 */ 2923 public boolean buildIfSelected() { 2924 if (isBuildEnabled() && !isBuilt()) { 2925 return build(); 2926 } 2927 log.debug("Train ({}) not selected or already built, skipping build", getName()); 2928 return false; 2929 } 2930 2931 /** 2932 * Build this train. Creates a train manifest. 2933 * 2934 * @return True if build successful. 2935 */ 2936 public synchronized boolean build() { 2937 reset(); 2938 // check to see if any other trains are building 2939 while (InstanceManager.getDefault(TrainManager.class).isAnyTrainBuilding()) { 2940 try { 2941 wait(100); // 100 msec 2942 } catch (InterruptedException e) { 2943 // TODO Auto-generated catch block 2944 log.error("Thread unexpectedly interrupted", e); 2945 } 2946 } 2947 // run before build scripts 2948 runScripts(getBuildScripts()); 2949 TrainBuilder tb = new TrainBuilder(); 2950 boolean results = tb.build(this); 2951 // run after build scripts 2952 runScripts(getAfterBuildScripts()); 2953 return results; 2954 } 2955 2956 /** 2957 * Run train scripts, waits for completion before returning. 2958 */ 2959 private synchronized void runScripts(List<String> scripts) { 2960 if (scripts.size() > 0) { 2961 // save the current status 2962 setOldStatusCode(getStatusCode()); 2963 setStatusCode(CODE_RUN_SCRIPTS); 2964 // create the python interpreter thread 2965 JmriScriptEngineManager.getDefault().initializeAllEngines(); 2966 // find the number of active threads 2967 ThreadGroup root = Thread.currentThread().getThreadGroup(); 2968 int numberOfThreads = root.activeCount(); 2969 // log.debug("Number of active threads: {}", numberOfThreads); 2970 for (String scriptPathname : scripts) { 2971 try { 2972 JmriScriptEngineManager.getDefault() 2973 .runScript(new File(jmri.util.FileUtil.getExternalFilename(scriptPathname))); 2974 } catch (Exception e) { 2975 log.error("Problem with script: {}", scriptPathname); 2976 } 2977 } 2978 // need to wait for scripts to complete or 4 seconds maximum 2979 int count = 0; 2980 while (root.activeCount() > numberOfThreads) { 2981 log.debug("Number of active threads: {}, at start: {}", root.activeCount(), numberOfThreads); 2982 try { 2983 wait(40); 2984 } catch (InterruptedException e) { 2985 Thread.currentThread().interrupt(); 2986 } 2987 if (count++ > 100) { 2988 break; // 4 seconds maximum 40*100 = 4000 2989 } 2990 } 2991 setStatusCode(getOldStatusCode()); 2992 } 2993 } 2994 2995 public boolean printBuildReport() { 2996 boolean isPreview = (InstanceManager.getDefault(TrainManager.class).isPrintPreviewEnabled() || 2997 Setup.isBuildReportAlwaysPreviewEnabled()); 2998 return printBuildReport(isPreview); 2999 } 3000 3001 public boolean printBuildReport(boolean isPreview) { 3002 File buildFile = InstanceManager.getDefault(TrainManagerXml.class).getTrainBuildReportFile(getName()); 3003 if (!buildFile.exists()) { 3004 log.warn("Build file missing for train {}", getName()); 3005 return false; 3006 } 3007 3008 if (isPreview && Setup.isBuildReportEditorEnabled()) { 3009 TrainPrintUtilities.editReport(buildFile, getName()); 3010 } else { 3011 TrainPrintUtilities.printReport(buildFile, 3012 Bundle.getMessage("buildReport", getDescription()), 3013 isPreview, NONE, true, NONE, NONE, Setup.PORTRAIT, Setup.getBuildReportFontSize(), true); 3014 } 3015 return true; 3016 } 3017 3018 protected void setBuildFailed(boolean status) { 3019 boolean old = _buildFailed; 3020 _buildFailed = status; 3021 if (old != status) { 3022 setDirtyAndFirePropertyChange("buildFailed", old ? "true" : "false", status ? "true" : "false"); // NOI18N 3023 } 3024 } 3025 3026 /** 3027 * Returns true if the train build failed. Note that returning false doesn't 3028 * mean the build was successful. 3029 * 3030 * @return true if train build failed. 3031 */ 3032 public boolean isBuildFailed() { 3033 return _buildFailed; 3034 } 3035 3036 protected void setBuildFailedMessage(String message) { 3037 String old = _buildFailedMessage; 3038 _buildFailedMessage = message; 3039 if (!old.equals(message)) { 3040 setDirtyAndFirePropertyChange("buildFailedMessage", old, message); // NOI18N 3041 } 3042 } 3043 3044 protected String getBuildFailedMessage() { 3045 return _buildFailedMessage; 3046 } 3047 3048 /** 3049 * Print manifest for train if already built. 3050 * 3051 * @return true if print successful. 3052 */ 3053 public boolean printManifestIfBuilt() { 3054 if (isBuilt()) { 3055 boolean isPreview = InstanceManager.getDefault(TrainManager.class).isPrintPreviewEnabled(); 3056 return (printManifest(isPreview)); 3057 } else { 3058 log.debug("Need to build train ({}) before printing manifest", getName()); 3059 return false; 3060 } 3061 } 3062 3063 /** 3064 * Print manifest for train. 3065 * 3066 * @param isPreview True if preview. 3067 * @return true if print successful, false if train print file not found. 3068 */ 3069 public boolean printManifest(boolean isPreview) { 3070 if (isModified()) { 3071 new TrainManifest(this); 3072 try { 3073 new JsonManifest(this).build(); 3074 } catch (IOException ex) { 3075 log.error("Unable to create JSON manifest {}", ex.getLocalizedMessage()); 3076 } 3077 new TrainCsvManifest(this); 3078 } 3079 File file = InstanceManager.getDefault(TrainManagerXml.class).getTrainManifestFile(getName()); 3080 if (!file.exists()) { 3081 log.warn("Manifest file missing for train ({})", getName()); 3082 return false; 3083 } 3084 if (isPreview && Setup.isManifestEditorEnabled()) { 3085 TrainUtilities.openDesktop(file); 3086 return true; 3087 } 3088 String logoURL = Setup.NONE; 3089 if (!getManifestLogoPathName().equals(NONE)) { 3090 logoURL = FileUtil.getExternalFilename(getManifestLogoPathName()); 3091 } else if (!Setup.getManifestLogoURL().equals(Setup.NONE)) { 3092 logoURL = FileUtil.getExternalFilename(Setup.getManifestLogoURL()); 3093 } 3094 Location departs = InstanceManager.getDefault(LocationManager.class).getLocationByName(getTrainDepartsName()); 3095 String printerName = Location.NONE; 3096 if (departs != null) { 3097 printerName = departs.getDefaultPrinterName(); 3098 } 3099 // the train description shouldn't exceed half of the page width or the 3100 // page number will be overwritten 3101 String name = getDescription(); 3102 if (name.length() > TrainCommon.getManifestHeaderLineLength() / 2) { 3103 name = name.substring(0, TrainCommon.getManifestHeaderLineLength() / 2); 3104 } 3105 TrainPrintUtilities.printReport(file, name, isPreview, Setup.getFontName(), false, logoURL, printerName, 3106 Setup.getManifestOrientation(), Setup.getManifestFontSize(), Setup.isPrintPageHeaderEnabled()); 3107 if (!isPreview) { 3108 setPrinted(true); 3109 } 3110 return true; 3111 } 3112 3113 public boolean openFile() { 3114 File file = createCsvManifestFile(); 3115 if (file == null || !file.exists()) { 3116 log.warn("CSV manifest file missing for train {}", getName()); 3117 return false; 3118 } 3119 TrainUtilities.openDesktop(file); 3120 return true; 3121 } 3122 3123 public boolean runFile() { 3124 File file = createCsvManifestFile(); 3125 if (file == null || !file.exists()) { 3126 log.warn("CSV manifest file missing for train {}", getName()); 3127 return false; 3128 } 3129 // Set up to process the CSV file by the external Manifest program 3130 InstanceManager.getDefault(TrainCustomManifest.class).addCsvFile(file); 3131 if (!InstanceManager.getDefault(TrainCustomManifest.class).process()) { 3132 if (!InstanceManager.getDefault(TrainCustomManifest.class).excelFileExists()) { 3133 JmriJOptionPane.showMessageDialog(null, 3134 Bundle.getMessage("LoadDirectoryNameFileName", 3135 InstanceManager.getDefault(TrainCustomManifest.class).getDirectoryPathName(), 3136 InstanceManager.getDefault(TrainCustomManifest.class).getFileName()), 3137 Bundle.getMessage("ManifestCreatorNotFound"), JmriJOptionPane.ERROR_MESSAGE); 3138 } 3139 return false; 3140 } 3141 return true; 3142 } 3143 3144 public File createCsvManifestFile() { 3145 if (isModified()) { 3146 new TrainManifest(this); 3147 try { 3148 new JsonManifest(this).build(); 3149 } catch (IOException ex) { 3150 log.error("Unable to create JSON manifest {}", ex.getLocalizedMessage()); 3151 } 3152 new TrainCsvManifest(this); 3153 } 3154 File file = InstanceManager.getDefault(TrainManagerXml.class).getTrainCsvManifestFile(getName()); 3155 if (!file.exists()) { 3156 log.warn("CSV manifest file was not created for train ({})", getName()); 3157 return null; 3158 } 3159 return file; 3160 } 3161 3162 public void setPrinted(boolean printed) { 3163 boolean old = _printed; 3164 _printed = printed; 3165 if (old != printed) { 3166 setDirtyAndFirePropertyChange("trainPrinted", old ? "true" : "false", printed ? "true" : "false"); // NOI18N 3167 } 3168 } 3169 3170 /** 3171 * Used to determine if train manifest was printed. 3172 * 3173 * @return true if the train manifest was printed. 3174 */ 3175 public boolean isPrinted() { 3176 return _printed; 3177 } 3178 3179 /** 3180 * Sets the panel position for the train icon for the current route location. 3181 * 3182 * @return true if train coordinates can be set 3183 */ 3184 public boolean setTrainIconCoordinates() { 3185 if (Setup.isTrainIconCordEnabled() && getCurrentRouteLocation() != null && _trainIcon != null) { 3186 getCurrentRouteLocation().setTrainIconX(_trainIcon.getX()); 3187 getCurrentRouteLocation().setTrainIconY(_trainIcon.getY()); 3188 return true; 3189 } 3190 return false; 3191 } 3192 3193 /** 3194 * Terminate train. 3195 */ 3196 public void terminate() { 3197 while (isBuilt()) { 3198 move(); 3199 } 3200 } 3201 3202 /** 3203 * Move train to next location in the route. Will move engines, cars, and train 3204 * icon. Will also terminate a train after it arrives at its final destination. 3205 */ 3206 public void move() { 3207 log.debug("Move train ({})", getName()); 3208 if (getRoute() == null || getCurrentRouteLocation() == null) { 3209 setBuilt(false); // break terminate loop 3210 return; 3211 } 3212 if (!isBuilt()) { 3213 log.error("ERROR attempt to move train ({}) that hasn't been built", getName()); 3214 return; 3215 } 3216 RouteLocation rl = getCurrentRouteLocation(); 3217 RouteLocation rlNext = getNextRouteLocation(rl); 3218 3219 setCurrentLocation(rlNext); 3220 3221 // cars and engines will move via property change 3222 setDirtyAndFirePropertyChange(TRAIN_LOCATION_CHANGED_PROPERTY, rl, rlNext); 3223 moveTrainIcon(rlNext); 3224 updateStatus(rl, rlNext); 3225 // tell GUI that train has complete its move 3226 setDirtyAndFirePropertyChange(TRAIN_MOVE_COMPLETE_CHANGED_PROPERTY, rl, rlNext); 3227 } 3228 3229 /** 3230 * Move train to a location in the train's route. Code checks to see if the 3231 * location requested is part of the train's route and if the train hasn't 3232 * already visited the location. This command can only move the train forward in 3233 * its route. Note that you can not terminate the train using this command. See 3234 * move() or terminate(). 3235 * 3236 * @param locationName The name of the location to move this train. 3237 * @return true if train was able to move to the named location. 3238 */ 3239 public boolean move(String locationName) { 3240 log.info("Move train ({}) to location ({})", getName(), locationName); 3241 if (getRoute() == null || getCurrentRouteLocation() == null) { 3242 return false; 3243 } 3244 List<RouteLocation> routeList = getRoute().getLocationsBySequenceList(); 3245 for (int i = 0; i < routeList.size(); i++) { 3246 RouteLocation rl = routeList.get(i); 3247 if (getCurrentRouteLocation() == rl) { 3248 for (int j = i + 1; j < routeList.size(); j++) { 3249 rl = routeList.get(j); 3250 if (rl.getName().equals(locationName)) { 3251 log.debug("Found location ({}) moving train to this location", locationName); 3252 for (j = i + 1; j < routeList.size(); j++) { 3253 rl = routeList.get(j); 3254 move(); 3255 if (rl.getName().equals(locationName)) { 3256 return true; 3257 } 3258 } 3259 } 3260 } 3261 break; // done 3262 } 3263 } 3264 return false; 3265 } 3266 3267 /** 3268 * Moves the train to the specified route location 3269 * 3270 * @param rl route location 3271 * @return true if successful 3272 */ 3273 public boolean move(RouteLocation rl) { 3274 if (rl == null) { 3275 return false; 3276 } 3277 log.debug("Move train ({}) to location ({})", getName(), rl.getName()); 3278 if (getRoute() == null || getCurrentRouteLocation() == null) { 3279 return false; 3280 } 3281 boolean foundCurrent = false; 3282 for (RouteLocation xrl : getRoute().getLocationsBySequenceList()) { 3283 if (getCurrentRouteLocation() == xrl) { 3284 foundCurrent = true; 3285 } 3286 if (xrl == rl) { 3287 if (foundCurrent) { 3288 return true; // done 3289 } else { 3290 break; // train passed this location 3291 } 3292 } 3293 if (foundCurrent) { 3294 move(); 3295 } 3296 } 3297 return false; 3298 } 3299 3300 /** 3301 * Move train to the next location in the train's route. The location name 3302 * provided must be equal to the next location name in the train's route. 3303 * 3304 * @param locationName The next location name in the train's route. 3305 * @return true if successful. 3306 */ 3307 public boolean moveToNextLocation(String locationName) { 3308 if (getNextLocationName().equals(locationName)) { 3309 move(); 3310 return true; 3311 } 3312 return false; 3313 } 3314 3315 public void loadTrainIcon() { 3316 if (getCurrentRouteLocation() != null) { 3317 moveTrainIcon(getCurrentRouteLocation()); 3318 } 3319 } 3320 3321 private final boolean animation = true; // when true use animation for icon 3322 // moves 3323 TrainIconAnimation _ta; 3324 3325 /* 3326 * The train icon is moved to route location (rl) for this train 3327 */ 3328 protected void moveTrainIcon(RouteLocation rl) { 3329 // create train icon if at departure, if program has been restarted, or removed 3330 if (rl == getTrainDepartsRouteLocation() || _trainIcon == null || !_trainIcon.isActive()) { 3331 createTrainIcon(rl); 3332 } 3333 // is the lead engine still in train 3334 if (getLeadEngine() != null && getLeadEngine().getRouteDestination() == rl && rl != null) { 3335 log.debug("Engine ({}) arriving at destination {}", getLeadEngine().toString(), rl.getName()); 3336 } 3337 if (_trainIcon != null && _trainIcon.isActive()) { 3338 setTrainIconColor(); 3339 _trainIcon.setShowToolTip(true); 3340 String txt = null; 3341 if (getCurrentLocationName().equals(NONE)) { 3342 txt = getDescription() + " " + Bundle.getMessage("Terminated") + " (" + getTrainTerminatesName() + ")"; 3343 } else { 3344 txt = Bundle.getMessage("TrainAtNext", 3345 getDescription(), getCurrentLocationName(), getNextLocationName(), getTrainLength(), 3346 Setup.getLengthUnit().toLowerCase()); 3347 } 3348 _trainIcon.getToolTip().setText(txt); 3349 _trainIcon.getToolTip().setBackgroundColor(Color.white); 3350 // rl can be null when train is terminated. 3351 if (rl != null) { 3352 if (rl.getTrainIconX() != 0 || rl.getTrainIconY() != 0) { 3353 if (animation) { 3354 TrainIconAnimation ta = new TrainIconAnimation(_trainIcon, rl, _ta); 3355 ta.start(); // start the animation 3356 _ta = ta; 3357 } else { 3358 _trainIcon.setLocation(rl.getTrainIconX(), rl.getTrainIconY()); 3359 } 3360 } 3361 } 3362 } 3363 } 3364 3365 public String getIconName() { 3366 String name = getName(); 3367 if (isBuilt() && getLeadEngine() != null && Setup.isTrainIconAppendEnabled()) { 3368 name += " " + getLeadEngine().getNumber(); 3369 } 3370 return name; 3371 } 3372 3373 public String getLeadEngineNumber() { 3374 if (getLeadEngine() == null) { 3375 return NONE; 3376 } 3377 return getLeadEngine().getNumber(); 3378 } 3379 3380 public String getLeadEngineRoadName() { 3381 if (getLeadEngine() == null) { 3382 return NONE; 3383 } 3384 return getLeadEngine().getRoadName(); 3385 } 3386 3387 public String getLeadEngineRoadAndNumber() { 3388 if (getLeadEngine() == null) { 3389 return NONE; 3390 } 3391 return getLeadEngine().toString(); 3392 } 3393 3394 public String getLeadEngineDccAddress() { 3395 if (getLeadEngine() == null) { 3396 return NONE; 3397 } 3398 return getLeadEngine().getDccAddress(); 3399 } 3400 3401 /** 3402 * Gets the lead engine, will create it if the program has been restarted 3403 * 3404 * @return lead engine for this train 3405 */ 3406 public Engine getLeadEngine() { 3407 if (_leadEngine == null && !_leadEngineId.equals(NONE)) { 3408 _leadEngine = InstanceManager.getDefault(EngineManager.class).getById(_leadEngineId); 3409 } 3410 return _leadEngine; 3411 } 3412 3413 public void setLeadEngine(Engine engine) { 3414 if (engine == null) { 3415 _leadEngineId = NONE; 3416 } 3417 _leadEngine = engine; 3418 } 3419 3420 /** 3421 * Returns the lead engine in a train's route. There can be up to two changes in 3422 * the lead engine for a train. 3423 * 3424 * @param routeLocation where in the train's route to find the lead engine. 3425 * @return lead engine 3426 */ 3427 public Engine getLeadEngine(RouteLocation routeLocation) { 3428 Engine lead = null; 3429 for (RouteLocation rl : getRoute().getLocationsBySequenceList()) { 3430 for (Engine engine : InstanceManager.getDefault(EngineManager.class).getByTrainList(this)) { 3431 if (engine.getRouteLocation() == rl && (engine.getConsist() == null || engine.isLead())) { 3432 lead = engine; 3433 break; 3434 } 3435 } 3436 if (rl == routeLocation) { 3437 break; 3438 } 3439 } 3440 return lead; 3441 } 3442 3443 protected TrainIcon _trainIcon = null; 3444 3445 public TrainIcon getTrainIcon() { 3446 return _trainIcon; 3447 } 3448 3449 public void createTrainIcon(RouteLocation rl) { 3450 if (_trainIcon != null && _trainIcon.isActive()) { 3451 _trainIcon.remove(); 3452 } 3453 // if there's a panel specified, get it and place icon 3454 if (!Setup.getPanelName().isEmpty()) { 3455 Editor editor = InstanceManager.getDefault(EditorManager.class).getTargetFrame(Setup.getPanelName()); 3456 if (editor != null) { 3457 try { 3458 _trainIcon = editor.addTrainIcon(getIconName()); 3459 } catch (Exception e) { 3460 log.error("Error placing train ({}) icon on panel ({})", getName(), Setup.getPanelName(), e); 3461 return; 3462 } 3463 _trainIcon.setTrain(this); 3464 if (getIconName().length() > 9) { 3465 _trainIcon.setFont(_trainIcon.getFont().deriveFont(8.f)); 3466 } 3467 if (rl != null) { 3468 _trainIcon.setLocation(rl.getTrainIconX(), rl.getTrainIconY()); 3469 } 3470 // add throttle if there's a throttle manager 3471 if (jmri.InstanceManager.getNullableDefault(jmri.ThrottleManager.class) != null) { 3472 // add throttle if JMRI loco roster entry exist 3473 RosterEntry entry = null; 3474 if (getLeadEngine() != null) { 3475 // first try and find a match based on loco road number 3476 entry = getLeadEngine().getRosterEntry(); 3477 } 3478 if (entry != null) { 3479 _trainIcon.setRosterEntry(entry); 3480 if (getLeadEngine().getConsist() != null) { 3481 _trainIcon.setConsistNumber(getLeadEngine().getConsist().getConsistNumber()); 3482 } 3483 } else { 3484 log.debug("Loco roster entry not found for train ({})", getName()); 3485 } 3486 } 3487 } 3488 } 3489 } 3490 3491 private void setTrainIconColor() { 3492 // Terminated train? 3493 if (getCurrentLocationName().equals(NONE)) { 3494 _trainIcon.setLocoColor(Setup.getTrainIconColorTerminate()); 3495 return; 3496 } 3497 // local train serving only one location? 3498 if (isLocalSwitcher()) { 3499 _trainIcon.setLocoColor(Setup.getTrainIconColorLocal()); 3500 return; 3501 } 3502 // set color based on train direction at current location 3503 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.NORTH) { 3504 _trainIcon.setLocoColor(Setup.getTrainIconColorNorth()); 3505 } 3506 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.SOUTH) { 3507 _trainIcon.setLocoColor(Setup.getTrainIconColorSouth()); 3508 } 3509 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.EAST) { 3510 _trainIcon.setLocoColor(Setup.getTrainIconColorEast()); 3511 } 3512 if (getCurrentRouteLocation().getTrainDirection() == RouteLocation.WEST) { 3513 _trainIcon.setLocoColor(Setup.getTrainIconColorWest()); 3514 } 3515 } 3516 3517 private void updateStatus(RouteLocation old, RouteLocation next) { 3518 if (next != null) { 3519 setStatusCode(CODE_TRAIN_EN_ROUTE); 3520 // run move scripts 3521 runScripts(getMoveScripts()); 3522 } else { 3523 log.debug("Train ({}) terminated", getName()); 3524 setTerminationDate(TrainCommon.getDate(false)); 3525 setStatusCode(CODE_TERMINATED); 3526 setBuilt(false); 3527 // run termination scripts 3528 runScripts(getTerminationScripts()); 3529 } 3530 } 3531 3532 /** 3533 * Sets the print status for switch lists 3534 * 3535 * @param status UNKNOWN PRINTED 3536 */ 3537 public void setSwitchListStatus(String status) { 3538 String old = _switchListStatus; 3539 _switchListStatus = status; 3540 if (!old.equals(status)) { 3541 setDirtyAndFirePropertyChange("switch list train status", old, status); // NOI18N 3542 } 3543 } 3544 3545 public String getSwitchListStatus() { 3546 return _switchListStatus; 3547 } 3548 3549 /** 3550 * Resets the train, removes engines and cars from this train. 3551 * 3552 * @return true if reset successful 3553 */ 3554 public boolean reset() { 3555 // is this train in route? 3556 if (isTrainEnRoute()) { 3557 log.info("Train ({}) has started its route, can not be reset", getName()); 3558 return false; 3559 } 3560 setCurrentLocation(null); 3561 setDepartureTrack(null); 3562 setTerminationTrack(null); 3563 setBuilt(false); 3564 setBuildFailed(false); 3565 setBuildFailedMessage(NONE); 3566 setPrinted(false); 3567 setModified(false); 3568 // remove cars and engines from this train via property change 3569 setStatusCode(CODE_TRAIN_RESET); 3570 // remove train icon 3571 if (_trainIcon != null && _trainIcon.isActive()) { 3572 _trainIcon.remove(); 3573 } 3574 return true; 3575 } 3576 3577 public void dispose() { 3578 if (getRoute() != null) { 3579 getRoute().removePropertyChangeListener(this); 3580 } 3581 InstanceManager.getDefault(CarRoads.class).removePropertyChangeListener(this); 3582 InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this); 3583 InstanceManager.getDefault(EngineTypes.class).removePropertyChangeListener(this); 3584 InstanceManager.getDefault(CarOwners.class).removePropertyChangeListener(this); 3585 InstanceManager.getDefault(EngineModels.class).removePropertyChangeListener(this); 3586 3587 setDirtyAndFirePropertyChange(DISPOSE_CHANGED_PROPERTY, null, "Dispose"); // NOI18N 3588 } 3589 3590 /** 3591 * Construct this Entry from XML. This member has to remain synchronized with 3592 * the detailed DTD in operations-trains.dtd 3593 * 3594 * @param e Consist XML element 3595 */ 3596 public Train(Element e) { 3597 org.jdom2.Attribute a; 3598 if ((a = e.getAttribute(Xml.ID)) != null) { 3599 _id = a.getValue(); 3600 } else { 3601 log.warn("no id attribute in train element when reading operations"); 3602 } 3603 if ((a = e.getAttribute(Xml.NAME)) != null) { 3604 _name = a.getValue(); 3605 } 3606 if ((a = e.getAttribute(Xml.DESCRIPTION)) != null) { 3607 _description = a.getValue(); 3608 } 3609 if ((a = e.getAttribute(Xml.DEPART_HOUR)) != null) { 3610 String hour = a.getValue(); 3611 if ((a = e.getAttribute(Xml.DEPART_MINUTE)) != null) { 3612 String minute = a.getValue(); 3613 _departureTime = hour + ":" + minute; 3614 } 3615 } 3616 3617 // Trains table row color 3618 Element eRowColor = e.getChild(Xml.ROW_COLOR); 3619 if (eRowColor != null && (a = eRowColor.getAttribute(Xml.NAME)) != null) { 3620 _tableRowColorName = a.getValue().toLowerCase(); 3621 } 3622 if (eRowColor != null && (a = eRowColor.getAttribute(Xml.RESET_ROW_COLOR)) != null) { 3623 _tableRowColorResetName = a.getValue().toLowerCase(); 3624 } 3625 3626 Element eRoute = e.getChild(Xml.ROUTE); 3627 if (eRoute != null) { 3628 if ((a = eRoute.getAttribute(Xml.ID)) != null) { 3629 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteById(a.getValue())); 3630 } 3631 if (eRoute.getChild(Xml.SKIPS) != null) { 3632 List<Element> skips = eRoute.getChild(Xml.SKIPS).getChildren(Xml.LOCATION); 3633 String[] locs = new String[skips.size()]; 3634 for (int i = 0; i < skips.size(); i++) { 3635 Element loc = skips.get(i); 3636 if ((a = loc.getAttribute(Xml.ID)) != null) { 3637 locs[i] = a.getValue(); 3638 } 3639 } 3640 setTrainSkipsLocations(locs); 3641 } 3642 } else { 3643 // old format 3644 // try and first get the route by id then by name 3645 if ((a = e.getAttribute(Xml.ROUTE_ID)) != null) { 3646 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteById(a.getValue())); 3647 } else if ((a = e.getAttribute(Xml.ROUTE)) != null) { 3648 setRoute(InstanceManager.getDefault(RouteManager.class).getRouteByName(a.getValue())); 3649 } 3650 if ((a = e.getAttribute(Xml.SKIP)) != null) { 3651 String locationIds = a.getValue(); 3652 String[] locs = locationIds.split("%%"); // NOI18N 3653 // log.debug("Train skips: {}", locationIds); 3654 setTrainSkipsLocations(locs); 3655 } 3656 } 3657 // new way of reading car types using elements 3658 if (e.getChild(Xml.TYPES) != null) { 3659 List<Element> carTypes = e.getChild(Xml.TYPES).getChildren(Xml.CAR_TYPE); 3660 String[] types = new String[carTypes.size()]; 3661 for (int i = 0; i < carTypes.size(); i++) { 3662 Element type = carTypes.get(i); 3663 if ((a = type.getAttribute(Xml.NAME)) != null) { 3664 types[i] = a.getValue(); 3665 } 3666 } 3667 setTypeNames(types); 3668 List<Element> locoTypes = e.getChild(Xml.TYPES).getChildren(Xml.LOCO_TYPE); 3669 types = new String[locoTypes.size()]; 3670 for (int i = 0; i < locoTypes.size(); i++) { 3671 Element type = locoTypes.get(i); 3672 if ((a = type.getAttribute(Xml.NAME)) != null) { 3673 types[i] = a.getValue(); 3674 } 3675 } 3676 setTypeNames(types); 3677 } // old way of reading car types up to version 2.99.6 3678 else if ((a = e.getAttribute(Xml.CAR_TYPES)) != null) { 3679 String names = a.getValue(); 3680 String[] types = names.split("%%"); // NOI18N 3681 // log.debug("Car types: {}", names); 3682 setTypeNames(types); 3683 } 3684 // old misspelled format 3685 if ((a = e.getAttribute(Xml.CAR_ROAD_OPERATION)) != null) { 3686 _carRoadOption = a.getValue(); 3687 } 3688 if ((a = e.getAttribute(Xml.CAR_ROAD_OPTION)) != null) { 3689 _carRoadOption = a.getValue(); 3690 } 3691 // new way of reading car roads using elements 3692 if (e.getChild(Xml.CAR_ROADS) != null) { 3693 List<Element> carRoads = e.getChild(Xml.CAR_ROADS).getChildren(Xml.CAR_ROAD); 3694 String[] roads = new String[carRoads.size()]; 3695 for (int i = 0; i < carRoads.size(); i++) { 3696 Element road = carRoads.get(i); 3697 if ((a = road.getAttribute(Xml.NAME)) != null) { 3698 roads[i] = a.getValue(); 3699 } 3700 } 3701 setCarRoadNames(roads); 3702 } // old way of reading car roads up to version 2.99.6 3703 else if ((a = e.getAttribute(Xml.CAR_ROADS)) != null) { 3704 String names = a.getValue(); 3705 String[] roads = names.split("%%"); // NOI18N 3706 log.debug("Train ({}) {} car roads: {}", getName(), getCarRoadOption(), names); 3707 setCarRoadNames(roads); 3708 } 3709 3710 if ((a = e.getAttribute(Xml.LOCO_ROAD_OPTION)) != null) { 3711 _locoRoadOption = a.getValue(); 3712 } 3713 // new way of reading engine roads using elements 3714 if (e.getChild(Xml.LOCO_ROADS) != null) { 3715 List<Element> locoRoads = e.getChild(Xml.LOCO_ROADS).getChildren(Xml.LOCO_ROAD); 3716 String[] roads = new String[locoRoads.size()]; 3717 for (int i = 0; i < locoRoads.size(); i++) { 3718 Element road = locoRoads.get(i); 3719 if ((a = road.getAttribute(Xml.NAME)) != null) { 3720 roads[i] = a.getValue(); 3721 } 3722 } 3723 setLocoRoadNames(roads); 3724 } 3725 3726 if ((a = e.getAttribute(Xml.CAR_LOAD_OPTION)) != null) { 3727 _loadOption = a.getValue(); 3728 } 3729 if ((a = e.getAttribute(Xml.CAR_OWNER_OPTION)) != null) { 3730 _ownerOption = a.getValue(); 3731 } 3732 if ((a = e.getAttribute(Xml.BUILT_START_YEAR)) != null) { 3733 _builtStartYear = a.getValue(); 3734 } 3735 if ((a = e.getAttribute(Xml.BUILT_END_YEAR)) != null) { 3736 _builtEndYear = a.getValue(); 3737 } 3738 // new way of reading car loads using elements 3739 if (e.getChild(Xml.CAR_LOADS) != null) { 3740 List<Element> carLoads = e.getChild(Xml.CAR_LOADS).getChildren(Xml.CAR_LOAD); 3741 String[] loads = new String[carLoads.size()]; 3742 for (int i = 0; i < carLoads.size(); i++) { 3743 Element load = carLoads.get(i); 3744 if ((a = load.getAttribute(Xml.NAME)) != null) { 3745 loads[i] = a.getValue(); 3746 } 3747 } 3748 setLoadNames(loads); 3749 } // old way of reading car loads up to version 2.99.6 3750 else if ((a = e.getAttribute(Xml.CAR_LOADS)) != null) { 3751 String names = a.getValue(); 3752 String[] loads = names.split("%%"); // NOI18N 3753 log.debug("Train ({}) {} car loads: {}", getName(), getLoadOption(), names); 3754 setLoadNames(loads); 3755 } 3756 // new way of reading car owners using elements 3757 if (e.getChild(Xml.CAR_OWNERS) != null) { 3758 List<Element> carOwners = e.getChild(Xml.CAR_OWNERS).getChildren(Xml.CAR_OWNER); 3759 String[] owners = new String[carOwners.size()]; 3760 for (int i = 0; i < carOwners.size(); i++) { 3761 Element owner = carOwners.get(i); 3762 if ((a = owner.getAttribute(Xml.NAME)) != null) { 3763 owners[i] = a.getValue(); 3764 } 3765 } 3766 setOwnerNames(owners); 3767 } // old way of reading car owners up to version 2.99.6 3768 else if ((a = e.getAttribute(Xml.CAR_OWNERS)) != null) { 3769 String names = a.getValue(); 3770 String[] owners = names.split("%%"); // NOI18N 3771 log.debug("Train ({}) {} car owners: {}", getName(), getOwnerOption(), names); 3772 setOwnerNames(owners); 3773 } 3774 3775 if ((a = e.getAttribute(Xml.NUMBER_ENGINES)) != null) { 3776 _numberEngines = a.getValue(); 3777 } 3778 if ((a = e.getAttribute(Xml.LEG2_ENGINES)) != null) { 3779 _leg2Engines = a.getValue(); 3780 } 3781 if ((a = e.getAttribute(Xml.LEG3_ENGINES)) != null) { 3782 _leg3Engines = a.getValue(); 3783 } 3784 if ((a = e.getAttribute(Xml.ENGINE_ROAD)) != null) { 3785 _engineRoad = a.getValue(); 3786 } 3787 if ((a = e.getAttribute(Xml.LEG2_ROAD)) != null) { 3788 _leg2Road = a.getValue(); 3789 } 3790 if ((a = e.getAttribute(Xml.LEG3_ROAD)) != null) { 3791 _leg3Road = a.getValue(); 3792 } 3793 if ((a = e.getAttribute(Xml.ENGINE_MODEL)) != null) { 3794 _engineModel = a.getValue(); 3795 } 3796 if ((a = e.getAttribute(Xml.LEG2_MODEL)) != null) { 3797 _leg2Model = a.getValue(); 3798 } 3799 if ((a = e.getAttribute(Xml.LEG3_MODEL)) != null) { 3800 _leg3Model = a.getValue(); 3801 } 3802 if ((a = e.getAttribute(Xml.REQUIRES)) != null) { 3803 try { 3804 _requires = Integer.parseInt(a.getValue()); 3805 } catch (NumberFormatException ee) { 3806 log.error("Requires ({}) isn't a valid number for train ({})", a.getValue(), getName()); 3807 } 3808 } 3809 if ((a = e.getAttribute(Xml.CABOOSE_ROAD)) != null) { 3810 _cabooseRoad = a.getValue(); 3811 } 3812 if ((a = e.getAttribute(Xml.LEG2_CABOOSE_ROAD)) != null) { 3813 _leg2CabooseRoad = a.getValue(); 3814 } 3815 if ((a = e.getAttribute(Xml.LEG3_CABOOSE_ROAD)) != null) { 3816 _leg3CabooseRoad = a.getValue(); 3817 } 3818 if ((a = e.getAttribute(Xml.LEG2_OPTIONS)) != null) { 3819 try { 3820 _leg2Options = Integer.parseInt(a.getValue()); 3821 } catch (NumberFormatException ee) { 3822 log.error("Leg 2 options ({}) isn't a valid number for train ({})", a.getValue(), getName()); 3823 } 3824 } 3825 if ((a = e.getAttribute(Xml.LEG3_OPTIONS)) != null) { 3826 try { 3827 _leg3Options = Integer.parseInt(a.getValue()); 3828 } catch (NumberFormatException ee) { 3829 log.error("Leg 3 options ({}) isn't a valid number for train ({})", a.getValue(), getName()); 3830 } 3831 } 3832 if ((a = e.getAttribute(Xml.BUILD_NORMAL)) != null) { 3833 _buildNormal = a.getValue().equals(Xml.TRUE); 3834 } 3835 if ((a = e.getAttribute(Xml.TO_TERMINAL)) != null) { 3836 _sendToTerminal = a.getValue().equals(Xml.TRUE); 3837 } 3838 if ((a = e.getAttribute(Xml.ALLOW_LOCAL_MOVES)) != null) { 3839 _allowLocalMoves = a.getValue().equals(Xml.TRUE); 3840 } 3841 if ((a = e.getAttribute(Xml.ALLOW_THROUGH_CARS)) != null) { 3842 _allowThroughCars = a.getValue().equals(Xml.TRUE); 3843 } 3844 if ((a = e.getAttribute(Xml.ALLOW_RETURN)) != null) { 3845 _allowCarsReturnStaging = a.getValue().equals(Xml.TRUE); 3846 } 3847 if ((a = e.getAttribute(Xml.SERVICE_ALL)) != null) { 3848 _serviceAllCarsWithFinalDestinations = a.getValue().equals(Xml.TRUE); 3849 } 3850 if ((a = e.getAttribute(Xml.BUILD_CONSIST)) != null) { 3851 _buildConsist = a.getValue().equals(Xml.TRUE); 3852 } 3853 if ((a = e.getAttribute(Xml.SEND_CUSTOM_STAGING)) != null) { 3854 _sendCarsWithCustomLoadsToStaging = a.getValue().equals(Xml.TRUE); 3855 } 3856 if ((a = e.getAttribute(Xml.BUILT)) != null) { 3857 _built = a.getValue().equals(Xml.TRUE); 3858 } 3859 if ((a = e.getAttribute(Xml.BUILD)) != null) { 3860 _build = a.getValue().equals(Xml.TRUE); 3861 } 3862 if ((a = e.getAttribute(Xml.BUILD_FAILED)) != null) { 3863 _buildFailed = a.getValue().equals(Xml.TRUE); 3864 } 3865 if ((a = e.getAttribute(Xml.BUILD_FAILED_MESSAGE)) != null) { 3866 _buildFailedMessage = a.getValue(); 3867 } 3868 if ((a = e.getAttribute(Xml.PRINTED)) != null) { 3869 _printed = a.getValue().equals(Xml.TRUE); 3870 } 3871 if ((a = e.getAttribute(Xml.MODIFIED)) != null) { 3872 _modified = a.getValue().equals(Xml.TRUE); 3873 } 3874 if ((a = e.getAttribute(Xml.SWITCH_LIST_STATUS)) != null) { 3875 _switchListStatus = a.getValue(); 3876 } 3877 if ((a = e.getAttribute(Xml.LEAD_ENGINE)) != null) { 3878 _leadEngineId = a.getValue(); 3879 } 3880 if ((a = e.getAttribute(Xml.TERMINATION_DATE)) != null) { 3881 _statusTerminatedDate = a.getValue(); 3882 } 3883 if ((a = e.getAttribute(Xml.REQUESTED_CARS)) != null) { 3884 try { 3885 _statusCarsRequested = Integer.parseInt(a.getValue()); 3886 } catch (NumberFormatException ee) { 3887 log.error("Status cars requested ({}) isn't a valid number for train ({})", a.getValue(), getName()); 3888 } 3889 } 3890 if ((a = e.getAttribute(Xml.STATUS_CODE)) != null) { 3891 try { 3892 _statusCode = Integer.parseInt(a.getValue()); 3893 } catch (NumberFormatException ee) { 3894 log.error("Status code ({}) isn't a valid number for train ({})", a.getValue(), getName()); 3895 } 3896 } else if ((a = e.getAttribute(Xml.STATUS)) != null) { 3897 // attempt to recover status code 3898 String status = a.getValue(); 3899 if (status.startsWith(BUILD_FAILED)) { 3900 _statusCode = CODE_BUILD_FAILED; 3901 } else if (status.startsWith(BUILT)) { 3902 _statusCode = CODE_BUILT; 3903 } else if (status.startsWith(PARTIAL_BUILT)) { 3904 _statusCode = CODE_PARTIAL_BUILT; 3905 } else if (status.startsWith(TERMINATED)) { 3906 String[] splitStatus = status.split(" "); 3907 if (splitStatus.length > 1) { 3908 _statusTerminatedDate = splitStatus[1]; 3909 } 3910 _statusCode = CODE_TERMINATED; 3911 } else if (status.startsWith(TRAIN_EN_ROUTE)) { 3912 _statusCode = CODE_TRAIN_EN_ROUTE; 3913 } else if (status.startsWith(TRAIN_RESET)) { 3914 _statusCode = CODE_TRAIN_RESET; 3915 } else { 3916 _statusCode = CODE_UNKNOWN; 3917 } 3918 } 3919 if ((a = e.getAttribute(Xml.OLD_STATUS_CODE)) != null) { 3920 try { 3921 _oldStatusCode = Integer.parseInt(a.getValue()); 3922 } catch (NumberFormatException ee) { 3923 log.error("Old status code ({}) isn't a valid number for train ({})", a.getValue(), getName()); 3924 } 3925 } else { 3926 _oldStatusCode = getStatusCode(); // use current status code if one 3927 // wasn't saved 3928 } 3929 if ((a = e.getAttribute(Xml.COMMENT)) != null) { 3930 _comment = a.getValue(); 3931 } 3932 if (getRoute() != null) { 3933 if ((a = e.getAttribute(Xml.CURRENT)) != null) { 3934 _current = getRoute().getLocationById(a.getValue()); 3935 } 3936 if ((a = e.getAttribute(Xml.LEG2_START)) != null) { 3937 _leg2Start = getRoute().getLocationById(a.getValue()); 3938 } 3939 if ((a = e.getAttribute(Xml.LEG3_START)) != null) { 3940 _leg3Start = getRoute().getLocationById(a.getValue()); 3941 } 3942 if ((a = e.getAttribute(Xml.LEG2_END)) != null) { 3943 _end2Leg = getRoute().getLocationById(a.getValue()); 3944 } 3945 if ((a = e.getAttribute(Xml.LEG3_END)) != null) { 3946 _leg3End = getRoute().getLocationById(a.getValue()); 3947 } 3948 if ((a = e.getAttribute(Xml.DEPARTURE_TRACK)) != null) { 3949 Location location = InstanceManager.getDefault(LocationManager.class) 3950 .getLocationByName(getTrainDepartsName()); 3951 if (location != null) { 3952 _departureTrack = location.getTrackById(a.getValue()); 3953 } else { 3954 log.error("Departure location not found for track {}", a.getValue()); 3955 } 3956 } 3957 if ((a = e.getAttribute(Xml.TERMINATION_TRACK)) != null) { 3958 Location location = InstanceManager.getDefault(LocationManager.class) 3959 .getLocationByName(getTrainTerminatesName()); 3960 if (location != null) { 3961 _terminationTrack = location.getTrackById(a.getValue()); 3962 } else { 3963 log.error("Termiation location not found for track {}", a.getValue()); 3964 } 3965 } 3966 } 3967 3968 // check for scripts 3969 if (e.getChild(Xml.SCRIPTS) != null) { 3970 List<Element> lb = e.getChild(Xml.SCRIPTS).getChildren(Xml.BUILD); 3971 for (Element es : lb) { 3972 if ((a = es.getAttribute(Xml.NAME)) != null) { 3973 addBuildScript(a.getValue()); 3974 } 3975 } 3976 List<Element> lab = e.getChild(Xml.SCRIPTS).getChildren(Xml.AFTER_BUILD); 3977 for (Element es : lab) { 3978 if ((a = es.getAttribute(Xml.NAME)) != null) { 3979 addAfterBuildScript(a.getValue()); 3980 } 3981 } 3982 List<Element> lm = e.getChild(Xml.SCRIPTS).getChildren(Xml.MOVE); 3983 for (Element es : lm) { 3984 if ((a = es.getAttribute(Xml.NAME)) != null) { 3985 addMoveScript(a.getValue()); 3986 } 3987 } 3988 List<Element> lt = e.getChild(Xml.SCRIPTS).getChildren(Xml.TERMINATE); 3989 for (Element es : lt) { 3990 if ((a = es.getAttribute(Xml.NAME)) != null) { 3991 addTerminationScript(a.getValue()); 3992 } 3993 } 3994 } 3995 // check for optional railroad name and logo 3996 if ((e.getChild(Xml.RAIL_ROAD) != null) && (a = e.getChild(Xml.RAIL_ROAD).getAttribute(Xml.NAME)) != null) { 3997 String name = a.getValue(); 3998 setRailroadName(name); 3999 } 4000 if ((e.getChild(Xml.MANIFEST_LOGO) != null)) { 4001 if ((a = e.getChild(Xml.MANIFEST_LOGO).getAttribute(Xml.NAME)) != null) { 4002 setManifestLogoPathName(a.getValue()); 4003 } 4004 } 4005 if ((a = e.getAttribute(Xml.SHOW_TIMES)) != null) { 4006 _showTimes = a.getValue().equals(Xml.TRUE); 4007 } 4008 4009 addPropertyChangeListerners(); 4010 } 4011 4012 private void addPropertyChangeListerners() { 4013 InstanceManager.getDefault(CarRoads.class).addPropertyChangeListener(this); 4014 InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this); 4015 InstanceManager.getDefault(EngineTypes.class).addPropertyChangeListener(this); 4016 InstanceManager.getDefault(CarOwners.class).addPropertyChangeListener(this); 4017 InstanceManager.getDefault(EngineModels.class).addPropertyChangeListener(this); 4018 } 4019 4020 /** 4021 * Create an XML element to represent this Entry. This member has to remain 4022 * synchronized with the detailed DTD in operations-trains.dtd. 4023 * 4024 * @return Contents in a JDOM Element 4025 */ 4026 public Element store() { 4027 Element e = new Element(Xml.TRAIN); 4028 e.setAttribute(Xml.ID, getId()); 4029 e.setAttribute(Xml.NAME, getName()); 4030 e.setAttribute(Xml.DESCRIPTION, getRawDescription()); 4031 e.setAttribute(Xml.DEPART_HOUR, getDepartureTimeHour()); 4032 e.setAttribute(Xml.DEPART_MINUTE, getDepartureTimeMinute()); 4033 4034 Element eRowColor = new Element(Xml.ROW_COLOR); 4035 eRowColor.setAttribute(Xml.NAME, getTableRowColorName()); 4036 eRowColor.setAttribute(Xml.RESET_ROW_COLOR, getTableRowColorNameReset()); 4037 e.addContent(eRowColor); 4038 4039 Element eRoute = new Element(Xml.ROUTE); 4040 if (getRoute() != null) { 4041 eRoute.setAttribute(Xml.NAME, getRoute().getName()); 4042 eRoute.setAttribute(Xml.ID, getRoute().getId()); 4043 e.addContent(eRoute); 4044 // build list of locations that this train skips 4045 String[] locationIds = getTrainSkipsLocations(); 4046 if (locationIds.length > 0) { 4047 Element eSkips = new Element(Xml.SKIPS); 4048 for (String id : locationIds) { 4049 Element eLoc = new Element(Xml.LOCATION); 4050 RouteLocation rl = getRoute().getLocationById(id); 4051 if (rl != null) { 4052 eLoc.setAttribute(Xml.NAME, rl.getName()); 4053 eLoc.setAttribute(Xml.ID, id); 4054 eSkips.addContent(eLoc); 4055 } 4056 } 4057 eRoute.addContent(eSkips); 4058 } 4059 } 4060 // build list of locations that this train skips 4061 if (getCurrentRouteLocation() != null) { 4062 e.setAttribute(Xml.CURRENT, getCurrentRouteLocation().getId()); 4063 } 4064 if (getDepartureTrack() != null) { 4065 e.setAttribute(Xml.DEPARTURE_TRACK, getDepartureTrack().getId()); 4066 } 4067 if (getTerminationTrack() != null) { 4068 e.setAttribute(Xml.TERMINATION_TRACK, getTerminationTrack().getId()); 4069 } 4070 e.setAttribute(Xml.BUILT_START_YEAR, getBuiltStartYear()); 4071 e.setAttribute(Xml.BUILT_END_YEAR, getBuiltEndYear()); 4072 e.setAttribute(Xml.NUMBER_ENGINES, getNumberEngines()); 4073 e.setAttribute(Xml.ENGINE_ROAD, getEngineRoad()); 4074 e.setAttribute(Xml.ENGINE_MODEL, getEngineModel()); 4075 e.setAttribute(Xml.REQUIRES, Integer.toString(getRequirements())); 4076 e.setAttribute(Xml.CABOOSE_ROAD, getCabooseRoad()); 4077 e.setAttribute(Xml.BUILD_NORMAL, isBuildTrainNormalEnabled() ? Xml.TRUE : Xml.FALSE); 4078 e.setAttribute(Xml.TO_TERMINAL, isSendCarsToTerminalEnabled() ? Xml.TRUE : Xml.FALSE); 4079 e.setAttribute(Xml.ALLOW_LOCAL_MOVES, isAllowLocalMovesEnabled() ? Xml.TRUE : Xml.FALSE); 4080 e.setAttribute(Xml.ALLOW_RETURN, isAllowReturnToStagingEnabled() ? Xml.TRUE : Xml.FALSE); 4081 e.setAttribute(Xml.ALLOW_THROUGH_CARS, isAllowThroughCarsEnabled() ? Xml.TRUE : Xml.FALSE); 4082 e.setAttribute(Xml.SERVICE_ALL, isServiceAllCarsWithFinalDestinationsEnabled() ? Xml.TRUE : Xml.FALSE); 4083 e.setAttribute(Xml.SEND_CUSTOM_STAGING, isSendCarsWithCustomLoadsToStagingEnabled() ? Xml.TRUE : Xml.FALSE); 4084 e.setAttribute(Xml.BUILD_CONSIST, isBuildConsistEnabled() ? Xml.TRUE : Xml.FALSE); 4085 e.setAttribute(Xml.BUILT, isBuilt() ? Xml.TRUE : Xml.FALSE); 4086 e.setAttribute(Xml.BUILD, isBuildEnabled() ? Xml.TRUE : Xml.FALSE); 4087 e.setAttribute(Xml.BUILD_FAILED, isBuildFailed() ? Xml.TRUE : Xml.FALSE); 4088 e.setAttribute(Xml.BUILD_FAILED_MESSAGE, getBuildFailedMessage()); 4089 e.setAttribute(Xml.PRINTED, isPrinted() ? Xml.TRUE : Xml.FALSE); 4090 e.setAttribute(Xml.MODIFIED, isModified() ? Xml.TRUE : Xml.FALSE); 4091 e.setAttribute(Xml.SWITCH_LIST_STATUS, getSwitchListStatus()); 4092 if (getLeadEngine() != null) { 4093 e.setAttribute(Xml.LEAD_ENGINE, getLeadEngine().getId()); 4094 } 4095 e.setAttribute(Xml.STATUS, getStatus()); 4096 e.setAttribute(Xml.TERMINATION_DATE, getTerminationDate()); 4097 e.setAttribute(Xml.REQUESTED_CARS, Integer.toString(getNumberCarsRequested())); 4098 e.setAttribute(Xml.STATUS_CODE, Integer.toString(getStatusCode())); 4099 e.setAttribute(Xml.OLD_STATUS_CODE, Integer.toString(getOldStatusCode())); 4100 e.setAttribute(Xml.COMMENT, getCommentWithColor()); 4101 e.setAttribute(Xml.SHOW_TIMES, isShowArrivalAndDepartureTimesEnabled() ? Xml.TRUE : Xml.FALSE); 4102 // build list of car types for this train 4103 String[] types = getTypeNames(); 4104 // new way of saving car types 4105 Element eTypes = new Element(Xml.TYPES); 4106 for (String type : types) { 4107 // don't save types that have been deleted by user 4108 if (InstanceManager.getDefault(EngineTypes.class).containsName(type)) { 4109 Element eType = new Element(Xml.LOCO_TYPE); 4110 eType.setAttribute(Xml.NAME, type); 4111 eTypes.addContent(eType); 4112 } else if (InstanceManager.getDefault(CarTypes.class).containsName(type)) { 4113 Element eType = new Element(Xml.CAR_TYPE); 4114 eType.setAttribute(Xml.NAME, type); 4115 eTypes.addContent(eType); 4116 } 4117 } 4118 e.addContent(eTypes); 4119 // save list of car roads for this train 4120 if (!getCarRoadOption().equals(ALL_ROADS)) { 4121 e.setAttribute(Xml.CAR_ROAD_OPTION, getCarRoadOption()); 4122 String[] roads = getCarRoadNames(); 4123 // new way of saving road names 4124 Element eRoads = new Element(Xml.CAR_ROADS); 4125 for (String road : roads) { 4126 Element eRoad = new Element(Xml.CAR_ROAD); 4127 eRoad.setAttribute(Xml.NAME, road); 4128 eRoads.addContent(eRoad); 4129 } 4130 e.addContent(eRoads); 4131 } 4132 // save list of engine roads for this train 4133 if (!getLocoRoadOption().equals(ALL_ROADS)) { 4134 e.setAttribute(Xml.LOCO_ROAD_OPTION, getLocoRoadOption()); 4135 String[] roads = getLocoRoadNames(); 4136 Element eRoads = new Element(Xml.LOCO_ROADS); 4137 for (String road : roads) { 4138 Element eRoad = new Element(Xml.LOCO_ROAD); 4139 eRoad.setAttribute(Xml.NAME, road); 4140 eRoads.addContent(eRoad); 4141 } 4142 e.addContent(eRoads); 4143 } 4144 // save list of car loads for this train 4145 if (!getLoadOption().equals(ALL_LOADS)) { 4146 e.setAttribute(Xml.CAR_LOAD_OPTION, getLoadOption()); 4147 String[] loads = getLoadNames(); 4148 // new way of saving car loads 4149 Element eLoads = new Element(Xml.CAR_LOADS); 4150 for (String load : loads) { 4151 Element eLoad = new Element(Xml.CAR_LOAD); 4152 eLoad.setAttribute(Xml.NAME, load); 4153 eLoads.addContent(eLoad); 4154 } 4155 e.addContent(eLoads); 4156 } 4157 // save list of car owners for this train 4158 if (!getOwnerOption().equals(ALL_OWNERS)) { 4159 e.setAttribute(Xml.CAR_OWNER_OPTION, getOwnerOption()); 4160 String[] owners = getOwnerNames(); 4161 // new way of saving car owners 4162 Element eOwners = new Element(Xml.CAR_OWNERS); 4163 for (String owner : owners) { 4164 Element eOwner = new Element(Xml.CAR_OWNER); 4165 eOwner.setAttribute(Xml.NAME, owner); 4166 eOwners.addContent(eOwner); 4167 } 4168 e.addContent(eOwners); 4169 } 4170 // save list of scripts for this train 4171 if (getBuildScripts().size() > 0 || 4172 getAfterBuildScripts().size() > 0 || 4173 getMoveScripts().size() > 0 || 4174 getTerminationScripts().size() > 0) { 4175 Element es = new Element(Xml.SCRIPTS); 4176 if (getBuildScripts().size() > 0) { 4177 for (String scriptPathname : getBuildScripts()) { 4178 Element em = new Element(Xml.BUILD); 4179 em.setAttribute(Xml.NAME, scriptPathname); 4180 es.addContent(em); 4181 } 4182 } 4183 if (getAfterBuildScripts().size() > 0) { 4184 for (String scriptPathname : getAfterBuildScripts()) { 4185 Element em = new Element(Xml.AFTER_BUILD); 4186 em.setAttribute(Xml.NAME, scriptPathname); 4187 es.addContent(em); 4188 } 4189 } 4190 if (getMoveScripts().size() > 0) { 4191 for (String scriptPathname : getMoveScripts()) { 4192 Element em = new Element(Xml.MOVE); 4193 em.setAttribute(Xml.NAME, scriptPathname); 4194 es.addContent(em); 4195 } 4196 } 4197 // save list of termination scripts for this train 4198 if (getTerminationScripts().size() > 0) { 4199 for (String scriptPathname : getTerminationScripts()) { 4200 Element et = new Element(Xml.TERMINATE); 4201 et.setAttribute(Xml.NAME, scriptPathname); 4202 es.addContent(et); 4203 } 4204 } 4205 e.addContent(es); 4206 } 4207 if (!getRailroadName().equals(NONE)) { 4208 Element r = new Element(Xml.RAIL_ROAD); 4209 r.setAttribute(Xml.NAME, getRailroadName()); 4210 e.addContent(r); 4211 } 4212 if (!getManifestLogoPathName().equals(NONE)) { 4213 Element l = new Element(Xml.MANIFEST_LOGO); 4214 l.setAttribute(Xml.NAME, getManifestLogoPathName()); 4215 e.addContent(l); 4216 } 4217 4218 if (getSecondLegOptions() != NO_CABOOSE_OR_FRED) { 4219 e.setAttribute(Xml.LEG2_OPTIONS, Integer.toString(getSecondLegOptions())); 4220 e.setAttribute(Xml.LEG2_ENGINES, getSecondLegNumberEngines()); 4221 e.setAttribute(Xml.LEG2_ROAD, getSecondLegEngineRoad()); 4222 e.setAttribute(Xml.LEG2_MODEL, getSecondLegEngineModel()); 4223 e.setAttribute(Xml.LEG2_CABOOSE_ROAD, getSecondLegCabooseRoad()); 4224 if (getSecondLegStartRouteLocation() != null) { 4225 e.setAttribute(Xml.LEG2_START, getSecondLegStartRouteLocation().getId()); 4226 } 4227 if (getSecondLegEndRouteLocation() != null) { 4228 e.setAttribute(Xml.LEG2_END, getSecondLegEndRouteLocation().getId()); 4229 } 4230 } 4231 if (getThirdLegOptions() != NO_CABOOSE_OR_FRED) { 4232 e.setAttribute(Xml.LEG3_OPTIONS, Integer.toString(getThirdLegOptions())); 4233 e.setAttribute(Xml.LEG3_ENGINES, getThirdLegNumberEngines()); 4234 e.setAttribute(Xml.LEG3_ROAD, getThirdLegEngineRoad()); 4235 e.setAttribute(Xml.LEG3_MODEL, getThirdLegEngineModel()); 4236 e.setAttribute(Xml.LEG3_CABOOSE_ROAD, getThirdLegCabooseRoad()); 4237 if (getThirdLegStartRouteLocation() != null) { 4238 e.setAttribute(Xml.LEG3_START, getThirdLegStartRouteLocation().getId()); 4239 } 4240 if (getThirdLegEndRouteLocation() != null) { 4241 e.setAttribute(Xml.LEG3_END, getThirdLegEndRouteLocation().getId()); 4242 } 4243 } 4244 return e; 4245 } 4246 4247 @Override 4248 public void propertyChange(java.beans.PropertyChangeEvent e) { 4249 if (Control.SHOW_PROPERTY) { 4250 log.debug("Train ({}) sees property change: ({}) old: ({}) new: ({})", getName(), e.getPropertyName(), 4251 e.getOldValue(), e.getNewValue()); 4252 } 4253 if (e.getPropertyName().equals(Route.DISPOSE)) { 4254 setRoute(null); 4255 } 4256 if (e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY) || 4257 e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) || 4258 e.getPropertyName().equals(EngineTypes.ENGINETYPES_NAME_CHANGED_PROPERTY)) { 4259 replaceType((String) e.getOldValue(), (String) e.getNewValue()); 4260 } 4261 if (e.getPropertyName().equals(CarRoads.CARROADS_NAME_CHANGED_PROPERTY)) { 4262 replaceRoad((String) e.getOldValue(), (String) e.getNewValue()); 4263 } 4264 if (e.getPropertyName().equals(CarOwners.CAROWNERS_NAME_CHANGED_PROPERTY)) { 4265 replaceOwner((String) e.getOldValue(), (String) e.getNewValue()); 4266 } 4267 if (e.getPropertyName().equals(EngineModels.ENGINEMODELS_NAME_CHANGED_PROPERTY)) { 4268 replaceModel((String) e.getOldValue(), (String) e.getNewValue()); 4269 } 4270 // forward route departure time property changes 4271 if (e.getPropertyName().equals(RouteLocation.DEPARTURE_TIME_CHANGED_PROPERTY)) { 4272 setDirtyAndFirePropertyChange(DEPARTURETIME_CHANGED_PROPERTY, e.getOldValue(), e.getNewValue()); 4273 } 4274 // forward any property changes in this train's route 4275 if (e.getSource().getClass().equals(Route.class)) { 4276 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 4277 } 4278 } 4279 4280 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 4281 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 4282 firePropertyChange(p, old, n); 4283 } 4284 4285 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Train.class); 4286 4287}