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