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