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("MM/dd/yyyy 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 MM/dd/yyyy HH:mm:ss 1067 */ 1068 public String getLastDate() { 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("MM/dd/yyyy HH:mm:ss"); // NOI18N 1073 return format.format(_lastDate); 1074 } 1075 1076 /** 1077 * Sets the last date when this rolling stock was moved 1078 * 1079 * @param date The Date when this rolling stock was last moved. 1080 * 1081 */ 1082 public void setLastDate(Date date) { 1083 Date old = _lastDate; 1084 _lastDate = date; 1085 if (!old.equals(_lastDate)) { 1086 setDirtyAndFirePropertyChange("rolling stock date", old, date); // NOI18N 1087 } 1088 } 1089 1090 /** 1091 * Provides the last date when this rolling stock was moved 1092 * 1093 * @return date 1094 */ 1095 public Date getLastMoveDate() { 1096 return _lastDate; 1097 } 1098 1099 /** 1100 * Sets the last date when this rolling stock was moved. This method is used 1101 * only for loading data from a file. Use setLastDate(Date) instead. 1102 * 1103 * @param date MM/dd/yyyy HH:mm:ss 1104 */ 1105 private void setLastDate(String date) { 1106 if (date.equals(NONE)) { 1107 return; // there was no date specified. 1108 } 1109 Date oldDate = _lastDate; 1110 // create a date object from the value. 1111 try { 1112 // try the new format (with seconds). 1113 SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); // NOI18N 1114 _lastDate = formatter.parse(date); 1115 } catch (java.text.ParseException pe0) { 1116 // try the old 12 hour format (no seconds). 1117 try { 1118 SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy hh:mmaa"); // NOI18N 1119 _lastDate = formatter.parse(date); 1120 } catch (java.text.ParseException pe1) { 1121 try { 1122 // try 24hour clock. 1123 SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy HH:mm"); // NOI18N 1124 _lastDate = formatter.parse(date); 1125 } catch (java.text.ParseException pe2) { 1126 log.warn("Not able to parse date: {} for rolling stock ({})", date, this); 1127 _lastDate = oldDate; // set the date back to what it was before 1128 } 1129 } 1130 } 1131 } 1132 1133 public void setBlocking(int number) { 1134 int old = _blocking; 1135 _blocking = number; 1136 if (old != number) { 1137 setDirtyAndFirePropertyChange("rolling stock blocking changed", old, number); // NOI18N 1138 } 1139 } 1140 1141 public int getBlocking() { 1142 return _blocking; 1143 } 1144 1145 /** 1146 * Set where in a train's route this rolling stock will be set out. 1147 * 1148 * @param routeDestination the location where the rolling stock is to leave the 1149 * train. 1150 */ 1151 public void setRouteDestination(RouteLocation routeDestination) { 1152 if (routeDestination != null && 1153 getDestination() != null && 1154 !routeDestination.getName().equals(getDestination().getName())) { 1155 log.debug("WARNING route destination name ({}) not equal to destination name ({}) for rolling stock ({})", 1156 routeDestination.getName(), getDestination().getName(), this); // NOI18N 1157 } 1158 RouteLocation old = _routeDestination; 1159 _routeDestination = routeDestination; 1160 if (old != routeDestination) { 1161 setDirtyAndFirePropertyChange(ROUTE_DESTINATION_CHANGED_PROPERTY, old, routeDestination); 1162 } 1163 } 1164 1165 public RouteLocation getRouteDestination() { 1166 return _routeDestination; 1167 } 1168 1169 public String getRouteDestinationId() { 1170 if (getRouteDestination() != null) { 1171 return getRouteDestination().getId(); 1172 } 1173 return NONE; 1174 } 1175 1176 public void setOwnerName(String owner) { 1177 String old = _owner; 1178 _owner = owner; 1179 if (!old.equals(owner)) { 1180 setDirtyAndFirePropertyChange("rolling stock owner", old, owner); // NOI18N 1181 } 1182 } 1183 1184 public String getOwnerName() { 1185 return _owner; 1186 } 1187 1188 /** 1189 * Set the rolling stock location as unknown. 1190 * 1191 * @param unknown when true, the rolling stock location is unknown. 1192 */ 1193 public void setLocationUnknown(boolean unknown) { 1194 boolean old = _locationUnknown; 1195 _locationUnknown = unknown; 1196 if (!old == unknown) { 1197 setDirtyAndFirePropertyChange("car location known", old ? "true" : "false", unknown ? "true" // NOI18N 1198 : "false"); // NOI18N 1199 } 1200 } 1201 1202 /** 1203 * 1204 * @return true when car's location is unknown 1205 */ 1206 public boolean isLocationUnknown() { 1207 return _locationUnknown; 1208 } 1209 1210 /** 1211 * Sets the rolling stock service state. When true, rolling stock is out of 1212 * service. Normal state is false, the rolling stock is in service and 1213 * available. 1214 * 1215 * @param outOfService when true, out of service 1216 */ 1217 public void setOutOfService(boolean outOfService) { 1218 boolean old = _outOfService; 1219 _outOfService = outOfService; 1220 if (!old == outOfService) { 1221 setDirtyAndFirePropertyChange("car out of service", old ? "true" : "false", outOfService ? "true" // NOI18N 1222 : "false"); // NOI18N 1223 } 1224 } 1225 1226 /** 1227 * 1228 * @return true when rolling stock is out of service 1229 */ 1230 public boolean isOutOfService() { 1231 return _outOfService; 1232 } 1233 1234 public void setSelected(boolean selected) { 1235 boolean old = _selected; 1236 _selected = selected; 1237 if (!old == selected) { 1238 setDirtyAndFirePropertyChange("selected", old ? "true" : "false", selected ? "true" // NOI18N 1239 : "false"); // NOI18N 1240 } 1241 } 1242 1243 /** 1244 * 1245 * @return true when rolling stock is selected 1246 */ 1247 public boolean isSelected() { 1248 return _selected; 1249 } 1250 1251 public void setComment(String comment) { 1252 String old = _comment; 1253 _comment = comment; 1254 if (!old.equals(comment)) { 1255 setDirtyAndFirePropertyChange(COMMENT_CHANGED_PROPERTY, old, comment); // NOI18N 1256 } 1257 } 1258 1259 public String getComment() { 1260 return _comment; 1261 } 1262 1263 protected void moveRollingStock(RouteLocation current, RouteLocation next) { 1264 if (current == getRouteLocation()) { 1265 setLastDate(java.util.Calendar.getInstance().getTime()); 1266 // Arriving at destination? 1267 if (getRouteLocation() == getRouteDestination() || next == null) { 1268 if (getRouteLocation() == getRouteDestination()) { 1269 log.debug("Rolling stock ({}) has arrived at destination ({})", this, getDestination()); 1270 } else { 1271 log.error("Rolling stock ({}) has a null route location for next", this); // NOI18N 1272 } 1273 setLocation(getDestination(), getDestinationTrack(), RollingStock.FORCE); // force RS to destination 1274 setDestination(null, null); // this also clears the route locations 1275 setTrain(null); // this must come after setDestination (route id is set) 1276 setMoves(getMoves() + 1); // bump count 1277 } else { 1278 log.debug("Rolling stock ({}) is in train ({}) leaves location ({}) destination ({})", this, 1279 getTrainName(), current.getName(), next.getName()); 1280 setLocation(next.getLocation(), null, RollingStock.FORCE); // force RS to location 1281 setRouteLocation(next); 1282 } 1283 } 1284 } 1285 1286 public void reset() { 1287 // the order of the next two instructions is important, otherwise rs will have 1288 // train's route id 1289 setTrain(null); 1290 setDestination(null, null); 1291 } 1292 1293 /** 1294 * Remove rolling stock. Releases all listeners. 1295 */ 1296 public void dispose() { 1297 setTrain(null); 1298 setDestination(null, null); 1299 setLocation(null, null); 1300 InstanceManager.getDefault(CarRoads.class).removePropertyChangeListener(this); 1301 InstanceManager.getDefault(CarOwners.class).removePropertyChangeListener(this); 1302 InstanceManager.getDefault(CarColors.class).removePropertyChangeListener(this); 1303 if (getIdTag() != null) { 1304 getIdTag().removePropertyChangeListener(_tagListener); 1305 } 1306 } 1307 1308 /** 1309 * Construct this Entry from XML. 1310 * 1311 * @param e RollingStock XML element 1312 */ 1313 public RollingStock(org.jdom2.Element e) { 1314 this(); 1315 org.jdom2.Attribute a; 1316 if ((a = e.getAttribute(Xml.ID)) != null) { 1317 _id = a.getValue(); 1318 } else { 1319 log.warn("no id attribute in rolling stock element when reading operations"); 1320 } 1321 if ((a = e.getAttribute(Xml.ROAD_NUMBER)) != null) { 1322 _number = a.getValue(); 1323 } 1324 if ((a = e.getAttribute(Xml.ROAD_NAME)) != null) { 1325 _road = a.getValue(); 1326 } 1327 if (_id == null || !_id.equals(createId(_road, _number))) { 1328 _id = createId(_road, _number); 1329 } 1330 if ((a = e.getAttribute(Xml.TYPE)) != null) { 1331 _type = a.getValue(); 1332 } 1333 if ((a = e.getAttribute(Xml.LENGTH)) != null) { 1334 _length = a.getValue(); 1335 } 1336 if ((a = e.getAttribute(Xml.COLOR)) != null) { 1337 _color = a.getValue(); 1338 } 1339 if ((a = e.getAttribute(Xml.WEIGHT)) != null) { 1340 _weight = a.getValue(); 1341 } 1342 if ((a = e.getAttribute(Xml.WEIGHT_TONS)) != null) { 1343 _weightTons = a.getValue(); 1344 } 1345 if ((a = e.getAttribute(Xml.BUILT)) != null) { 1346 _built = a.getValue(); 1347 } 1348 1349 Location location = null; 1350 Track track = null; 1351 if ((a = e.getAttribute(Xml.LOCATION_ID)) != null) { 1352 location = locationManager.getLocationById(a.getValue()); 1353 } 1354 if ((a = e.getAttribute(Xml.SEC_LOCATION_ID)) != null && location != null) { 1355 track = location.getTrackById(a.getValue()); 1356 } 1357 setLocation(location, track, RollingStock.FORCE); // force location 1358 1359 Location destination = null; 1360 track = null; 1361 if ((a = e.getAttribute(Xml.DESTINATION_ID)) != null) { 1362 destination = locationManager.getLocationById(a.getValue()); 1363 } 1364 if ((a = e.getAttribute(Xml.SEC_DESTINATION_ID)) != null && destination != null) { 1365 track = destination.getTrackById(a.getValue()); 1366 } 1367 setDestination(destination, track, true); // force destination 1368 1369 if ((a = e.getAttribute(Xml.DIVISION_ID)) != null) { 1370 _division = InstanceManager.getDefault(DivisionManager.class).getDivisionById(a.getValue()); 1371 } 1372 // TODO remove the following 3 lines in 2022 1373 if ((a = e.getAttribute(Xml.DIVISION_ID_ERROR)) != null) { 1374 _division = InstanceManager.getDefault(DivisionManager.class).getDivisionById(a.getValue()); 1375 } 1376 if ((a = e.getAttribute(Xml.MOVES)) != null) { 1377 try { 1378 _moves = Integer.parseInt(a.getValue()); 1379 } catch (NumberFormatException nfe) { 1380 log.error("Move count ({}) for rollingstock ({}) isn't a valid number!", a.getValue(), toString()); 1381 } 1382 } 1383 if ((a = e.getAttribute(Xml.LAST_LOCATION_ID)) != null) { 1384 _lastLocationId = a.getValue(); 1385 } 1386 if ((a = e.getAttribute(Xml.LAST_TRACK_ID)) != null) { 1387 _lastTrackId = a.getValue(); 1388 } 1389 if ((a = e.getAttribute(Xml.TRAIN_ID)) != null) { 1390 setTrain(InstanceManager.getDefault(TrainManager.class).getTrainById(a.getValue())); 1391 } else if ((a = e.getAttribute(Xml.TRAIN)) != null) { 1392 setTrain(InstanceManager.getDefault(TrainManager.class).getTrainByName(a.getValue())); 1393 } 1394 if (getTrain() != null && 1395 getTrain().getRoute() != null && 1396 (a = e.getAttribute(Xml.ROUTE_LOCATION_ID)) != null) { 1397 _routeLocation = getTrain().getRoute().getLocationById(a.getValue()); 1398 if ((a = e.getAttribute(Xml.ROUTE_DESTINATION_ID)) != null) { 1399 _routeDestination = getTrain().getRoute().getLocationById(a.getValue()); 1400 } 1401 } 1402 if ((a = e.getAttribute(Xml.LAST_ROUTE_ID)) != null) { 1403 _routeId = a.getValue(); 1404 } 1405 if ((a = e.getAttribute(Xml.OWNER)) != null) { 1406 _owner = a.getValue(); 1407 } 1408 if ((a = e.getAttribute(Xml.COMMENT)) != null) { 1409 _comment = a.getValue(); 1410 } 1411 if ((a = e.getAttribute(Xml.VALUE)) != null) { 1412 _value = a.getValue(); 1413 } 1414 if ((a = e.getAttribute(Xml.RFID)) != null) { 1415 setRfid(a.getValue()); 1416 } 1417 if ((a = e.getAttribute(Xml.LOC_UNKNOWN)) != null) { 1418 _locationUnknown = a.getValue().equals(Xml.TRUE); 1419 } 1420 if ((a = e.getAttribute(Xml.OUT_OF_SERVICE)) != null) { 1421 _outOfService = a.getValue().equals(Xml.TRUE); 1422 } 1423 if ((a = e.getAttribute(Xml.SELECTED)) != null) { 1424 _selected = a.getValue().equals(Xml.TRUE); 1425 } 1426 if ((a = e.getAttribute(Xml.DATE)) != null) { 1427 setLastDate(a.getValue()); // uses the setLastDate(String) method. 1428 } 1429 if ((a = e.getAttribute(Xml.BLOCKING)) != null) { 1430 try { 1431 _blocking = Integer.parseInt(a.getValue()); 1432 } catch (NumberFormatException nfe) { 1433 log.error("Blocking ({}) for rollingstock ({}) isn't a valid number!", a.getValue(), toString()); 1434 } 1435 } 1436 // check for rolling stock without a track assignment 1437 if (getLocation() != null && getTrack() == null && getTrain() == null) { 1438 log.warn("Rollingstock ({}) at ({}) doesn't have a track assignment", this, getLocationName()); 1439 } 1440 addPropertyChangeListeners(); 1441 } 1442 1443// boolean verboseStore = false; 1444 1445 /** 1446 * Add XML elements to represent this Entry. 1447 * 1448 * @param e Element for car or engine store. 1449 * 1450 * @return Contents in a JDOM Element 1451 */ 1452 protected org.jdom2.Element store(org.jdom2.Element e) { 1453 e.setAttribute(Xml.ID, getId()); 1454 e.setAttribute(Xml.ROAD_NAME, getRoadName()); 1455 e.setAttribute(Xml.ROAD_NUMBER, getNumber()); 1456 e.setAttribute(Xml.TYPE, getTypeName()); 1457 e.setAttribute(Xml.LENGTH, getLength()); 1458 if (!getColor().equals(NONE)) { 1459 e.setAttribute(Xml.COLOR, getColor()); 1460 } 1461 if (!getWeight().equals(DEFAULT_WEIGHT)) { 1462 e.setAttribute(Xml.WEIGHT, getWeight()); 1463 } 1464 if (!getWeightTons().equals(NONE)) { 1465 e.setAttribute(Xml.WEIGHT_TONS, getWeightTons()); 1466 } 1467 if (!getBuilt().equals(NONE)) { 1468 e.setAttribute(Xml.BUILT, getBuilt()); 1469 } 1470 if (!getLocationId().equals(NONE)) { 1471 e.setAttribute(Xml.LOCATION_ID, getLocationId()); 1472 } 1473 if (!getRouteLocationId().equals(NONE)) { 1474 e.setAttribute(Xml.ROUTE_LOCATION_ID, getRouteLocationId()); 1475 } 1476 if (!getTrackId().equals(NONE)) { 1477 e.setAttribute(Xml.SEC_LOCATION_ID, getTrackId()); 1478 } 1479 if (!getDestinationId().equals(NONE)) { 1480 e.setAttribute(Xml.DESTINATION_ID, getDestinationId()); 1481 } 1482 if (!getRouteDestinationId().equals(NONE)) { 1483 e.setAttribute(Xml.ROUTE_DESTINATION_ID, getRouteDestinationId()); 1484 } 1485 if (!getDestinationTrackId().equals(NONE)) { 1486 e.setAttribute(Xml.SEC_DESTINATION_ID, getDestinationTrackId()); 1487 } 1488 if (!getDivisionId().equals(NONE)) { 1489 e.setAttribute(Xml.DIVISION_ID, getDivisionId()); 1490 } 1491 if (!getLastRouteId().equals(NONE)) { 1492 e.setAttribute(Xml.LAST_ROUTE_ID, getLastRouteId()); 1493 } 1494// if (verboseStore) { 1495// e.setAttribute(Xml.LOCATION, getLocationName()); 1496// e.setAttribute(Xml.TRACK, getTrackName()); 1497// e.setAttribute(Xml.DESTINATION, getDestinationName()); 1498// e.setAttribute(Xml.DES_TRACK, getDestinationTrackName()); 1499// } 1500 e.setAttribute(Xml.MOVES, Integer.toString(getMoves())); 1501 e.setAttribute(Xml.DATE, getLastDate()); 1502 e.setAttribute(Xml.SELECTED, isSelected() ? Xml.TRUE : Xml.FALSE); 1503 if (!getLastLocationId().equals(LOCATION_UNKNOWN)) { 1504 e.setAttribute(Xml.LAST_LOCATION_ID, getLastLocationId()); 1505 } 1506 if (!getLastTrackId().equals(LOCATION_UNKNOWN)) { 1507 e.setAttribute(Xml.LAST_TRACK_ID, getLastTrackId()); 1508 } 1509 if (!getTrainName().equals(NONE)) { 1510 e.setAttribute(Xml.TRAIN, getTrainName()); 1511 e.setAttribute(Xml.TRAIN_ID, getTrain().getId()); 1512 } 1513 if (!getOwnerName().equals(NONE)) { 1514 e.setAttribute(Xml.OWNER, getOwnerName()); 1515 } 1516 if (!getValue().equals(NONE)) { 1517 e.setAttribute(Xml.VALUE, getValue()); 1518 } 1519 if (!getRfid().equals(NONE)) { 1520 e.setAttribute(Xml.RFID, getRfid()); 1521 } 1522 if (isLocationUnknown()) { 1523 e.setAttribute(Xml.LOC_UNKNOWN, isLocationUnknown() ? Xml.TRUE : Xml.FALSE); 1524 } 1525 if (isOutOfService()) { 1526 e.setAttribute(Xml.OUT_OF_SERVICE, isOutOfService() ? Xml.TRUE : Xml.FALSE); 1527 } 1528 if (getBlocking() != 0) { 1529 e.setAttribute(Xml.BLOCKING, Integer.toString(getBlocking())); 1530 } 1531 if (!getComment().equals(NONE)) { 1532 e.setAttribute(Xml.COMMENT, getComment()); 1533 } 1534 return e; 1535 } 1536 1537 private void addPropertyChangeListeners() { 1538 InstanceManager.getDefault(CarRoads.class).addPropertyChangeListener(this); 1539 InstanceManager.getDefault(CarOwners.class).addPropertyChangeListener(this); 1540 InstanceManager.getDefault(CarColors.class).addPropertyChangeListener(this); 1541 } 1542 1543 // rolling stock listens for changes in a location name or if a location is 1544 // deleted 1545 @Override 1546 public void propertyChange(PropertyChangeEvent e) { 1547 // log.debug("Property change for rolling stock: " + toString()+ " property 1548 // name: " 1549 // +e.getPropertyName()+ " old: "+e.getOldValue()+ " new: "+e.getNewValue()); 1550 // notify if track or location name changes 1551 if (e.getPropertyName().equals(Location.NAME_CHANGED_PROPERTY)) { 1552 log.debug("Property change for rolling stock: ({}) property name: ({}) old: ({}) new: ({})", this, 1553 e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1554 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1555 } 1556 if (e.getPropertyName().equals(Location.DISPOSE_CHANGED_PROPERTY)) { 1557 if (e.getSource() == getLocation()) { 1558 log.debug("delete location for rolling stock: ({})", this); 1559 setLocation(null, null); 1560 } 1561 if (e.getSource() == getDestination()) { 1562 log.debug("delete destination for rolling stock: ({})", this); 1563 setDestination(null, null); 1564 } 1565 } 1566 if (e.getPropertyName().equals(Track.DISPOSE_CHANGED_PROPERTY)) { 1567 if (e.getSource() == getTrack()) { 1568 log.debug("delete location for rolling stock: ({})", this); 1569 setLocation(getLocation(), null); 1570 } 1571 if (e.getSource() == getDestinationTrack()) { 1572 log.debug("delete destination for rolling stock: ({})", this); 1573 setDestination(getDestination(), null); 1574 } 1575 } 1576 if (e.getPropertyName().equals(Train.DISPOSE_CHANGED_PROPERTY) && e.getSource() == getTrain()) { 1577 log.debug("delete train for rolling stock: ({})", this); 1578 setTrain(null); 1579 } 1580 if (e.getPropertyName().equals(Train.TRAIN_LOCATION_CHANGED_PROPERTY) && e.getSource() == getTrain()) { 1581 log.debug("Rolling stock ({}) is serviced by train ({})", this, getTrainName()); 1582 moveRollingStock((RouteLocation) e.getOldValue(), (RouteLocation) e.getNewValue()); 1583 } 1584 if (e.getPropertyName().equals(Train.STATUS_CHANGED_PROPERTY) && 1585 e.getNewValue().equals(Train.TRAIN_RESET) && 1586 e.getSource() == getTrain()) { 1587 log.debug("Rolling stock ({}) is removed from train ({}) by reset", this, getTrainName()); // NOI18N 1588 reset(); 1589 } 1590 if (e.getPropertyName().equals(Train.NAME_CHANGED_PROPERTY)) { 1591 setDirtyAndFirePropertyChange(e.getPropertyName(), e.getOldValue(), e.getNewValue()); 1592 } 1593 if (e.getPropertyName().equals(CarRoads.CARROADS_NAME_CHANGED_PROPERTY)) { 1594 if (e.getOldValue().equals(getRoadName())) { 1595 log.debug("Rolling stock ({}) sees road name change from ({}) to ({})", this, e.getOldValue(), 1596 e.getNewValue()); // NOI18N 1597 if (e.getNewValue() != null) { 1598 setRoadName((String) e.getNewValue()); 1599 } 1600 } 1601 } 1602 if (e.getPropertyName().equals(CarOwners.CAROWNERS_NAME_CHANGED_PROPERTY)) { 1603 if (e.getOldValue().equals(getOwnerName())) { 1604 log.debug("Rolling stock ({}) sees owner name change from ({}) to ({})", this, e.getOldValue(), 1605 e.getNewValue()); // NOI18N 1606 setOwnerName((String) e.getNewValue()); 1607 } 1608 } 1609 if (e.getPropertyName().equals(CarColors.CARCOLORS_NAME_CHANGED_PROPERTY)) { 1610 if (e.getOldValue().equals(getColor())) { 1611 log.debug("Rolling stock ({}) sees color name change from ({}) to ({})", this, e.getOldValue(), 1612 e.getNewValue()); // NOI18N 1613 setColor((String) e.getNewValue()); 1614 } 1615 } 1616 } 1617 1618 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 1619 firePropertyChange(p, old, n); 1620 } 1621 1622 private final static Logger log = LoggerFactory.getLogger(RollingStock.class); 1623 1624}