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