001package jmri.jmrit.operations.rollingstock; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.text.SimpleDateFormat; 006import java.util.Date; 007 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011import jmri.*; 012import jmri.beans.Identifiable; 013import jmri.beans.PropertyChangeSupport; 014import jmri.jmrit.operations.locations.*; 015import jmri.jmrit.operations.locations.divisions.Division; 016import jmri.jmrit.operations.locations.divisions.DivisionManager; 017import jmri.jmrit.operations.rollingstock.cars.*; 018import jmri.jmrit.operations.routes.RouteLocation; 019import jmri.jmrit.operations.setup.Setup; 020import jmri.jmrit.operations.trains.Train; 021import jmri.jmrit.operations.trains.TrainManager; 022import jmri.jmrit.operations.trains.trainbuilder.TrainCommon; 023 024/** 025 * Represents rolling stock, both powered (locomotives) and not powered (cars) 026 * on the layout. 027 * 028 * @author Daniel Boudreau Copyright (C) 2009, 2010, 2013, 2023 029 */ 030public abstract class RollingStock extends PropertyChangeSupport implements Identifiable, PropertyChangeListener { 031 032 public static final String NONE = ""; 033 public static final int DEFAULT_BLOCKING_ORDER = 0; 034 public static final int MAX_BLOCKING_ORDER = 100; 035 public static final boolean FORCE = true; // ignore length, type, etc. when setting car's track 036 protected static final String DEFAULT_WEIGHT = "0"; 037 038 public static final String CLONE = TrainCommon.HYPHEN + "(Clone)"; // NOI18N 039 // parentheses are special chars 040 public static final String CLONE_REGEX = TrainCommon.HYPHEN + "\\(Clone\\)"; // NOI18N 041 042 protected String _id = NONE; 043 protected String _number = NONE; 044 protected String _road = NONE; 045 protected String _type = NONE; 046 protected String _length = "0"; 047 protected String _color = NONE; 048 protected String _weight = DEFAULT_WEIGHT; 049 protected String _weightTons = DEFAULT_WEIGHT; 050 protected String _built = NONE; 051 protected String _owner = NONE; 052 protected String _comment = NONE; 053 protected String _routeId = NONE; // saved route for interchange tracks 054 protected String _rfid = NONE; 055 protected String _value = NONE; 056 protected Date _lastDate = null; 057 protected boolean _locationUnknown = false; 058 protected boolean _outOfService = false; 059 protected boolean _selected = false; 060 061 protected Location _location = null; 062 protected Track _track = null; 063 protected Location _destination = null; 064 protected Track _trackDestination = null; 065 protected Train _train = null; 066 protected RouteLocation _routeLocation = null; 067 protected RouteLocation _routeDestination = null; 068 protected Division _division = null; 069 protected boolean _clone = false; 070 protected int _cloneOrder = 9999999; 071 protected int _moves = 0; 072 protected String _lastLocationId = LOCATION_UNKNOWN; // the rollingstock's last location id 073 protected String _lastTrackId = LOCATION_UNKNOWN; // the rollingstock's last track id 074 protected Train _lastTrain = null; // the last train moving this rs 075 protected int _blocking = DEFAULT_BLOCKING_ORDER; 076 protected String _pickupTime = NONE; 077 protected String _setoutTime = NONE; 078 079 protected IdTag _tag = null; 080 protected PropertyChangeListener _tagListener = null; 081 protected Location _whereLastSeen = null; // location reported by tag reader 082 protected Date _whenLastSeen = null; // date reported by tag reader 083 084 public static final String LOCATION_UNKNOWN = "0"; 085 086 protected int number = 0; // used by rolling stock manager for sort by number 087 088 public static final String ERROR_TRACK = "ERROR wrong track for location"; // checks for coding error // NOI18N 089 090 // property changes 091 public static final String TRACK_CHANGED_PROPERTY = "rolling stock track location"; // NOI18N 092 public static final String DESTINATION_TRACK_CHANGED_PROPERTY = "rolling stock track destination"; // NOI18N 093 public static final String TRAIN_CHANGED_PROPERTY = "rolling stock train"; // NOI18N 094 public static final String LENGTH_CHANGED_PROPERTY = "rolling stock length"; // NOI18N 095 public static final String TYPE_CHANGED_PROPERTY = "rolling stock type"; // NOI18N 096 public static final String ROUTE_LOCATION_CHANGED_PROPERTY = "rolling stock route location"; // NOI18N 097 public static final String ROUTE_DESTINATION_CHANGED_PROPERTY = "rolling stock route destination"; // NOI18N 098 public static final String COMMENT_CHANGED_PROPERTY = "rolling stock comment"; // NOI18N 099 100 // the draw bar length must only be calculated once at startup 101 public static final int COUPLERS = Setup.getLengthUnit().equals(Setup.FEET) 102 ? Integer.parseInt(Bundle.getMessage("DrawBarLengthFeet")) 103 : Integer.parseInt(Bundle.getMessage("DrawBarLengthMeter")); // stocks TODO catch empty/non-integer value 104 105 LocationManager locationManager = InstanceManager.getDefault(LocationManager.class); 106 107 public RollingStock() { 108 _lastDate = (new java.util.GregorianCalendar()).getGregorianChange(); // set to change date of the Gregorian 109 // Calendar. 110 } 111 112 public RollingStock(String road, String number) { 113 this(); 114 log.debug("New rolling stock ({} {})", road, number); 115 _road = road; 116 _number = number; 117 _id = createId(road, number); 118 addPropertyChangeListeners(); 119 } 120 121 public static String createId(String road, String number) { 122 return road + number; 123 } 124 125 @Override 126 public String getId() { 127 return _id; 128 } 129 130 public RollingStock copy(RollingStock rs) { 131 rs.setBuilt(getBuilt()); 132 rs.setColor(getColor()); 133 rs.setLength(getLength()); 134 rs.setWeightTons(getWeightTons()); 135 rs.setNumber(getNumber()); 136 rs.setOwnerName(getOwnerName()); 137 rs.setRoadName(getRoadName()); 138 rs.setTypeName(getTypeName()); 139 rs.setComment(getComment()); 140 rs.setBlocking(getBlocking()); 141 rs.setLastTrain(getLastTrain()); 142 rs.setLastDate(getLastDate()); 143 rs.setLastLocationId(getLastLocationId()); 144 rs.setLastTrackId(getLastTrackId()); 145 rs.setDivision(getDivision()); 146 return rs; 147 } 148 149 /** 150 * Set the rolling stock identification or road number 151 * 152 * @param number The rolling stock road number. 153 * 154 */ 155 public void setNumber(String number) { 156 String oldNumber = _number; 157 _number = number; 158 if (!oldNumber.equals(number)) { 159 firePropertyChange("rolling stock number", oldNumber, number); // NOI18N 160 String oldId = _id; 161 _id = createId(_road, number); 162 setDirtyAndFirePropertyChange(Xml.ID, oldId, _id); 163 } 164 } 165 166 public String getNumber() { 167 return _number; 168 } 169 170 public void setRoadName(String road) { 171 String old = _road; 172 _road = road; 173 if (!old.equals(road)) { 174 firePropertyChange("rolling stock road", old, road); // NOI18N 175 String oldId = _id; 176 _id = createId(road, _number); 177 setDirtyAndFirePropertyChange(Xml.ID, oldId, _id); 178 } 179 } 180 181 public String getRoadName() { 182 return _road; 183 } 184 185 /** 186 * For combobox and identification 187 */ 188 @Override 189 public String toString() { 190 return getRoadName() + " " + getNumber(); 191 } 192 193 /** 194 * Sets the type of rolling stock. "Boxcar" for example is a type of car. 195 * 196 * @param type The type of rolling stock. 197 */ 198 public void setTypeName(String type) { 199 String old = _type; 200 _type = type; 201 if (!old.equals(type)) { 202 setDirtyAndFirePropertyChange("rolling stock type", old, type); // NOI18N 203 } 204 } 205 206 public String getTypeName() { 207 return _type; 208 } 209 210 protected boolean _lengthChange = false; // used for loco length change 211 212 /** 213 * Sets the body length of the rolling stock. For example, a 40' boxcar would be 214 * entered as 40 feet. Coupler length is added by the program when determining 215 * if a car could fit on a track. 216 * 217 * @see #getTotalLength() 218 * @param length the body length in feet or meters 219 */ 220 public void setLength(String length) { 221 String old = _length; 222 if (!old.equals(length)) { 223 // adjust used length if rolling stock is at a location 224 if (getLocation() != null && getTrack() != null) { 225 getLocation().setUsedLength(getLocation().getUsedLength() + Integer.parseInt(length) - Integer.parseInt(old)); 226 getTrack().setUsedLength(getTrack().getUsedLength() + Integer.parseInt(length) - Integer.parseInt(old)); 227 if (getDestination() != null && getDestinationTrack() != null && !_lengthChange) { 228 _lengthChange = true; // prevent recursive loop, and we want the "old" engine length 229 log.debug("Rolling stock ({}) has destination ({}, {})", this, getDestination().getName(), 230 getDestinationTrack().getName()); 231 getTrack().deletePickupRS(this); 232 getDestinationTrack().deleteDropRS(this); 233 // now change the length and update tracks 234 _length = length; 235 getTrack().addPickupRS(this); 236 getDestinationTrack().addDropRS(this); 237 _lengthChange = false; // done 238 } 239 } 240 _length = length; 241 setDirtyAndFirePropertyChange(LENGTH_CHANGED_PROPERTY, old, length); 242 } 243 } 244 245 /** 246 * gets the body length of the rolling stock 247 * 248 * @return length in feet or meters 249 * 250 * @see #getTotalLength() 251 */ 252 public String getLength() { 253 return _length; 254 } 255 256 public int getLengthInteger() { 257 try { 258 return Integer.parseInt(getLength()); 259 } catch (NumberFormatException e) { 260 log.error("Rolling stock ({}) length ({}) is not valid ", this, getLength()); 261 } 262 return 0; 263 } 264 265 /** 266 * Returns the length of the rolling stock including the couplers 267 * 268 * 269 * @return total length of the rolling stock in feet or meters 270 */ 271 public int getTotalLength() { 272 return getLengthInteger() + RollingStock.COUPLERS; 273 } 274 275 public void setColor(String color) { 276 String old = _color; 277 _color = color; 278 if (!old.equals(color)) { 279 setDirtyAndFirePropertyChange("rolling stock color", old, color); // NOI18N 280 } 281 } 282 283 public String getColor() { 284 return _color; 285 } 286 287 /** 288 * 289 * @param weight rolling stock weight in ounces. 290 */ 291 public void setWeight(String weight) { 292 String old = _weight; 293 _weight = weight; 294 if (!old.equals(weight)) { 295 setDirtyAndFirePropertyChange("rolling stock weight", old, weight); // NOI18N 296 } 297 } 298 299 public String getWeight() { 300 return _weight; 301 } 302 303 /** 304 * Sets the full scale weight in tons. 305 * 306 * @param weight full scale rolling stock weight in tons. 307 */ 308 public void setWeightTons(String weight) { 309 String old = _weightTons; 310 _weightTons = weight; 311 if (!old.equals(weight)) { 312 setDirtyAndFirePropertyChange("rolling stock weight tons", old, weight); // NOI18N 313 } 314 } 315 316 public String getWeightTons() { 317 if (!_weightTons.equals(DEFAULT_WEIGHT)) { 318 return _weightTons; 319 } 320 // calculate the ton weight based on actual weight 321 double weight = 0; 322 try { 323 weight = Double.parseDouble(getWeight()); 324 } catch (NumberFormatException e) { 325 log.trace("Weight not set for rolling stock ({})", this); 326 } 327 return Integer.toString((int) (weight * Setup.getScaleTonRatio())); 328 } 329 330 public int getAdjustedWeightTons() { 331 int weightTons = 0; 332 try { 333 // get loaded weight 334 weightTons = Integer.parseInt(getWeightTons()); 335 } catch (NumberFormatException e) { 336 log.debug("Rolling stock ({}) weight not set", this); 337 } 338 return weightTons; 339 } 340 341 /** 342 * Set the date that the rolling stock was built. Use 4 digits for the year, or 343 * the format MM-YY where MM is the two digit month, and YY is the last two 344 * years if the rolling stock was built in the 1900s. Use MM-YYYY for units 345 * build after 1999. 346 * 347 * @param built The string built date. 348 * 349 */ 350 public void setBuilt(String built) { 351 String old = _built; 352 _built = built; 353 if (!old.equals(built)) { 354 setDirtyAndFirePropertyChange("rolling stock built", old, built); // NOI18N 355 } 356 } 357 358 public String getBuilt() { 359 return _built; 360 } 361 362 /** 363 * 364 * @return location unknown symbol, out of service symbol, or none if car okay 365 */ 366 public String getStatus() { 367 return (isLocationUnknown() ? "<?> " : (isOutOfService() ? "<O> " : NONE)); // NOI18N 368 } 369 370 public Location getLocation() { 371 return _location; 372 } 373 374 /** 375 * Get rolling stock's location name 376 * 377 * @return empty string if rolling stock isn't on layout 378 */ 379 public String getLocationName() { 380 if (getLocation() != null) { 381 return getLocation().getName(); 382 } 383 return NONE; 384 } 385 386 public String getSplitLocationName() { 387 return TrainCommon.splitString(getLocationName()); 388 } 389 390 /** 391 * Get rolling stock's location id 392 * 393 * @return empty string if rolling stock isn't on the layout 394 */ 395 public String getLocationId() { 396 if (getLocation() != null) { 397 return getLocation().getId(); 398 } 399 return NONE; 400 } 401 402 public Track getTrack() { 403 return _track; 404 } 405 406 /** 407 * Set the rolling stock's location and track. Doesn't do any checking and does 408 * not fire a property change. Used exclusively by the Router code. Use 409 * setLocation(Location, Track) instead. 410 * 411 * @param track to place the rolling stock on. 412 */ 413 public void setTrack(Track track) { 414 if (track != null) { 415 _location = track.getLocation(); 416 } 417 _track = track; 418 } 419 420 /** 421 * Get rolling stock's track name 422 * 423 * @return empty string if rolling stock isn't on a track 424 */ 425 public String getTrackName() { 426 if (getTrack() != null) { 427 return getTrack().getName(); 428 } 429 return NONE; 430 } 431 432 public String getSplitTrackName() { 433 return TrainCommon.splitString(getTrackName()); 434 } 435 436 public String getTrackType() { 437 if (getTrack() != null) { 438 return getTrack().getTrackTypeName(); 439 } 440 return NONE; 441 } 442 443 /** 444 * Get rolling stock's track id 445 * 446 * @return empty string if rolling stock isn't on a track 447 */ 448 public String getTrackId() { 449 if (getTrack() != null) { 450 return getTrack().getId(); 451 } 452 return NONE; 453 } 454 455 /** 456 * Sets rolling stock location on the layout 457 * 458 * @param location The Location. 459 * @param track (yard, spur, staging, or interchange track) 460 * @return "okay" if successful, "type" if the rolling stock's type isn't 461 * acceptable, "road" if rolling stock road isn't acceptable, or 462 * "length" if the rolling stock length didn't fit. 463 */ 464 public String setLocation(Location location, Track track) { 465 return setLocation(location, track, !FORCE); // don't force 466 } 467 468 /** 469 * Sets rolling stock location on the layout 470 * 471 * @param location The Location. 472 * @param track (yard, spur, staging, or interchange track) 473 * @param force when true place rolling stock ignore track length, type, and 474 * road 475 * @return "okay" if successful, "type" if the rolling stock's type isn't 476 * acceptable, "road" if rolling stock road isn't acceptable, or 477 * "length" if the rolling stock length didn't fit. 478 */ 479 public String setLocation(Location location, Track track, boolean force) { 480 Location oldLocation = getLocation(); 481 Track oldTrack = getTrack(); 482 // first determine if rolling stock can be move to the new location 483 if (!force && (oldLocation != location || oldTrack != track)) { 484 String status = testLocation(location, track); 485 if (!status.equals(Track.OKAY)) { 486 return status; 487 } 488 } 489 // now update 490 _location = location; 491 _track = track; 492 493 if (oldLocation != location || oldTrack != track) { 494 // update rolling stock location on layout, maybe this should be a property 495 // change? 496 // first remove rolling stock from existing location 497 if (oldLocation != null) { 498 oldLocation.deleteRS(this); 499 oldLocation.removePropertyChangeListener(this); 500 // if track is null, then rolling stock is in a train 501 if (oldTrack != null) { 502 oldTrack.deleteRS(this); 503 oldTrack.removePropertyChangeListener(this); 504 // if there's a destination then pickup complete 505 if (getDestination() != null) { 506 oldLocation.deletePickupRS(); 507 oldTrack.deletePickupRS(this); 508 // don't update rs's previous location if just re-staging 509 if (!oldLocation.isStaging() || 510 location == null || 511 !location.isStaging() || 512 getTrain() != null && 513 getTrain().getRoute() != null && 514 getTrain().getRoute().size() > 2) { 515 setLastLocationId(oldLocation.getId()); 516 setLastTrackId(oldTrack.getId()); 517 } 518 } 519 } 520 } 521 if (getLocation() != null) { 522 getLocation().addRS(this); 523 // Need to know if location name changes so we can forward to listeners 524 getLocation().addPropertyChangeListener(this); 525 } 526 if (getTrack() != null) { 527 getTrack().addRS(this); 528 // Need to know if location name changes so we can forward to listeners 529 getTrack().addPropertyChangeListener(this); 530 // if there's a destination then there's a pick up 531 if (getDestination() != null) { 532 getLocation().addPickupRS(); 533 getTrack().addPickupRS(this); 534 } 535 } 536 setDirtyAndFirePropertyChange(TRACK_CHANGED_PROPERTY, oldTrack, track); 537 } 538 return Track.OKAY; 539 } 540 541 /** 542 * Used to confirm that a track is associated with a location, and that rolling 543 * stock can be placed on track. 544 * 545 * @param location The location 546 * @param track The track, can be null 547 * @return OKAY if track exists at location and rolling stock can be placed on 548 * track, ERROR_TRACK if track isn't at location, other if rolling stock 549 * can't be placed on track. 550 */ 551 public String testLocation(Location location, Track track) { 552 if (track == null) { 553 return Track.OKAY; 554 } 555 if (location != null && !location.isTrackAtLocation(track)) { 556 return ERROR_TRACK; 557 } 558 return track.isRollingStockAccepted(this); 559 } 560 561 /** 562 * Sets rolling stock destination on the layout 563 * 564 * @param destination The Location. 565 * 566 * @param track (yard, spur, staging, or interchange track) 567 * @return "okay" if successful, "type" if the rolling stock's type isn't 568 * acceptable, or "length" if the rolling stock length didn't fit. 569 */ 570 public String setDestination(Location destination, Track track) { 571 return setDestination(destination, track, !RollingStock.FORCE); 572 } 573 574 /** 575 * Sets rolling stock destination on the layout 576 * 577 * @param destination The Location. 578 * 579 * @param track (yard, spur, staging, or interchange track) 580 * @param force when true ignore track length, type, and road when setting 581 * destination 582 * @return "okay" if successful, "type" if the rolling stock's type isn't 583 * acceptable, or "length" if the rolling stock length didn't fit. 584 */ 585 public String setDestination(Location destination, Track track, boolean force) { 586 // first determine if rolling stock can be move to the new destination 587 if (!force) { 588 String status = rsCheckDestination(destination, track); 589 if (!status.equals(Track.OKAY)) { 590 return status; 591 } 592 } 593 // now set the rolling stock destination! 594 Location oldDestination = getDestination(); 595 _destination = destination; 596 Track oldTrack = getDestinationTrack(); 597 _trackDestination = track; 598 599 if (oldDestination != destination || oldTrack != track) { 600 if (oldDestination != null) { 601 oldDestination.deleteDropRS(); 602 oldDestination.removePropertyChangeListener(this); 603 // delete pick up in case destination is null 604 if (getLocation() != null && getTrack() != null) { 605 getLocation().deletePickupRS(); 606 getTrack().deletePickupRS(this); 607 } 608 } 609 if (oldTrack != null) { 610 oldTrack.deleteDropRS(this); 611 oldTrack.removePropertyChangeListener(this); 612 } 613 if (getDestination() != null) { 614 getDestination().addDropRS(); 615 if (getLocation() != null && getTrack() != null) { 616 getLocation().addPickupRS(); 617 getTrack().addPickupRS(this); 618 } 619 // Need to know if destination name changes so we can forward to listeners 620 getDestination().addPropertyChangeListener(this); 621 } 622 if (getDestinationTrack() != null) { 623 getDestinationTrack().addDropRS(this); 624 // Need to know if destination name changes so we can forward to listeners 625 getDestinationTrack().addPropertyChangeListener(this); 626 } else { 627 // rolling stock has been terminated or reset 628 if (getTrain() != null && getTrain().getRoute() != null) { 629 setLastRouteId(getTrain().getRoute().getId()); 630 } 631 setRouteLocation(null); 632 setRouteDestination(null); 633 } 634 setDirtyAndFirePropertyChange(DESTINATION_TRACK_CHANGED_PROPERTY, oldTrack, track); 635 } 636 return Track.OKAY; 637 } 638 639 /** 640 * Used to check destination track to see if it will accept rolling stock 641 * 642 * @param destination The Location. 643 * @param track The Track at destination. 644 * 645 * @return status OKAY, TYPE, ROAD, LENGTH, ERROR_TRACK 646 */ 647 public String checkDestination(Location destination, Track track) { 648 return rsCheckDestination(destination, track); 649 } 650 651 private String rsCheckDestination(Location destination, Track track) { 652 // first perform a code check 653 if (destination != null && !destination.isTrackAtLocation(track)) { 654 return ERROR_TRACK; 655 } 656 if (destination != null && !destination.acceptsTypeName(getTypeName())) { 657 return Track.TYPE + " (" + getTypeName() + ")"; 658 } 659 if (destination == null || track == null) { 660 return Track.OKAY; 661 } 662 return track.isRollingStockAccepted(this); 663 } 664 665 public Location getDestination() { 666 return _destination; 667 } 668 669 /** 670 * Sets rolling stock destination without reserving destination track space or 671 * drop count. Does not fire a property change. Used by car router to test 672 * destinations. Use setDestination(Location, Track) instead. 673 * 674 * @param destination for the rolling stock 675 */ 676 public void setDestination(Location destination) { 677 _destination = destination; 678 } 679 680 public String getDestinationName() { 681 if (getDestination() != null) { 682 return getDestination().getName(); 683 } 684 return NONE; 685 } 686 687 public String getSplitDestinationName() { 688 return TrainCommon.splitString(getDestinationName()); 689 } 690 691 public String getDestinationId() { 692 if (getDestination() != null) { 693 return getDestination().getId(); 694 } 695 return NONE; 696 } 697 698 /** 699 * Sets rolling stock destination track without reserving destination track 700 * space or drop count. Used by car router to test destinations. Does not fire a 701 * property change. Use setDestination(Location, Track) instead. 702 * 703 * @param track The Track for set out at destination. 704 * 705 */ 706 public void setDestinationTrack(Track track) { 707 if (track != null) { 708 _destination = track.getLocation(); 709 } 710 _trackDestination = track; 711 } 712 713 public Track getDestinationTrack() { 714 return _trackDestination; 715 } 716 717 public String getDestinationTrackName() { 718 if (getDestinationTrack() != null) { 719 return getDestinationTrack().getName(); 720 } 721 return NONE; 722 } 723 724 public String getSplitDestinationTrackName() { 725 return TrainCommon.splitString(getDestinationTrackName()); 726 } 727 728 public String getDestinationTrackId() { 729 if (getDestinationTrack() != null) { 730 return getDestinationTrack().getId(); 731 } 732 return NONE; 733 } 734 735 public void setClone(boolean clone) { 736 boolean old = _clone; 737 _clone = clone; 738 if (!old == clone) { 739 setDirtyAndFirePropertyChange("clone", old ? "true" : "false", clone ? "true" : "false"); // NOI18N 740 } 741 } 742 743 public boolean isClone() { 744 return _clone; 745 } 746 747 public void setCloneOrder(int number) { 748 _cloneOrder = number; 749 } 750 751 public int getCloneOrder() { 752 return _cloneOrder; 753 } 754 755 public void setDivision(Division division) { 756 Division old = _division; 757 _division = division; 758 if (old != _division) { 759 setDirtyAndFirePropertyChange("homeDivisionChange", old, division); 760 } 761 } 762 763 public Division getDivision() { 764 return _division; 765 } 766 767 public String getDivisionName() { 768 if (getDivision() != null) { 769 return getDivision().getName(); 770 } 771 return NONE; 772 } 773 774 public String getDivisionId() { 775 if (getDivision() != null) { 776 return getDivision().getId(); 777 } 778 return NONE; 779 } 780 781 /** 782 * Used to block cars from staging 783 * 784 * @param id The location id from where the car came from before going into 785 * staging. 786 */ 787 public void setLastLocationId(String id) { 788 _lastLocationId = id; 789 } 790 791 public String getLastLocationId() { 792 return _lastLocationId; 793 } 794 795 public String getLastLocationName() { 796 Location location = locationManager.getLocationById(getLastLocationId()); 797 if (location != null) { 798 return location.getName(); 799 } 800 return NONE; 801 } 802 803 public void setLastTrackId(String id) { 804 _lastTrackId = id; 805 } 806 807 public String getLastTrackId() { 808 return _lastTrackId; 809 } 810 811 public String getLastTrackName() { 812 Location location = locationManager.getLocationById(getLastLocationId()); 813 if (location != null) { 814 Track track = location.getTrackById(getLastTrackId()); 815 if (track != null) { 816 return track.getName(); 817 } 818 } 819 return NONE; 820 } 821 822 public void setMoves(int moves) { 823 int old = _moves; 824 _moves = moves; 825 if (old != moves) { 826 setDirtyAndFirePropertyChange("rolling stock moves", Integer.toString(old), // NOI18N 827 Integer.toString(moves)); 828 } 829 } 830 831 public int getMoves() { 832 return _moves; 833 } 834 835 /** 836 * Sets the train that will service this rolling stock. 837 * 838 * @param train The Train. 839 * 840 */ 841 public void setTrain(Train train) { 842 Train old = _train; 843 _train = train; 844 if (old != train) { 845 if (old != null) { 846 old.removePropertyChangeListener(this); 847 } 848 if (train != null) { 849 train.addPropertyChangeListener(this); 850 } 851 setDirtyAndFirePropertyChange(TRAIN_CHANGED_PROPERTY, old, train); 852 } 853 } 854 855 public Train getTrain() { 856 return _train; 857 } 858 859 public String getTrainName() { 860 if (getTrain() != null) { 861 return getTrain().getName(); 862 } 863 return NONE; 864 } 865 866 /** 867 * Sets the last train that serviced this rolling stock. 868 * 869 * @param train The last Train. 870 */ 871 public void setLastTrain(Train train) { 872 Train old = _lastTrain; 873 _lastTrain = train; 874 if (old != train) { 875 if (old != null) { 876 old.removePropertyChangeListener(this); 877 } 878 if (train != null) { 879 train.addPropertyChangeListener(this); 880 } 881 setDirtyAndFirePropertyChange(TRAIN_CHANGED_PROPERTY, old, train); 882 } 883 } 884 885 public Train getLastTrain() { 886 return _lastTrain; 887 } 888 889 public String getLastTrainName() { 890 if (getLastTrain() != null) { 891 return getLastTrain().getName(); 892 } 893 return NONE; 894 } 895 896 /** 897 * Sets the location where the rolling stock will be picked up by the train. 898 * 899 * @param routeLocation the pick up location for this rolling stock. 900 */ 901 public void setRouteLocation(RouteLocation routeLocation) { 902 // a couple of error checks before setting the route location 903 if (getLocation() == null && routeLocation != null) { 904 log.debug("WARNING rolling stock ({}) does not have an assigned location", this); // NOI18N 905 } else if (routeLocation != null && getLocation() != null && !routeLocation.getName().equals(getLocation().getName())) { 906 log.error("ERROR route location name({}) not equal to location name ({}) for rolling stock ({})", 907 routeLocation.getName(), getLocation().getName(), this); // NOI18N 908 } 909 RouteLocation old = _routeLocation; 910 _routeLocation = routeLocation; 911 if (old != routeLocation) { 912 setDirtyAndFirePropertyChange(ROUTE_LOCATION_CHANGED_PROPERTY, old, routeLocation); 913 } 914 } 915 916 /** 917 * Where in a train's route this car resides 918 * 919 * @return the location in a train's route 920 */ 921 public RouteLocation getRouteLocation() { 922 return _routeLocation; 923 } 924 925 public String getRouteLocationId() { 926 if (getRouteLocation() != null) { 927 return getRouteLocation().getId(); 928 } 929 return NONE; 930 } 931 932 /** 933 * Used to determine which train delivered a car to an interchange track. 934 * 935 * @return the route id of the last train delivering this car. 936 */ 937 public String getLastRouteId() { 938 return _routeId; 939 } 940 941 /** 942 * Sets the id of the route that was used to set out the rolling stock. Used to 943 * determine if the rolling stock can be pick ups from an interchange track. 944 * 945 * @param id The route id. 946 */ 947 public void setLastRouteId(String id) { 948 _routeId = id; 949 } 950 951 public String getValue() { 952 return _value; 953 } 954 955 /** 956 * Sets the value (cost, price) for this rolling stock. Currently has nothing to 957 * do with operations. But nice to have. 958 * 959 * @param value a string representing what this item is worth. 960 */ 961 public void setValue(String value) { 962 String old = _value; 963 _value = value; 964 if (!old.equals(value)) { 965 setDirtyAndFirePropertyChange("rolling stock value", old, value); // NOI18N 966 } 967 } 968 969 public String getRfid() { 970 return _rfid; 971 } 972 973 /** 974 * Sets the RFID for this rolling stock. 975 * 976 * @param id 12 character RFID string. 977 */ 978 public void setRfid(String id) { 979 String old = _rfid; 980 if (id != null && !id.equals(old)) { 981 log.debug("Setting IdTag for {} to {}", this, id); 982 _rfid = id; 983 if (!id.equals(NONE)) { 984 try { 985 IdTag tag = InstanceManager.getDefault(IdTagManager.class).provideIdTag(id); 986 setIdTag(tag); 987 } catch (IllegalArgumentException e) { 988 log.error("Exception recording tag {} - exception value {}", id, e.getMessage()); 989 } 990 } 991 setDirtyAndFirePropertyChange("rolling stock rfid", old, id); // NOI18N 992 } 993 } 994 995 public IdTag getIdTag() { 996 return _tag; 997 } 998 999 /** 1000 * Sets the id tag for this rolling stock. The id tag isn't saved, between 1001 * session but the tag label is saved as _rfid. 1002 * 1003 * @param tag the id tag 1004 */ 1005 public void setIdTag(IdTag tag) { 1006 if (_tag != null) { 1007 _tag.removePropertyChangeListener(_tagListener); 1008 } 1009 _tag = tag; 1010 if (_tagListener == null) { 1011 // store the tag listener so we can reuse it and 1012 // dispose of it as necessary. 1013 _tagListener = new PropertyChangeListener() { 1014 @Override 1015 public void propertyChange(java.beans.PropertyChangeEvent e) { 1016 if (e.getPropertyName().equals("whereLastSeen")) { 1017 log.debug("Tag Reader Position update received for {}", this); 1018 // update the position of this piece of rolling 1019 // stock when its IdTag is seen, but only if 1020 // the actual location changes. 1021 if (e.getNewValue() != null) { 1022 // first, check to see if this reader is 1023 // associated with a track. 1024 Track newTrack = locationManager.getTrackByReporter((jmri.Reporter) e.getNewValue()); 1025 if (newTrack != null) { 1026 if (newTrack != getTrack()) { 1027 // set the car's location based on the track. 1028 setLocation(newTrack.getLocation(), newTrack); 1029 // also notify listeners that the last seen 1030 // location has changed. 1031 setDirtyAndFirePropertyChange("rolling stock whereLastSeen", _whereLastSeen, 1032 _whereLastSeen = newTrack.getLocation()); 1033 1034 } 1035 } else { 1036 // the reader isn't associated with a track, 1037 Location newLocation = locationManager 1038 .getLocationByReporter((jmri.Reporter) e.getNewValue()); 1039 if (newLocation != getLocation()) { 1040 // we really should be able to set the 1041 // location where we last saw the tag: 1042 // setLocation(newLocation,null); 1043 // for now, notify listeners that the 1044 // location changed. 1045 setDirtyAndFirePropertyChange("rolling stock whereLastSeen", _whereLastSeen, 1046 _whereLastSeen = newLocation); 1047 1048 } 1049 } 1050 } 1051 } 1052 if (e.getPropertyName().equals("whenLastSeen")) { 1053 log.debug("Tag Reader Time at Location update received for {}", this); 1054 // update the time when this car was last moved 1055 // stock when its IdTag is seen. 1056 if (e.getNewValue() != null) { 1057 Date newDate = ((Date) e.getNewValue()); 1058 setLastDate(newDate); 1059 // and notify listeners when last seen was updated. 1060 setDirtyAndFirePropertyChange("rolling stock whenLastSeen", _whenLastSeen, 1061 _whenLastSeen = newDate); 1062 } 1063 } 1064 } 1065 }; 1066 } 1067 if (_tag != null) { 1068 _tag.addPropertyChangeListener(_tagListener); 1069 setRfid(_tag.getSystemName()); 1070 } else { 1071 setRfid(NONE); 1072 } 1073 // initilize _whenLastSeen and _whereLastSeen for property 1074 // change notification. 1075 _whereLastSeen = getWhereLastSeen(); 1076 _whenLastSeen = getWhenLastSeen(); 1077 } 1078 1079 public String getWhereLastSeenName() { 1080 if (getWhereLastSeen() != null) { 1081 return getWhereLastSeen().getName(); 1082 } 1083 return NONE; 1084 } 1085 1086 public Location getWhereLastSeen() { 1087 if (_tag == null) { 1088 return null; 1089 } 1090 jmri.Reporter r = _tag.getWhereLastSeen(); 1091 Track t = locationManager.getTrackByReporter(r); 1092 if (t != null) { 1093 return t.getLocation(); 1094 } 1095 // the reader isn't associated with a track, return 1096 // the location it is associated with, which might be null. 1097 return locationManager.getLocationByReporter(r); 1098 } 1099 1100 public Track getTrackLastSeen() { 1101 if (_tag == null) { 1102 // there isn't a tag associated with this piece of rolling stock. 1103 return null; 1104 } 1105 jmri.Reporter r = _tag.getWhereLastSeen(); 1106 if (r == null) { 1107 // there is a tag associated with this piece 1108 // of rolling stock, but no reporter has seen it (or it was reset). 1109 return null; 1110 } 1111 // this return value will be null, if there isn't an associated track 1112 // for the last reporter. 1113 return locationManager.getTrackByReporter(r); 1114 } 1115 1116 public String getTrackLastSeenName() { 1117 // let getTrackLastSeen() find the track, if it exists. 1118 Track t = getTrackLastSeen(); 1119 if (t != null) { 1120 // if there is a track, return the name. 1121 return t.getName(); 1122 } 1123 // otherwise, there is no track to return the name of. 1124 return NONE; 1125 } 1126 1127 public Date getWhenLastSeen() { 1128 if (_tag == null) { 1129 return null; // never seen, so no date. 1130 } 1131 return _tag.getWhenLastSeen(); 1132 } 1133 1134 /** 1135 * Provides the last date when this rolling stock was moved, or was reset from a 1136 * built train, as a string. 1137 * 1138 * @return date 1139 */ 1140 public String getWhenLastSeenDate() { 1141 if (getWhenLastSeen() == null) { 1142 return NONE; // return an empty string for the default date. 1143 } 1144 SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); // NOI18N 1145 return format.format(getWhenLastSeen()); 1146 } 1147 1148 /** 1149 * Provides the last date when this rolling stock was moved 1150 * 1151 * @return String yyyy/MM/dd HH:mm:ss 1152 */ 1153 public String getSortDate() { 1154 if (_lastDate.equals((new java.util.GregorianCalendar()).getGregorianChange())) { 1155 return NONE; // return an empty string for the default date. 1156 } 1157 SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); // NOI18N 1158 return format.format(_lastDate); 1159 } 1160 1161 /** 1162 * Provides the last date when this rolling stock was moved 1163 * 1164 * @return String MM/dd/yyyy HH:mm:ss 1165 */ 1166 public String getLastDate() { 1167 if (_lastDate.equals((new java.util.GregorianCalendar()).getGregorianChange())) { 1168 return NONE; // return an empty string for the default date. 1169 } 1170 SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); // NOI18N 1171 return format.format(_lastDate); 1172 } 1173 1174 /** 1175 * Sets the last date when this rolling stock was moved 1176 * 1177 * @param date The Date when this rolling stock was last moved. 1178 * 1179 */ 1180 public void setLastDate(Date date) { 1181 Date old = _lastDate; 1182 _lastDate = date; 1183 if (!old.equals(_lastDate)) { 1184 setDirtyAndFirePropertyChange("rolling stock date", old, date); // NOI18N 1185 } 1186 } 1187 1188 /** 1189 * Provides the last date when this rolling stock was moved 1190 * 1191 * @return date 1192 */ 1193 public Date getLastMoveDate() { 1194 return _lastDate; 1195 } 1196 1197 /** 1198 * Sets the last date when this rolling stock was moved. This method is used 1199 * only for loading data from a file. Use setLastDate(Date) instead. 1200 * 1201 * @param date yyyy/MM/dd HH:mm:ss, MM/dd/yyyy HH:mm:ss, MM/dd/yyyy hh:mmaa, 1202 * or MM/dd/yyyy HH:mm 1203 */ 1204 public void setLastDate(String date) { 1205 Date d = TrainCommon.convertStringToDate(date); 1206 if (d != null) { 1207 _lastDate = d; 1208 } 1209 } 1210 1211 public void setBlocking(int number) { 1212 int old = _blocking; 1213 _blocking = number; 1214 if (old != number) { 1215 setDirtyAndFirePropertyChange("rolling stock blocking changed", old, number); // NOI18N 1216 } 1217 } 1218 1219 public int getBlocking() { 1220 return _blocking; 1221 } 1222 1223 /** 1224 * Set where in a train's route this rolling stock will be set out. 1225 * 1226 * @param routeDestination the location where the rolling stock is to leave the 1227 * train. 1228 */ 1229 public void setRouteDestination(RouteLocation routeDestination) { 1230 if (routeDestination != null && 1231 getDestination() != null && 1232 !routeDestination.getName().equals(getDestination().getName())) { 1233 log.debug("WARNING route destination name ({}) not equal to destination name ({}) for rolling stock ({})", 1234 routeDestination.getName(), getDestination().getName(), this); // NOI18N 1235 } 1236 RouteLocation old = _routeDestination; 1237 _routeDestination = routeDestination; 1238 if (old != routeDestination) { 1239 setDirtyAndFirePropertyChange(ROUTE_DESTINATION_CHANGED_PROPERTY, old, routeDestination); 1240 } 1241 } 1242 1243 public RouteLocation getRouteDestination() { 1244 return _routeDestination; 1245 } 1246 1247 public String getRouteDestinationId() { 1248 if (getRouteDestination() != null) { 1249 return getRouteDestination().getId(); 1250 } 1251 return NONE; 1252 } 1253 1254 public void setOwnerName(String owner) { 1255 String old = _owner; 1256 _owner = owner; 1257 if (!old.equals(owner)) { 1258 setDirtyAndFirePropertyChange("rolling stock owner", old, owner); // NOI18N 1259 } 1260 } 1261 1262 public String getOwnerName() { 1263 return _owner; 1264 } 1265 1266 /** 1267 * Set the rolling stock location as unknown. 1268 * 1269 * @param unknown when true, the rolling stock location is unknown. 1270 */ 1271 public void setLocationUnknown(boolean unknown) { 1272 boolean old = _locationUnknown; 1273 _locationUnknown = unknown; 1274 if (!old == unknown) { 1275 setDirtyAndFirePropertyChange("car location known", old ? "true" : "false", unknown ? "true" // NOI18N 1276 : "false"); // NOI18N 1277 } 1278 } 1279 1280 /** 1281 * 1282 * @return true when car's location is unknown 1283 */ 1284 public boolean isLocationUnknown() { 1285 return _locationUnknown; 1286 } 1287 1288 /** 1289 * Sets the rolling stock service state. When true, rolling stock is out of 1290 * service. Normal state is false, the rolling stock is in service and 1291 * available. 1292 * 1293 * @param outOfService when true, out of service 1294 */ 1295 public void setOutOfService(boolean outOfService) { 1296 boolean old = _outOfService; 1297 _outOfService = outOfService; 1298 if (!old == outOfService) { 1299 setDirtyAndFirePropertyChange("car out of service", old ? "true" : "false", outOfService ? "true" // NOI18N 1300 : "false"); // NOI18N 1301 } 1302 } 1303 1304 /** 1305 * 1306 * @return true when rolling stock is out of service 1307 */ 1308 public boolean isOutOfService() { 1309 return _outOfService; 1310 } 1311 1312 public void setSelected(boolean selected) { 1313 boolean old = _selected; 1314 _selected = selected; 1315 if (!old == selected) { 1316 setDirtyAndFirePropertyChange("selected", old ? "true" : "false", selected ? "true" // NOI18N 1317 : "false"); // NOI18N 1318 } 1319 } 1320 1321 /** 1322 * 1323 * @return true when rolling stock is selected 1324 */ 1325 public boolean isSelected() { 1326 return _selected; 1327 } 1328 1329 public void setComment(String comment) { 1330 String old = _comment; 1331 _comment = comment; 1332 if (!old.equals(comment)) { 1333 setDirtyAndFirePropertyChange(COMMENT_CHANGED_PROPERTY, old, comment); // NOI18N 1334 } 1335 } 1336 1337 public String getComment() { 1338 return _comment; 1339 } 1340 1341 public void setPickupTime(String time) { 1342 String old = _pickupTime; 1343 _pickupTime = time; 1344 setDirtyAndFirePropertyChange("Pickup Time Changed", old, time); // NOI18N 1345 } 1346 1347 public String getPickupTime() { 1348 if (getTrain() != null) { 1349 return _pickupTime; 1350 } 1351 return NONE; 1352 } 1353 1354 public void setSetoutTime(String time) { 1355 String old = _setoutTime; 1356 _setoutTime = time; 1357 setDirtyAndFirePropertyChange("Setout Time Changed", old, time); // NOI18N 1358 } 1359 1360 public String getSetoutTime() { 1361 if (getTrain() != null) { 1362 return _setoutTime; 1363 } 1364 return NONE; 1365 } 1366 1367 protected void moveRollingStock(RouteLocation current, RouteLocation next) { 1368 if (current == getRouteLocation()) { 1369 setLastDate(java.util.Calendar.getInstance().getTime()); 1370 // Arriving at destination? 1371 if (getRouteLocation() == getRouteDestination() || next == null) { 1372 if (getRouteLocation() == getRouteDestination()) { 1373 log.debug("Rolling stock ({}) has arrived at destination ({})", this, getDestination()); 1374 } else { 1375 log.error("Rolling stock ({}) has a null route location for next", this); // NOI18N 1376 } 1377 setLocation(getDestination(), getDestinationTrack(), RollingStock.FORCE); // force RS to destination 1378 setDestination(null, null); // this also clears the route locations 1379 setLastTrain(getTrain()); // save the last train moving this rs 1380 setTrain(null); // this must come after setDestination (route id is set) 1381 setMoves(getMoves() + 1); // bump count 1382 } else { 1383 log.debug("Rolling stock ({}) is in train ({}) leaves location ({}) destination ({})", this, 1384 getTrainName(), current.getName(), next.getName()); 1385 setLocation(next.getLocation(), null, RollingStock.FORCE); // force RS to location 1386 setRouteLocation(next); 1387 } 1388 } 1389 } 1390 1391 public void reset() { 1392 setPickupTime(NONE); 1393 // the order of the next two instructions is important, otherwise rs will have 1394 // train's route id 1395 setTrain(null); 1396 setDestination(null, null); 1397 } 1398 1399 /** 1400 * Remove rolling stock. Releases all listeners. 1401 */ 1402 public void dispose() { 1403 setTrain(null); 1404 setDestination(null, null); 1405 setLocation(null, null); 1406 InstanceManager.getDefault(CarRoads.class).removePropertyChangeListener(this); 1407 InstanceManager.getDefault(CarOwners.class).removePropertyChangeListener(this); 1408 InstanceManager.getDefault(CarColors.class).removePropertyChangeListener(this); 1409 if (getIdTag() != null) { 1410 getIdTag().removePropertyChangeListener(_tagListener); 1411 } 1412 } 1413 1414 /** 1415 * Construct this Entry from XML. 1416 * 1417 * @param e RollingStock XML element 1418 */ 1419 public RollingStock(org.jdom2.Element e) { 1420 this(); 1421 org.jdom2.Attribute a; 1422 if ((a = e.getAttribute(Xml.ID)) != null) { 1423 _id = a.getValue(); 1424 } else { 1425 log.warn("no id attribute in rolling stock element when reading operations"); 1426 } 1427 if ((a = e.getAttribute(Xml.ROAD_NUMBER)) != null) { 1428 _number = a.getValue(); 1429 } 1430 if ((a = e.getAttribute(Xml.ROAD_NAME)) != null) { 1431 _road = a.getValue(); 1432 } 1433 if (_id == null || !_id.equals(createId(_road, _number))) { 1434 _id = createId(_road, _number); 1435 } 1436 if ((a = e.getAttribute(Xml.TYPE)) != null) { 1437 _type = a.getValue(); 1438 } 1439 if ((a = e.getAttribute(Xml.LENGTH)) != null) { 1440 _length = a.getValue(); 1441 } 1442 if ((a = e.getAttribute(Xml.COLOR)) != null) { 1443 _color = a.getValue(); 1444 } 1445 if ((a = e.getAttribute(Xml.WEIGHT)) != null) { 1446 _weight = a.getValue(); 1447 } 1448 if ((a = e.getAttribute(Xml.WEIGHT_TONS)) != null) { 1449 _weightTons = a.getValue(); 1450 } 1451 if ((a = e.getAttribute(Xml.BUILT)) != null) { 1452 _built = a.getValue(); 1453 } 1454 if ((a = e.getAttribute(Xml.CLONE)) != null) { 1455 _clone = a.getValue().equals(Xml.TRUE); 1456 } 1457 Location location = null; 1458 Track track = null; 1459 if ((a = e.getAttribute(Xml.LOCATION_ID)) != null) { 1460 location = locationManager.getLocationById(a.getValue()); 1461 } 1462 if ((a = e.getAttribute(Xml.SEC_LOCATION_ID)) != null && location != null) { 1463 track = location.getTrackById(a.getValue()); 1464 } 1465 setLocation(location, track, RollingStock.FORCE); // force location 1466 1467 Location destination = null; 1468 track = null; 1469 if ((a = e.getAttribute(Xml.DESTINATION_ID)) != null) { 1470 destination = locationManager.getLocationById(a.getValue()); 1471 } 1472 if ((a = e.getAttribute(Xml.SEC_DESTINATION_ID)) != null && destination != null) { 1473 track = destination.getTrackById(a.getValue()); 1474 } 1475 setDestination(destination, track, RollingStock.FORCE); // force destination 1476 1477 if ((a = e.getAttribute(Xml.DIVISION_ID)) != null) { 1478 _division = InstanceManager.getDefault(DivisionManager.class).getDivisionById(a.getValue()); 1479 } 1480 // TODO remove the following 3 lines in 2022 1481 if ((a = e.getAttribute(Xml.DIVISION_ID_ERROR)) != null) { 1482 _division = InstanceManager.getDefault(DivisionManager.class).getDivisionById(a.getValue()); 1483 } 1484 if ((a = e.getAttribute(Xml.MOVES)) != null) { 1485 try { 1486 _moves = Integer.parseInt(a.getValue()); 1487 } catch (NumberFormatException nfe) { 1488 log.error("Move count ({}) for rollingstock ({}) isn't a valid number!", a.getValue(), toString()); 1489 } 1490 } 1491 if ((a = e.getAttribute(Xml.LAST_LOCATION_ID)) != null) { 1492 _lastLocationId = a.getValue(); 1493 } 1494 if ((a = e.getAttribute(Xml.LAST_TRACK_ID)) != null) { 1495 _lastTrackId = a.getValue(); 1496 } 1497 if ((a = e.getAttribute(Xml.TRAIN_ID)) != null) { 1498 setTrain(InstanceManager.getDefault(TrainManager.class).getTrainById(a.getValue())); 1499 } else if ((a = e.getAttribute(Xml.TRAIN)) != null) { 1500 setTrain(InstanceManager.getDefault(TrainManager.class).getTrainByName(a.getValue())); 1501 } 1502 if (getTrain() != null && 1503 getTrain().getRoute() != null && 1504 (a = e.getAttribute(Xml.ROUTE_LOCATION_ID)) != null) { 1505 _routeLocation = getTrain().getRoute().getRouteLocationById(a.getValue()); 1506 if ((a = e.getAttribute(Xml.ROUTE_DESTINATION_ID)) != null) { 1507 _routeDestination = getTrain().getRoute().getRouteLocationById(a.getValue()); 1508 } 1509 } 1510 if ((a = e.getAttribute(Xml.LAST_TRAIN_ID)) != null) { 1511 setLastTrain(InstanceManager.getDefault(TrainManager.class).getTrainById(a.getValue())); 1512 } 1513 if ((a = e.getAttribute(Xml.LAST_ROUTE_ID)) != null) { 1514 _routeId = a.getValue(); 1515 } 1516 if ((a = e.getAttribute(Xml.OWNER)) != null) { 1517 _owner = a.getValue(); 1518 } 1519 if ((a = e.getAttribute(Xml.COMMENT)) != null) { 1520 _comment = a.getValue(); 1521 } 1522 if ((a = e.getAttribute(Xml.VALUE)) != null) { 1523 _value = a.getValue(); 1524 } 1525 if ((a = e.getAttribute(Xml.RFID)) != null) { 1526 setRfid(a.getValue()); 1527 } 1528 if ((a = e.getAttribute(Xml.LOC_UNKNOWN)) != null) { 1529 _locationUnknown = a.getValue().equals(Xml.TRUE); 1530 } 1531 if ((a = e.getAttribute(Xml.OUT_OF_SERVICE)) != null) { 1532 _outOfService = a.getValue().equals(Xml.TRUE); 1533 } 1534 if ((a = e.getAttribute(Xml.SELECTED)) != null) { 1535 _selected = a.getValue().equals(Xml.TRUE); 1536 } 1537 if ((a = e.getAttribute(Xml.DATE)) != null) { 1538 setLastDate(a.getValue()); // uses the setLastDate(String) method. 1539 } 1540 if ((a = e.getAttribute(Xml.PICKUP_TIME)) != null) { 1541 _pickupTime = a.getValue(); 1542 } 1543 if ((a = e.getAttribute(Xml.SETOUT_TIME)) != null) { 1544 _setoutTime = a.getValue(); 1545 } 1546 if ((a = e.getAttribute(Xml.BLOCKING)) != null) { 1547 try { 1548 _blocking = Integer.parseInt(a.getValue()); 1549 } catch (NumberFormatException nfe) { 1550 log.error("Blocking ({}) for rollingstock ({}) isn't a valid number!", a.getValue(), toString()); 1551 } 1552 } 1553 // check for rolling stock without a track assignment 1554 if (getLocation() != null && getTrack() == null && getTrain() == null) { 1555 log.warn("Rollingstock ({}) at ({}) doesn't have a track assignment", this, getLocationName()); 1556 } 1557 addPropertyChangeListeners(); 1558 } 1559 1560 /** 1561 * Add XML elements to represent this Entry. 1562 * 1563 * @param e Element for car or engine store. 1564 * 1565 * @return Contents in a JDOM Element 1566 */ 1567 protected org.jdom2.Element store(org.jdom2.Element e) { 1568 e.setAttribute(Xml.ID, getId()); 1569 e.setAttribute(Xml.ROAD_NAME, getRoadName()); 1570 e.setAttribute(Xml.ROAD_NUMBER, getNumber()); 1571 e.setAttribute(Xml.TYPE, getTypeName()); 1572 e.setAttribute(Xml.LENGTH, getLength()); 1573 if (!getColor().equals(NONE)) { 1574 e.setAttribute(Xml.COLOR, getColor()); 1575 } 1576 if (!getWeight().equals(DEFAULT_WEIGHT)) { 1577 e.setAttribute(Xml.WEIGHT, getWeight()); 1578 } 1579 if (!getWeightTons().equals(NONE)) { 1580 e.setAttribute(Xml.WEIGHT_TONS, getWeightTons()); 1581 } 1582 if (!getBuilt().equals(NONE)) { 1583 e.setAttribute(Xml.BUILT, getBuilt()); 1584 } 1585 if (!getLocationId().equals(NONE)) { 1586 e.setAttribute(Xml.LOCATION_ID, getLocationId()); 1587 } 1588 if (!getRouteLocationId().equals(NONE)) { 1589 e.setAttribute(Xml.ROUTE_LOCATION_ID, getRouteLocationId()); 1590 } 1591 if (!getTrackId().equals(NONE)) { 1592 e.setAttribute(Xml.SEC_LOCATION_ID, getTrackId()); 1593 } 1594 if (!getDestinationId().equals(NONE)) { 1595 e.setAttribute(Xml.DESTINATION_ID, getDestinationId()); 1596 } 1597 if (!getRouteDestinationId().equals(NONE)) { 1598 e.setAttribute(Xml.ROUTE_DESTINATION_ID, getRouteDestinationId()); 1599 } 1600 if (!getDestinationTrackId().equals(NONE)) { 1601 e.setAttribute(Xml.SEC_DESTINATION_ID, getDestinationTrackId()); 1602 } 1603 if (!getDivisionId().equals(NONE)) { 1604 e.setAttribute(Xml.DIVISION_ID, getDivisionId()); 1605 } 1606 if (!getLastRouteId().equals(NONE)) { 1607 e.setAttribute(Xml.LAST_ROUTE_ID, getLastRouteId()); 1608 } 1609 if (isClone()) { 1610 e.setAttribute(Xml.CLONE, isClone() ? Xml.TRUE : Xml.FALSE); 1611 } 1612 e.setAttribute(Xml.MOVES, Integer.toString(getMoves())); 1613 e.setAttribute(Xml.DATE, getLastDate()); 1614 e.setAttribute(Xml.SELECTED, isSelected() ? Xml.TRUE : Xml.FALSE); 1615 if (!getLastLocationId().equals(LOCATION_UNKNOWN)) { 1616 e.setAttribute(Xml.LAST_LOCATION_ID, getLastLocationId()); 1617 } 1618 if (!getLastTrackId().equals(LOCATION_UNKNOWN)) { 1619 e.setAttribute(Xml.LAST_TRACK_ID, getLastTrackId()); 1620 } 1621 if (!getTrainName().equals(NONE)) { 1622 e.setAttribute(Xml.TRAIN, getTrainName()); 1623 e.setAttribute(Xml.TRAIN_ID, getTrain().getId()); 1624 } 1625 if (!getLastTrainName().equals(NONE)) { 1626 e.setAttribute(Xml.LAST_TRAIN, getLastTrainName()); 1627 e.setAttribute(Xml.LAST_TRAIN_ID, getLastTrain().getId()); 1628 } 1629 if (!getOwnerName().equals(NONE)) { 1630 e.setAttribute(Xml.OWNER, getOwnerName()); 1631 } 1632 if (!getValue().equals(NONE)) { 1633 e.setAttribute(Xml.VALUE, getValue()); 1634 } 1635 if (!getRfid().equals(NONE)) { 1636 e.setAttribute(Xml.RFID, getRfid()); 1637 } 1638 if (isLocationUnknown()) { 1639 e.setAttribute(Xml.LOC_UNKNOWN, isLocationUnknown() ? Xml.TRUE : Xml.FALSE); 1640 } 1641 if (isOutOfService()) { 1642 e.setAttribute(Xml.OUT_OF_SERVICE, isOutOfService() ? Xml.TRUE : Xml.FALSE); 1643 } 1644 if (!getPickupTime().equals(NONE)) { 1645 e.setAttribute(Xml.PICKUP_TIME, getPickupTime()); 1646 } 1647 if (!getSetoutTime().equals(NONE)) { 1648 e.setAttribute(Xml.SETOUT_TIME, getSetoutTime()); 1649 } 1650 if (getBlocking() != 0) { 1651 e.setAttribute(Xml.BLOCKING, Integer.toString(getBlocking())); 1652 } 1653 if (!getComment().equals(NONE)) { 1654 e.setAttribute(Xml.COMMENT, getComment()); 1655 } 1656 return e; 1657 } 1658 1659 private void addPropertyChangeListeners() { 1660 InstanceManager.getDefault(CarRoads.class).addPropertyChangeListener(this); 1661 InstanceManager.getDefault(CarOwners.class).addPropertyChangeListener(this); 1662 InstanceManager.getDefault(CarColors.class).addPropertyChangeListener(this); 1663 } 1664 1665 // rolling stock listens for changes in a location name or if a location is 1666 // deleted 1667 @Override 1668 public void propertyChange(PropertyChangeEvent e) { 1669 // log.debug("Property change for rolling stock: " + toString()+ " property 1670 // name: " 1671 // +e.getPropertyName()+ " old: "+e.getOldValue()+ " new: "+e.getNewValue()); 1672 // notify if track or location name changes 1673 if (e.getPropertyName().equals(Location.NAME_CHANGED_PROPERTY)) { 1674 log.debug("Property change for rolling stock: ({}) property name: ({}) old: ({}) new: ({})", this, 1675 e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1676 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1677 } 1678 if (e.getPropertyName().equals(Location.DISPOSE_CHANGED_PROPERTY)) { 1679 if (e.getSource() == getLocation()) { 1680 log.debug("delete location for rolling stock: ({})", this); 1681 setLocation(null, null); 1682 } 1683 if (e.getSource() == getDestination()) { 1684 log.debug("delete destination for rolling stock: ({})", this); 1685 setDestination(null, null); 1686 } 1687 } 1688 if (e.getPropertyName().equals(Track.DISPOSE_CHANGED_PROPERTY)) { 1689 if (e.getSource() == getTrack()) { 1690 log.debug("delete location for rolling stock: ({})", this); 1691 setLocation(getLocation(), null); 1692 } 1693 if (e.getSource() == getDestinationTrack()) { 1694 log.debug("delete destination for rolling stock: ({})", this); 1695 setDestination(getDestination(), null); 1696 } 1697 } 1698 if (e.getPropertyName().equals(Train.DISPOSE_CHANGED_PROPERTY) && e.getSource() == getTrain()) { 1699 log.debug("delete train for rolling stock: ({})", this); 1700 setTrain(null); 1701 } 1702 if (e.getPropertyName().equals(Train.TRAIN_LOCATION_CHANGED_PROPERTY) && e.getSource() == getTrain()) { 1703 log.debug("Rolling stock ({}) is serviced by train ({})", this, getTrainName()); 1704 moveRollingStock((RouteLocation) e.getOldValue(), (RouteLocation) e.getNewValue()); 1705 } 1706 if (e.getPropertyName().equals(Train.STATUS_CHANGED_PROPERTY) && 1707 e.getNewValue().equals(Train.TRAIN_RESET) && 1708 e.getSource() == getTrain()) { 1709 log.debug("Rolling stock ({}) is removed from train ({}) by reset", this, getTrainName()); // NOI18N 1710 reset(); 1711 } 1712 if (e.getPropertyName().equals(Train.NAME_CHANGED_PROPERTY)) { 1713 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1714 } 1715 if (e.getPropertyName().equals(CarRoads.CARROADS_NAME_CHANGED_PROPERTY)) { 1716 if (e.getOldValue().equals(getRoadName())) { 1717 log.debug("Rolling stock ({}) sees road name change from ({}) to ({})", this, e.getOldValue(), 1718 e.getNewValue()); // NOI18N 1719 if (e.getNewValue() != null) { 1720 setRoadName((String) e.getNewValue()); 1721 } 1722 } 1723 } 1724 if (e.getPropertyName().equals(CarOwners.CAROWNERS_NAME_CHANGED_PROPERTY)) { 1725 if (e.getOldValue().equals(getOwnerName())) { 1726 log.debug("Rolling stock ({}) sees owner name change from ({}) to ({})", this, e.getOldValue(), 1727 e.getNewValue()); // NOI18N 1728 setOwnerName((String) e.getNewValue()); 1729 } 1730 } 1731 if (e.getPropertyName().equals(CarColors.CARCOLORS_NAME_CHANGED_PROPERTY)) { 1732 if (e.getOldValue().equals(getColor())) { 1733 log.debug("Rolling stock ({}) sees color name change from ({}) to ({})", this, e.getOldValue(), 1734 e.getNewValue()); // NOI18N 1735 setColor((String) e.getNewValue()); 1736 } 1737 } 1738 } 1739 1740 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 1741 firePropertyChange(p, old, n); 1742 } 1743 1744 private final static Logger log = LoggerFactory.getLogger(RollingStock.class); 1745 1746}