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