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