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