001package jmri.jmrit.dispatcher; 002 003import java.beans.PropertyChangeListener; 004import java.beans.PropertyChangeSupport; 005import java.util.ArrayList; 006import java.util.Date; 007import java.util.List; 008 009import javax.annotation.OverridingMethodsMustInvokeSuper; 010import javax.annotation.Nonnull; 011 012import jmri.Block; 013import jmri.InstanceManager; 014import jmri.NamedBeanHandle; 015import jmri.Path; 016import jmri.Section; 017import jmri.Sensor; 018import jmri.Transit; 019import jmri.Transit.TransitType; 020import jmri.TransitSection; 021import jmri.Section.SectionType; 022import jmri.beans.PropertyChangeProvider; 023import jmri.jmrit.display.layoutEditor.LayoutBlock; 024import jmri.jmrit.display.layoutEditor.LayoutBlockManager; 025 026/** 027 * This class holds information and options for an ActiveTrain, that is a train 028 * that has been linked to a Transit and activated for transit around the 029 * layout. 030 * <p> 031 * An ActiveTrain may be assigned one of the following modes, which specify how 032 * the active train will be run through its transit: AUTOMATIC - indicates the 033 * ActiveTrain will be run under automatic control of the computer. (Automatic 034 * Running) MANUAL - indicates an ActiveTrain running in AUTOMATIC mode has 035 * reached a Special Action in its Transit that requires MANUAL operation. When 036 * this happens, the status changes to WORKING, and the mode changes to MANUAL. 037 * The ActiveTrain will be run by an operator using a throttle. AUTOMATIC 038 * running is resumed when the work has been completed. DISPATCHED - indicates 039 * the ActiveTrain will be run by an operator using a throttle. A dispatcher 040 * will allocate Sections to the ActiveTrain as needed, control optional signals 041 * using a CTC panel or computer logic, and arbitrate any conflicts between 042 * ActiveTrains. (Human Dispatcher). 043 * <p> 044 * An ActiveTrain will have one of the following statuses: 045 * <dl> 046 * <dt>RUNNING</dt><dd>Actively running on the layout, according to its mode of 047 * operation.</dd> 048 * <dt>PAUSED</dt><dd>Paused waiting for a user-specified number of fast clock 049 * minutes. The Active Train is expected to move to either RUNNING or WAITING 050 * once the specified number of minutes has elapsed. This is intended for 051 * automatic station stops. (automatic trains only)</dd> 052 * <dt>WAITING</dt><dd>Stopped waiting for a Section allocation. This is the 053 * state the Active Train is in when it is created in Dispatcher.</dd> 054 * <dt>WORKING</dt><dd>Performing work under control of a human engineer. This is 055 * the state an Active Train assumes when an engineer is picking up or setting 056 * out cars at industries. (automatic trains only)</dd> 057 * <dt>READY</dt><dd>Train has completed WORKING, and is awaiting a restart - 058 * dispatcher clearance to resume running. (automatic trains only)</dd> 059 * <dt>STOPPED</dt><dd>Train was stopped by the dispatcher. Dispatcher must 060 * resume. (automatic trains only)</dd> 061 * <dt>DONE</dt><dd>Train has completed its transit of the layout and is ready to 062 * be terminated by the dispatcher, or Restart pressed to repeat the automated 063 * run.</dd> 064 * </dl> 065 * Status is a bound property. 066 * <p> 067 * The ActiveTrain status should maintained (setStatus) by the running class, or 068 * if running in DISPATCHED mode, by Dispatcher. When an ActiveTrain is WAITING, 069 * and the dispatcher allocates a section to it, the status of the ActiveTrain 070 * is automatically set to RUNNING. So an autoRun class can listen to the status 071 * of the ActiveTrain to trigger start up if the train has been waiting for the 072 * dispatcher. Note: There is still more to be programmed here. 073 * <p> 074 * Train information supplied when the ActiveTrain is created can come from any 075 * of the following: 076 * <dl> 077 * <dt>ROSTER</dt><dd>The train was selected from the JMRI roster menu</dd> 078 * <dt>OPERATIONS</dt><dd>The train was selected from trains available from JMRI 079 * operations</dd> 080 * <dt>USER</dt><dd>Neither menu was used--the user entered a name and DCC 081 * address.</dd> 082 * </dl> 083 * Train source information is recorded when an ActiveTrain is created, 084 * and may be referenced by getTrainSource if it is needed by other objects. The 085 * train source should be specified in the Dispatcher Options window prior to 086 * creating an ActiveTrain. 087 * <p> 088 * ActiveTrains are referenced via a list in DispatcherFrame, which serves as a 089 * manager for ActiveTrain objects. 090 * <p> 091 * ActiveTrains are transient, and are not saved to disk. Active Train 092 * information can be saved to disk, making set up with the same options, etc 093 * very easy. 094 * <p> 095 * An ActiveTrain runs through its Transit in the FORWARD direction, until a 096 * Transit Action reverses the direction of travel in the Transit. When running 097 * with its Transit reversed, the Active Train returns to its starting Section. 098 * Upon reaching and stopping in its starting Section, the Transit is 099 * automatically set back to the forward direction. If AutoRestart is set, the 100 * run is repeated. The direction of travel in the Transit is maintained here. 101 * 102 * <p> 103 * This file is part of JMRI. 104 * <p> 105 * JMRI is open source software; you can redistribute it and/or modify it under 106 * the terms of version 2 of the GNU General Public License as published by the 107 * Free Software Foundation. See the "COPYING" file for a copy of this license. 108 * <p> 109 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 110 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 111 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 112 * 113 * @author Dave Duchamp Copyright (C) 2008-2011 114 */ 115public class ActiveTrain implements PropertyChangeProvider { 116 117 private static final jmri.NamedBean.DisplayOptions USERSYS = jmri.NamedBean.DisplayOptions.USERNAME_SYSTEMNAME; 118 119 /** 120 * Create an ActiveTrain. 121 * 122 * @param t the transit linked to this ActiveTrain 123 * @param name the train name 124 * @param trainSource the source for this ActiveTrain 125 */ 126 public ActiveTrain(Transit t, String name, int trainSource) { 127 mTransit = t; 128 mTrainName = name; 129 mTrainSource = trainSource; 130 } 131 132 /** 133 * Constants representing the Status of this ActiveTrain When created, the 134 * Status of an Active Train is always WAITING, 135 */ 136 public static final int RUNNING = 0x01; // running on the layout 137 public static final int PAUSED = 0x02; // paused for a number of fast minutes 138 public static final int WAITING = 0x04; // waiting for a section allocation 139 public static final int WORKING = 0x08; // actively working 140 public static final int READY = 0x10; // completed work, waiting for restart 141 public static final int STOPPED = 0x20; // stopped by the dispatcher (auto trains only) 142 public static final int DONE = 0x40; // completed its transit 143 144 /** 145 * Constants representing Type of ActiveTrains. 146 */ 147 public static final int NONE = 0x00; // no train type defined 148 public static final int LOCAL_PASSENGER = 0x01; // low priority local passenger train 149 public static final int LOCAL_FREIGHT = 0x02; // low priority freight train performing local tasks 150 public static final int THROUGH_PASSENGER = 0x03; // normal priority through passenger train 151 public static final int THROUGH_FREIGHT = 0x04; // normal priority through freight train 152 public static final int EXPRESS_PASSENGER = 0x05; // high priority passenger train 153 public static final int EXPRESS_FREIGHT = 0x06; // high priority freight train 154 public static final int MOW = 0x07; // low priority maintenance of way train 155 156 /** 157 * Constants representing the mode of running of the Active Train The mode 158 * is set when the Active Train is created. The mode may be switched during 159 * a run. 160 */ 161 public static final int AUTOMATIC = 0x02; // requires mAutoRun to be "true" (auto trains only) 162 public static final int MANUAL = 0x04; // requires mAutoRun to be "true" (auto trains only) 163 public static final int DISPATCHED = 0x08; 164 public static final int TERMINATED = 0x10; //terminated 165 166 /** 167 * Constants representing the source of the train information 168 */ 169 public static final int ROSTER = 0x01; 170 public static final int OPERATIONS = 0x02; 171 public static final int USER = 0x04; 172 173 /** 174 * The value of {@link #getAllocateMethod()} if allocating as many sections as are clear. 175 */ 176 public static final int ALLOCATE_AS_FAR_AS_IT_CAN = -1; 177 /** 178 * The value of {@link #getAllocateMethod()} if allocating up until the next safe section 179 */ 180 public static final int ALLOCATE_BY_SAFE_SECTIONS = 0; 181 182 /** 183 * String property constant for status. 184 */ 185 public static final String PROPERTY_STATUS = "status"; 186 187 /** 188 * String property constant for mode. 189 */ 190 public static final String PROPERTY_MODE = "mode"; 191 192 /** 193 * String property constant for signal. 194 */ 195 public static final String PROPERTY_SIGNAL = "signal"; 196 197 /** 198 * String property constant for section allocated. 199 */ 200 public static final String PROPERTY_SECTION_ALLOCATED = "sectionallocated"; 201 202 /** 203 * String property constant for section de-allocated. 204 */ 205 public static final String PROPERTY_SECTION_DEALLOCATED = "sectiondeallocated"; 206 207 /** 208 * How much of the train can be detected 209 */ 210 public enum TrainDetection { 211 TRAINDETECTION_WHOLETRAIN, 212 TRAINDETECTION_HEADONLY, 213 TRAINDETECTION_HEADANDTAIL 214 } 215 216 /** 217 * Scale Length type 218 */ 219 public enum TrainLengthUnits { 220 TRAINLENGTH_SCALEFEET, 221 TRAINLENGTH_SCALEMETERS, 222 TRAINLENGTH_ACTUALINCHS, 223 TRAINLENGTH_ACTUALCM 224 } 225 226 // instance variables 227 private DispatcherFrame mDispatcher = null; 228 private Transit mTransit = null; 229 private String mTrainName = ""; 230 private int mTrainSource = ROSTER; 231 private jmri.jmrit.roster.RosterEntry mRoster = null; 232 private int mStatus = WAITING; 233 private int mMode = DISPATCHED; 234 private boolean mTransitReversed = false; // true if Transit is running in reverse 235 private boolean mAllocationReversed = false; // true if allocating Sections in reverse 236 private AutoActiveTrain mAutoActiveTrain = null; 237 private final List<AllocatedSection> mAllocatedSections = new ArrayList<>(); 238 private Section mLastAllocatedSection = null; 239 private Section mLastAllocOverrideSafe = null; 240 private int mLastAllocatedSectionSeqNumber = 0; 241 private Section mSecondAllocatedSection = null; 242 private int mNextAllocationNumber = 1; 243 private Section mNextSectionToAllocate = null; 244 private int mNextSectionSeqNumber = 0; 245 private int mNextSectionDirection = 0; 246 private Block mStartBlock = null; 247 private int mStartBlockSectionSequenceNumber = 0; 248 private Block mEndBlock = null; 249 private Section mEndBlockSection = null; 250 private int mEndBlockSectionSequenceNumber = 0; 251 private int mPriority = 0; 252 private boolean mAutoRun = false; 253 private String mDccAddress = ""; 254 private boolean mResetWhenDone = true; 255 private boolean mReverseAtEnd = false; 256 private int mAllocateMethod = 3; 257 public static final int NODELAY = 0x00; 258 public static final int TIMEDDELAY = 0x01; 259 public static final int SENSORDELAY = 0x02; 260 private TrainDetection trainDetection = TrainDetection.TRAINDETECTION_HEADONLY; 261 262 private int mDelayedRestart = NODELAY; 263 private int mDelayedStart = NODELAY; 264 private int mDepartureTimeHr = 8; 265 private int mDepartureTimeMin = 0; 266 private int mRestartDelay = 0; 267 private NamedBeanHandle<Sensor> mStartSensor = null; // A Sensor that when changes state to active will trigger the trains start. 268 private boolean resetStartSensor = true; 269 private NamedBeanHandle<Sensor> mRestartSensor = null; // A Sensor that when changes state to active will trigger the trains restart. 270 private boolean resetRestartSensor = true; 271 private NamedBeanHandle<Sensor> mReverseRestartSensor = null; // A Sensor that when changes state to active will trigger the trains restart. 272 private boolean resetReverseRestartSensor = true; 273 private int mDelayReverseRestart = NODELAY; 274 private int mTrainType = LOCAL_FREIGHT; 275 private boolean terminateWhenFinished = false; 276 private String mNextTrain = ""; 277 private int mSignalType; 278 // Runtime/config flag: whether to honour section stop sensors 279 private boolean useStopSensor = true; 280 281 // start up instance variables 282 private boolean mStarted = false; 283 284 // 285 // Access methods 286 // 287 public boolean getStarted() { 288 return mStarted; 289 } 290 291 public void setDispatcher(DispatcherFrame df) { 292 mDispatcher = df; 293 mSignalType = df.getSignalType(); 294 if (mTransit.getTransitType() == TransitType.DYNAMICADHOC) { 295 mSignalType = DispatcherFrame.SECTIONSALLOCATED; 296 } 297 } 298 299 public void setStarted() { 300 mStarted = true; 301 mStatus = RUNNING; 302 holdAllocation(false); 303 setStatus(WAITING); 304 if (mAutoActiveTrain != null && mDispatcher.getSignalType() == DispatcherFrame.SIGNALMAST) { 305 mAutoActiveTrain.setupNewCurrentSignal(null,false); 306 } 307 } 308 309 public Transit getTransit() { 310 return mTransit; 311 } 312 313 public String getTransitName() { 314 return mTransit.getDisplayName(); 315 } 316 317 public String getActiveTrainName() { 318 return (mTrainName + " / " + getTransitName()); 319 } 320 321 // Note: Transit and Train may not be changed once an ActiveTrain is created. 322 public String getTrainName() { 323 return mTrainName; 324 } 325 326 public int getTrainSource() { 327 return mTrainSource; 328 } 329 330 public void setRosterEntry(jmri.jmrit.roster.RosterEntry re) { 331 mRoster = re; 332 } 333 334 public boolean getUseStopSensor() { 335 return useStopSensor; 336 } 337 338 public void setUseStopSensor(boolean value) { 339 useStopSensor = value; 340 } 341 342 public jmri.jmrit.roster.RosterEntry getRosterEntry() { 343 if (mRoster == null && getTrainSource() == ROSTER) { 344 //Try to resolve the roster based upon the train name 345 mRoster = jmri.jmrit.roster.Roster.getDefault().getEntryForId(getTrainName()); 346 } else if (getTrainSource() != ROSTER) { 347 mRoster = null; 348 } 349 return mRoster; 350 } 351 352 public int getStatus() { 353 return mStatus; 354 } 355 356 public void setStatus(int status) { 357 if (restartPoint) { 358 return; 359 } 360 if ((status == RUNNING) || (status == PAUSED) || (status == WAITING) || (status == WORKING) 361 || (status == READY) || (status == STOPPED) || (status == DONE)) { 362 if (mStatus != status) { 363 int old = mStatus; 364 mStatus = status; 365 firePropertyChange(PROPERTY_STATUS, old, mStatus); 366 if (mStatus == DONE) { 367 mDispatcher.terminateActiveTrain(this,terminateWhenFinished,true); 368 } 369 } 370 } else { 371 log.error("Invalid ActiveTrain status - {}", status); 372 } 373 } 374 375 public void setControlingSignal(Object oldSignal, Object newSignal) { 376 firePropertyChange(PROPERTY_SIGNAL, oldSignal, newSignal); 377 } 378 379 public String getStatusText() { 380 if (mStatus == RUNNING) { 381 return Bundle.getMessage("RUNNING"); 382 } else if (mStatus == PAUSED) { 383 return Bundle.getMessage("PAUSED"); 384 } else if (mStatus == WAITING) { 385 if (!mStarted) { 386 if (mDelayedStart == TIMEDDELAY) { 387 return jmri.jmrit.beantable.LogixTableAction.formatTime(mDepartureTimeHr, 388 mDepartureTimeMin) + " " + Bundle.getMessage("START"); 389 } else if (mDelayedStart == SENSORDELAY) { 390 return (Bundle.getMessage("BeanNameSensor") + " " + getDelaySensorName()); 391 } 392 } 393 return Bundle.getMessage("WAITING"); 394 } else if (mStatus == WORKING) { 395 return Bundle.getMessage("WORKING"); 396 } else if (mStatus == READY) { 397 if (restartPoint && getDelayedRestart() == TIMEDDELAY) { 398 return jmri.jmrit.beantable.LogixTableAction.formatTime(restartHr, 399 restartMin) + " " + Bundle.getMessage("START"); 400 } else if (restartPoint && getDelayedRestart() == SENSORDELAY) { 401 return (Bundle.getMessage("BeanNameSensor") + " " + getRestartSensorName()); 402 } 403 return Bundle.getMessage("READY"); 404 } else if (mStatus == STOPPED) { 405 return Bundle.getMessage("STOPPED"); 406 } else if (mStatus == DONE) { 407 return Bundle.getMessage("DONE"); 408 } 409 return (""); 410 } 411 412 /** 413 * sets the train detection type 414 * @param value {@link ActiveTrain.TrainDetection} 415 */ 416 public void setTrainDetection(TrainDetection value) { 417 trainDetection = value; 418 } 419 420 /** 421 * Gets the train detection type 422 * @return {@link ActiveTrain.TrainDetection} 423 */ 424 public TrainDetection getTrainDetection() { 425 return trainDetection; 426 } 427 428 public boolean isTransitReversed() { 429 return mTransitReversed; 430 } 431 432 public void setTransitReversed(boolean set) { 433 mTransitReversed = set; 434 } 435 436 public boolean isAllocationReversed() { 437 return mAllocationReversed; 438 } 439 440 public void setAllocationReversed(boolean set) { 441 mAllocationReversed = set; 442 } 443 444 public int getDelayedStart() { 445 return mDelayedStart; 446 } 447 448 public void setNextTrain(String nextTrain) { 449 mNextTrain = nextTrain; 450 } 451 452 public String getNextTrain() { 453 return mNextTrain; 454 } 455 456 public void setDelayedStart(int delay) { 457 mDelayedStart = delay; 458 } 459 460 public int getDelayedRestart() { 461 return mDelayedRestart; 462 } 463 464 public void setDelayedRestart(int delay) { 465 mDelayedRestart = delay; 466 } 467 468 public int getDelayReverseRestart() { 469 return mDelayReverseRestart; 470 } 471 472 public void setReverseDelayRestart(int delay) { 473 mDelayReverseRestart = delay; 474 } 475 476 public int getDepartureTimeHr() { 477 return mDepartureTimeHr; 478 } 479 480 public void setDepartureTimeHr(int hr) { 481 mDepartureTimeHr = hr; 482 } 483 484 public int getDepartureTimeMin() { 485 return mDepartureTimeMin; 486 } 487 488 public void setDepartureTimeMin(int min) { 489 mDepartureTimeMin = min; 490 } 491 492 public void setRestartDelay(int min) { 493 mRestartDelay = min; 494 } 495 496 public int getRestartDelay() { 497 return mRestartDelay; 498 } 499 500 int mReverseRestartDelay; 501 public int getReverseRestartDelay() { 502 return mReverseRestartDelay; 503 } 504 public void setReverseRestartDelay(int min) { 505 mReverseRestartDelay = min; 506 } 507 508 int restartHr = 0; 509 int restartMin = 0; 510 511 public int getRestartDepartHr() { 512 return restartHr; 513 } 514 515 public int getRestartDepartMin() { 516 return restartMin; 517 } 518 519 public void setTerminateWhenDone(boolean boo) { 520 terminateWhenFinished = boo; 521 } 522 523 public Sensor getDelaySensor() { 524 if (mStartSensor == null) { 525 return null; 526 } 527 return mStartSensor.getBean(); 528 } 529 530 public String getDelaySensorName() { 531 if (mStartSensor == null) { 532 return null; 533 } 534 return mStartSensor.getName(); 535 } 536 537 public void setDelaySensor(Sensor s) { 538 if (s == null) { 539 mStartSensor = null; 540 return; 541 } 542 mStartSensor = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(s.getDisplayName(), s); 543 } 544 545 public void setResetStartSensor(boolean b) { 546 resetStartSensor = b; 547 } 548 549 public boolean getResetStartSensor() { 550 return resetStartSensor; 551 } 552 553 public Sensor getReverseRestartSensor() { 554 if (mReverseRestartSensor == null) { 555 return null; 556 } 557 return mReverseRestartSensor.getBean(); 558 } 559 560 public String getReverseRestartSensorName() { 561 if (mReverseRestartSensor == null) { 562 return null; 563 } 564 return mReverseRestartSensor.getName(); 565 } 566 567 public void setReverseDelaySensor(Sensor s) { 568 if (s == null) { 569 mReverseRestartSensor = null; 570 return; 571 } 572 mReverseRestartSensor = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(s.getDisplayName(), s); 573 } 574 575 public void setReverseResetRestartSensor(boolean b) { 576 resetReverseRestartSensor = b; 577 } 578 579 public boolean getResetReverseRestartSensor() { 580 return resetReverseRestartSensor; 581 } 582 583 public Sensor getRestartSensor() { 584 if (mRestartSensor == null) { 585 return null; 586 } 587 return mRestartSensor.getBean(); 588 } 589 590 public String getRestartSensorName() { 591 if (mRestartSensor == null) { 592 return null; 593 } 594 return mRestartSensor.getName(); 595 } 596 597 public void setRestartSensor(Sensor s) { 598 if (s == null) { 599 mRestartSensor = null; 600 return; 601 } 602 mRestartSensor = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(s.getDisplayName(), s); 603 } 604 605 public void setResetRestartSensor(boolean b) { 606 resetRestartSensor = b; 607 } 608 609 public boolean getResetRestartSensor() { 610 return resetRestartSensor; 611 } 612 613 public int getSignalType() { 614 return mSignalType; 615 } 616 617 private java.beans.PropertyChangeListener delaySensorListener = null; 618 private java.beans.PropertyChangeListener restartSensorListener = null; 619 private java.beans.PropertyChangeListener restartAllocationSensorListener = null; 620 621 public void initializeDelaySensor() { 622 if (mStartSensor == null) { 623 log.error("Call to initialise delay on start sensor, but none specified"); 624 return; 625 } 626 if (delaySensorListener == null) { 627 final ActiveTrain at = this; 628 delaySensorListener = e -> { 629 if (Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName()) 630 && ((Integer) e.getNewValue()) == Sensor.ACTIVE) { 631 getDelaySensor().removePropertyChangeListener(delaySensorListener); 632 mDispatcher.removeDelayedTrain(at); 633 setStarted(); 634 mDispatcher.queueScanOfAllocationRequests(); 635 if (resetStartSensor) { 636 try { 637 getDelaySensor().setKnownState(Sensor.INACTIVE); 638 log.debug("Start sensor {} set back to inActive", getDelaySensor().getDisplayName(USERSYS)); 639 } catch (jmri.JmriException ex) { 640 log.error("Error resetting start sensor {} back to inActive", 641 getDelaySensor().getDisplayName(USERSYS)); 642 } 643 } 644 } 645 }; 646 } 647 getDelaySensor().addPropertyChangeListener(delaySensorListener); 648 } 649 650 public void initializeRestartSensor(Sensor restartSensor, boolean resetSensor) { 651 if (restartSensor == null) { 652 log.error("Call to initialise delay on restart sensor, but none specified"); 653 return; 654 } 655 if (restartSensorListener == null) { 656 final ActiveTrain at = this; 657 restartSensorListener = e -> { 658 if (Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName()) 659 && ((Integer) e.getNewValue()) == Sensor.ACTIVE) { 660 restartSensor.removePropertyChangeListener(restartSensorListener); 661 restartSensorListener = null; 662 mDispatcher.removeDelayedTrain(at); 663 restart(); 664 mDispatcher.queueScanOfAllocationRequests(); 665 if (resetSensor) { 666 try { 667 restartSensor.setKnownState(Sensor.INACTIVE); 668 log.debug("Restart sensor {} set back to inActive", 669 getRestartSensor().getDisplayName(USERSYS)); 670 } catch (jmri.JmriException ex) { 671 log.error("Error resetting restart sensor back to inActive"); 672 } 673 } 674 } 675 }; 676 } 677 restartSensor.addPropertyChangeListener(restartSensorListener); 678 } 679 680 public void initializeRestartAllocationSensor(NamedBeanHandle<Sensor> restartAllocationSensor) { 681 if (restartAllocationSensor == null) { 682 log.error("Call to initialise delay on restart allocation sensor, but none specified"); 683 return; 684 } 685 if (restartAllocationSensorListener == null) { 686 restartAllocationSensorListener = e -> { 687 if (Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName()) 688 && (((Integer) e.getNewValue()) == Sensor.INACTIVE)) { 689 restartAllocationSensor.getBean().removePropertyChangeListener(restartAllocationSensorListener); 690 restartAllocationSensorListener = null; 691 mDispatcher.queueScanOfAllocationRequests(); 692 } 693 }; 694 } 695 restartAllocationSensor.getBean().addPropertyChangeListener(restartAllocationSensorListener); 696 } 697 698 public void setTrainType(int type) { 699 mTrainType = type; 700 } 701 702 /** 703 * set train type using localized string name as stored 704 * 705 * @param sType name, such as "LOCAL_PASSENGER" 706 */ 707 public void setTrainType(String sType) { 708 if (sType.equals(Bundle.getMessage("LOCAL_FREIGHT"))) { 709 setTrainType(LOCAL_FREIGHT); 710 } else if (sType.equals(Bundle.getMessage("LOCAL_PASSENGER"))) { 711 setTrainType(LOCAL_PASSENGER); 712 } else if (sType.equals(Bundle.getMessage("THROUGH_FREIGHT"))) { 713 setTrainType(THROUGH_FREIGHT); 714 } else if (sType.equals(Bundle.getMessage("THROUGH_PASSENGER"))) { 715 setTrainType(THROUGH_PASSENGER); 716 } else if (sType.equals(Bundle.getMessage("EXPRESS_FREIGHT"))) { 717 setTrainType(EXPRESS_FREIGHT); 718 } else if (sType.equals(Bundle.getMessage("EXPRESS_PASSENGER"))) { 719 setTrainType(EXPRESS_PASSENGER); 720 } else if (sType.equals(Bundle.getMessage("MOW"))) { 721 setTrainType(MOW); 722 } 723 } 724 725 public int getTrainType() { 726 return mTrainType; 727 } 728 729 public String getTrainTypeText() { 730 if (mTrainType == LOCAL_FREIGHT) { 731 return Bundle.getMessage("LOCAL_FREIGHT"); 732 } else if (mTrainType == LOCAL_PASSENGER) { 733 return Bundle.getMessage("LOCAL_PASSENGER"); 734 } else if (mTrainType == THROUGH_FREIGHT) { 735 return Bundle.getMessage("THROUGH_FREIGHT"); 736 } else if (mTrainType == THROUGH_PASSENGER) { 737 return Bundle.getMessage("THROUGH_PASSENGER"); 738 } else if (mTrainType == EXPRESS_FREIGHT) { 739 return Bundle.getMessage("EXPRESS_FREIGHT"); 740 } else if (mTrainType == EXPRESS_PASSENGER) { 741 return Bundle.getMessage("EXPRESS_PASSENGER"); 742 } else if (mTrainType == MOW) { 743 return Bundle.getMessage("MOW"); 744 } 745 return (""); 746 } 747 748 public int getMode() { 749 return mMode; 750 } 751 752 public void forcePassNextSafeSection() { 753 for (AllocatedSection as: mAllocatedSections) { 754 if (as.getTransitSection().getSection() == mLastAllocatedSection 755 && as.getTransitSection().isSafe() 756 && as.getNextSection().getOccupancy() == Section.UNOCCUPIED) { 757 mLastAllocOverrideSafe = mLastAllocatedSection; 758 } 759 } 760 } 761 762 public void setMode(int mode) { 763 if ((mode == AUTOMATIC) || (mode == MANUAL) 764 || (mode == DISPATCHED || mode == TERMINATED)) { 765 int old = mMode; 766 mMode = mode; 767 firePropertyChange(PROPERTY_MODE, old, mMode); 768 } else { 769 log.error("Attempt to set ActiveTrain mode to illegal value - {}", mode); 770 } 771 } 772 773 @Nonnull 774 public String getModeText() { 775 switch (mMode) { 776 case AUTOMATIC: 777 return Bundle.getMessage("AUTOMATIC"); 778 case MANUAL: 779 return Bundle.getMessage("MANUAL"); 780 case DISPATCHED: 781 return Bundle.getMessage("DISPATCHED"); 782 case TERMINATED: 783 return Bundle.getMessage("TERMINATED"); 784 default: 785 return ""; 786 } 787 } 788 789 public void setAutoActiveTrain(AutoActiveTrain aat) { 790 mAutoActiveTrain = aat; 791 } 792 793 public AutoActiveTrain getAutoActiveTrain() { 794 return mAutoActiveTrain; 795 } 796 797 public int getRunningDirectionFromSectionAndSeq(Section s, int seqNo) { 798 int dir = mTransit.getDirectionFromSectionAndSeq(s, seqNo); 799 if (mTransitReversed) { 800 if (dir == Section.FORWARD) { 801 dir = Section.REVERSE; 802 } else { 803 dir = Section.FORWARD; 804 } 805 } 806 return dir; 807 } 808 809 public int getAllocationDirectionFromSectionAndSeq(Section s, int seqNo) { 810 int dir = mTransit.getDirectionFromSectionAndSeq(s, seqNo); 811 if (mAllocationReversed) { 812 if (dir == Section.FORWARD) { 813 dir = Section.REVERSE; 814 } else { 815 dir = Section.FORWARD; 816 } 817 } 818 return dir; 819 } 820 821 public void addAllocatedSection(AllocatedSection as) { 822 if (as != null) { 823 mAllocatedSections.add(as); 824 if (as.getSection() == mNextSectionToAllocate) { 825 // this is the next Section in the Transit, update pointers 826 mLastAllocatedSection = as.getSection(); 827 mLastAllocOverrideSafe = null; 828 mLastAllocatedSectionSeqNumber = mNextSectionSeqNumber; 829 mNextSectionToAllocate = as.getNextSection(); 830 mNextSectionSeqNumber = as.getNextSectionSequence(); 831 mNextSectionDirection = getAllocationDirectionFromSectionAndSeq( 832 mNextSectionToAllocate, mNextSectionSeqNumber); 833 as.setAllocationNumber(mNextAllocationNumber); 834 mNextAllocationNumber++; 835 } else { 836 // this is an extra allocated Section 837 as.setAllocationNumber(-1); 838 } 839 if ((mStatus == WAITING) && mStarted) { 840 setStatus(RUNNING); 841 } 842 if (as.getSequence() == 2) { 843 mSecondAllocatedSection = as.getSection(); 844 } 845 if (mDispatcher.getNameInAllocatedBlock()) { 846 if (mDispatcher.getRosterEntryInBlock() && getRosterEntry() != null) { 847 as.getSection().setNameFromActiveBlock(getRosterEntry()); 848 } else { 849 as.getSection().setNameInBlocks(mTrainName); 850 } 851 as.getSection().suppressNameUpdate(true); 852 } 853 if (mDispatcher.getExtraColorForAllocated()) { 854 as.getSection().setAlternateColorFromActiveBlock(true); 855 } 856 // notify anyone interested 857 firePropertyChange(PROPERTY_SECTION_ALLOCATED,as , null); 858 refreshPanel(); 859 } else { 860 log.error("Null Allocated Section reference in addAllocatedSection of ActiveTrain"); 861 } 862 } 863 864 private void refreshPanel() { 865 var editorManager = InstanceManager.getDefault(jmri.jmrit.display.EditorManager.class); 866 for (var panel : editorManager.getAll(jmri.jmrit.display.layoutEditor.LayoutEditor.class)) { 867 panel.redrawPanel(); 868 } 869 } 870 871 public void removeAllocatedSection(AllocatedSection as) { 872 if (as == null) { 873 log.error("Null AllocatedSection reference in removeAllocatedSection of ActiveTrain"); 874 return; 875 } 876 int index = -1; 877 for (int i = 0; i < mAllocatedSections.size(); i++) { 878 if (as == mAllocatedSections.get(i)) { 879 index = i; 880 } 881 } 882 if (index < 0) { 883 log.error("Attempt to remove an unallocated Section {}", as.getSection().getDisplayName(USERSYS)); 884 return; 885 } 886 mAllocatedSections.remove(index); 887 if (mDispatcher.getNameInAllocatedBlock()) { 888 as.getSection().clearNameInUnoccupiedBlocks(); 889 as.getSection().suppressNameUpdate(false); 890 } 891 for (Block b: as.getSection().getBlockList()) { 892 if (!mDispatcher.checkForBlockInAllocatedSection(b, as.getSection())) { 893 String userName = b.getUserName(); 894 if (userName != null) { 895 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 896 if (lb != null) { 897 lb.setUseExtraColor(false); 898 } 899 } 900 } 901 } 902 // notify anyone interested 903 firePropertyChange(PROPERTY_SECTION_DEALLOCATED,as , null); 904 refreshPanel(); 905 if (as.getSection() == mLastAllocatedSection) { 906 mLastAllocatedSection = null; 907 mLastAllocOverrideSafe = null; 908 if (!mAllocatedSections.isEmpty()) { 909 mLastAllocatedSection = mAllocatedSections.get( 910 mAllocatedSections.size() - 1).getSection(); 911 mLastAllocatedSectionSeqNumber = mAllocatedSections.size() - 1; 912 } 913 } 914 } 915 916 /** 917 * This resets the state of the ActiveTrain so that it can be reallocated. 918 */ 919 public void allocateAFresh() { 920 setStatus(WAITING); 921 holdAllocation = false; 922 setTransitReversed(false); 923 List<AllocatedSection> sectionsToRelease = new ArrayList<>(); 924 for (AllocatedSection as : mDispatcher.getAllocatedSectionsList()) { 925 if (as.getActiveTrain() == this) { 926 sectionsToRelease.add(as); 927 } 928 } 929 for (AllocatedSection as : sectionsToRelease) { 930 mDispatcher.releaseAllocatedSection(as, true); // need to find Allocated Section 931 mDispatcher.queueWaitForEmpty(); //ensure release processed before proceding. 932 as.getSection().setState(Section.FREE); 933 } 934 if (mLastAllocatedSection != null) { 935 mLastAllocatedSection.setState(Section.FREE); 936 } 937 resetAllAllocatedSections(); 938 clearAllocations(); 939 setAllocationReversed(false); 940 // wait for AutoAllocate to do complete. 941 mDispatcher.queueWaitForEmpty(); 942 if (mAutoRun) { 943 mAutoActiveTrain.allocateAFresh(); 944 } 945 mDispatcher.allocateNewActiveTrain(this); 946 } 947 948 public void clearAllocations() { 949 for (AllocatedSection as : getAllocatedSectionList()) { 950 removeAllocatedSection(as); 951 } 952 } 953 954 public List<AllocatedSection> getAllocatedSectionList() { 955 List<AllocatedSection> list = new ArrayList<>(); 956 for (int i = 0; i < mAllocatedSections.size(); i++) { 957 list.add(mAllocatedSections.get(i)); 958 } 959 return list; 960 } 961 962 /** 963 * Returns list of all Blocks occupied by or allocated to this train. They 964 * are in order from the tail of the train to the head of the train then on 965 * to the forward-most allocated block. Note that unoccupied blocks can 966 * exist before and after the occupied blocks. 967 * 968 * TODO: doesn't handle reversing of adjacent multi-block sections well 969 * 970 * @return the list of blocks order of occupation 971 */ 972 public List<Block> getBlockList() { 973 List<Block> list = new ArrayList<>(); 974 for (int i = 0; i < mAllocatedSections.size(); i++) { // loop thru allocated sections, then all blocks for each section 975 Section s = mAllocatedSections.get(i).getSection(); 976 List<Block> bl = s.getBlockList(); 977 if (bl.size() > 1) { //sections with multiple blocks need extra logic 978 979 boolean blocksConnected = true; 980 //determine if blocks should be added in forward or reverse order based on connectivity 981 if (i == 0) { //for first section, compare last block to first of next section 982 if (mAllocatedSections.size() > 1 983 && //only one section, assume forward 984 !connected(bl.get(bl.size() - 1), mAllocatedSections.get(i + 1).getSection().getBlockList().get(0))) { 985 blocksConnected = false; 986 } 987 } else { //not first section, check for connectivity between last block in list, and first block in this section 988 if (!connected(list.get(list.size() - 1), bl.get(0))) { //last block is not connected to first block, add reverse 989 blocksConnected = false; 990 } 991 } 992 if (blocksConnected) { //blocks were connected, so add to outgoing in forward order 993 for (int j = 0; j < bl.size(); j++) { 994 Block b = bl.get(j); 995 list.add(b); 996 log.trace("block {} ({}) added to list for Section {} (fwd)", b.getDisplayName(USERSYS), 997 (b.getState() == Block.OCCUPIED ? "OCCUPIED" : "UNOCCUPIED"), 998 s.getDisplayName(USERSYS)); 999 } 1000 } else { //not connected, add in reverse order 1001 for (int j = bl.size() - 1; j >= 0; j--) { 1002 Block b = bl.get(j); 1003 list.add(b); 1004 log.trace("block {} ({}) added to list for Section {} (rev)", b.getDisplayName(USERSYS), 1005 (b.getState() == Block.OCCUPIED ? "OCCUPIED" : "UNOCCUPIED"), 1006 s.getDisplayName(USERSYS)); 1007 } 1008 } 1009 1010 } else { //single block sections are simply added to the outgoing list 1011 Block b = bl.get(0); 1012 list.add(b); 1013 log.trace("block {} ({}) added to list for Section {} (one)", b.getDisplayName(USERSYS), 1014 (b.getState() == Block.OCCUPIED ? "OCCUPIED" : "UNOCCUPIED"), 1015 s.getDisplayName(USERSYS)); 1016 } 1017 } 1018 return list; 1019 } 1020 1021 /* copied from Section.java */ 1022 private boolean connected(Block b1, Block b2) { 1023 if ((b1 != null) && (b2 != null)) { 1024 List<Path> paths = b1.getPaths(); 1025 for (int i = 0; i < paths.size(); i++) { 1026 if (paths.get(i).getBlock() == b2) { 1027 return true; 1028 } 1029 } 1030 } 1031 return false; 1032 } 1033 1034 public Section getLastAllocatedSection() { 1035 return mLastAllocatedSection; 1036 } 1037 1038 public Section getLastAllocOverrideSafe() { 1039 return mLastAllocOverrideSafe; 1040 } 1041 1042 public int getLastAllocatedSectionSeqNumber() { 1043 return mLastAllocatedSectionSeqNumber; 1044 } 1045 1046 public String getLastAllocatedSectionName() { 1047 if (mLastAllocatedSection == null) { 1048 return "<" + Bundle.getMessage("None").toLowerCase() + ">"; // <none> 1049 } 1050 return getSectionName(mLastAllocatedSection); 1051 } 1052 1053 public Section getNextSectionToAllocate() { 1054 return mNextSectionToAllocate; 1055 } 1056 1057 public int getNextSectionSeqNumber() { 1058 return mNextSectionSeqNumber; 1059 } 1060 1061 public String getNextSectionToAllocateName() { 1062 if (mNextSectionToAllocate == null) { 1063 return "<" + Bundle.getMessage("None").toLowerCase() + ">"; // <none> 1064 } 1065 return getSectionName(mNextSectionToAllocate); 1066 } 1067 1068 private String getSectionName(@Nonnull Section sc) { 1069 return sc.getDisplayName(); 1070 } 1071 1072 public Block getStartBlock() { 1073 return mStartBlock; 1074 } 1075 1076 public void setStartBlock(Block sBlock) { 1077 mStartBlock = sBlock; 1078 } 1079 1080 public int getStartBlockSectionSequenceNumber() { 1081 return mStartBlockSectionSequenceNumber; 1082 } 1083 1084 public void setStartBlockSectionSequenceNumber(int sBlockSeqNum) { 1085 mStartBlockSectionSequenceNumber = sBlockSeqNum; 1086 } 1087 1088 public Block getEndBlock() { 1089 return mEndBlock; 1090 } 1091 1092 public void setEndBlock(Block eBlock) { 1093 mEndBlock = eBlock; 1094 } 1095 1096 public Section getEndBlockSection() { 1097 return mEndBlockSection; 1098 } 1099 1100 public void setEndBlockSection(Section eSection) { 1101 mEndBlockSection = eSection; 1102 } 1103 1104 public int getEndBlockSectionSequenceNumber() { 1105 return mEndBlockSectionSequenceNumber; 1106 } 1107 1108 public void setEndBlockSectionSequenceNumber(int eBlockSeqNum) { 1109 mEndBlockSectionSequenceNumber = eBlockSeqNum; 1110 } 1111 1112 public int getPriority() { 1113 return mPriority; 1114 } 1115 1116 public void setPriority(int priority) { 1117 mPriority = priority; 1118 } 1119 1120 public boolean getAutoRun() { 1121 return mAutoRun; 1122 } 1123 1124 public void setAutoRun(boolean autoRun) { 1125 mAutoRun = autoRun; 1126 } 1127 1128 public String getDccAddress() { 1129 return mDccAddress; 1130 } 1131 1132 public void setDccAddress(String dccAddress) { 1133 mDccAddress = dccAddress; 1134 } 1135 1136 public boolean getResetWhenDone() { 1137 return mResetWhenDone; 1138 } 1139 1140 public void setResetWhenDone(boolean s) { 1141 mResetWhenDone = s; 1142 } 1143 1144 public boolean getReverseAtEnd() { 1145 return mReverseAtEnd; 1146 } 1147 1148 public void setReverseAtEnd(boolean s) { 1149 mReverseAtEnd = s; 1150 } 1151 1152 protected Section getSecondAllocatedSection() { 1153 return mSecondAllocatedSection; 1154 } 1155 1156 /** 1157 * Returns the AllocateM Method to be used by autoAllocate 1158 * 1159 * @return The number of Blocks ahead to be allocated or 0 = Allocate By Safe 1160 * sections or -1 - Allocate All The Way. 1161 */ 1162 public int getAllocateMethod() { 1163 return mAllocateMethod; 1164 } 1165 1166 /** 1167 * Sets the Allocation Method to be used bu autoAllocate 1168 * @param i The number of Blocks ahead to be allocated or 0 = Allocate By Safe 1169 * sections or -1 - Allocate All The Way. 1170 */ 1171 public void setAllocateMethod(int i) { 1172 mAllocateMethod = i; 1173 } 1174 1175 // 1176 // Operating methods 1177 // 1178 public AllocationRequest initializeFirstAllocation() { 1179 if (!mAllocatedSections.isEmpty()) { 1180 log.error("ERROR - Request to initialize first allocation, when allocations already present"); 1181 return null; 1182 } 1183 if ((mStartBlockSectionSequenceNumber > 0) && (mStartBlock != null)) { 1184 mNextSectionToAllocate = mTransit.getSectionFromBlockAndSeq(mStartBlock, 1185 mStartBlockSectionSequenceNumber); 1186 if (mNextSectionToAllocate == null) { 1187 mNextSectionToAllocate = mTransit.getSectionFromConnectedBlockAndSeq(mStartBlock, 1188 mStartBlockSectionSequenceNumber); 1189 if (mNextSectionToAllocate == null) { 1190 log.error("ERROR - Cannot find Section for first allocation of ActiveTrain{}", getActiveTrainName()); 1191 return null; 1192 } 1193 } 1194 mNextSectionSeqNumber = mStartBlockSectionSequenceNumber; 1195 mNextSectionDirection = getAllocationDirectionFromSectionAndSeq(mNextSectionToAllocate, 1196 mNextSectionSeqNumber); 1197 } else { 1198 log.error("ERROR - Insufficient information to initialize first allocation"); 1199 return null; 1200 } 1201 if (!mDispatcher.requestAllocation(this, 1202 mNextSectionToAllocate, mNextSectionDirection, mNextSectionSeqNumber, true, null, true)) { 1203 log.error("Allocation request failed for first allocation of {}", getActiveTrainName()); 1204 } 1205 if (mDispatcher.getRosterEntryInBlock() && getRosterEntry() != null) { 1206 mStartBlock.setValue(getRosterEntry()); 1207 } else if (mDispatcher.getShortNameInBlock()) { 1208 mStartBlock.setValue(mTrainName); 1209 } 1210 AllocationRequest ar = mDispatcher.findAllocationRequestInQueue(mNextSectionToAllocate, 1211 mNextSectionSeqNumber, mNextSectionDirection, this); 1212 return ar; 1213 } 1214 1215 protected boolean addEndSection(Section s, int seq) { 1216 AllocatedSection as = mAllocatedSections.get(mAllocatedSections.size() - 1); 1217 if (!as.setNextSection(s, seq)) { 1218 return false; 1219 } 1220 setEndBlockSection(s); 1221 setEndBlockSectionSequenceNumber(seq); 1222 //At this stage the section direction hasn't been set, by default the exit block returned is the reverse if the section is free 1223 setEndBlock(s.getExitBlock()); 1224 mNextSectionSeqNumber = seq; 1225 mNextSectionToAllocate = s; 1226 return true; 1227 } 1228 1229 /*This is for use where the transit has been extended, then the last section has been cancelled no 1230 checks are performed, these should be done by a higher level code*/ 1231 protected void removeLastAllocatedSection() { 1232 AllocatedSection as = mAllocatedSections.get(mAllocatedSections.size() - 1); 1233 //Set the end block using the AllocatedSections exit block before clearing the next section in the allocatedsection 1234 setEndBlock(as.getExitBlock()); 1235 1236 as.setNextSection(null, 0); 1237 setEndBlockSection(as.getSection()); 1238 1239 setEndBlockSectionSequenceNumber(getEndBlockSectionSequenceNumber() - 1); 1240 // In theory the following values should have already been set if there are no more sections to allocate. 1241 mNextSectionSeqNumber = 0; 1242 mNextSectionToAllocate = null; 1243 } 1244 1245 protected AllocatedSection reverseAllAllocatedSections() { 1246 AllocatedSection aSec = null; 1247 for (int i = 0; i < mAllocatedSections.size(); i++) { 1248 aSec = mAllocatedSections.get(i); 1249 int dir = mTransit.getDirectionFromSectionAndSeq(aSec.getSection(), aSec.getSequence()); 1250 if (dir == Section.FORWARD) { 1251 aSec.getSection().setState(Section.REVERSE); 1252 } else { 1253 aSec.getSection().setState(Section.FORWARD); 1254 } 1255 aSec.setStoppingSensors(); 1256 } 1257 return aSec; 1258 } 1259 1260 protected void resetAllAllocatedSections() { 1261 for (int i = 0; i < mAllocatedSections.size(); i++) { 1262 AllocatedSection aSec = mAllocatedSections.get(i); 1263 int dir = mTransit.getDirectionFromSectionAndSeq(aSec.getSection(), aSec.getSequence()); 1264 aSec.getSection().setState(dir); 1265 aSec.setStoppingSensors(); 1266 } 1267 } 1268 1269 protected void setRestart(int delayType, int restartDelay, Sensor delaySensor, boolean resetSensorAfter) { 1270 if (delayType == NODELAY) { 1271 holdAllocation(false); 1272 return; 1273 } 1274 1275 setStatus(READY); 1276 restartPoint = true; 1277 if (delayType == TIMEDDELAY) { 1278 Date now = InstanceManager.getDefault(jmri.Timebase.class).getTime(); 1279 @SuppressWarnings("deprecation") // Date.getHours 1280 int nowHours = now.getHours(); 1281 @SuppressWarnings("deprecation") // Date.getMinutes 1282 int nowMinutes = now.getMinutes(); 1283 int hours = restartDelay / 60; 1284 int minutes = restartDelay % 60; 1285 restartHr = nowHours + hours + ((nowMinutes + minutes) / 60); 1286 restartMin = ((nowMinutes + minutes) % 60); 1287 if (restartHr>23){ 1288 restartHr=restartHr-24; 1289 } 1290 } 1291 mDispatcher.addDelayedTrain(this, delayType, delaySensor, resetSensorAfter ); 1292 } 1293 1294 protected boolean isInAllocatedList(AllocatedSection as) { 1295 for (int i = 0; i < mAllocatedSections.size(); i++) { 1296 if (mAllocatedSections.get(i) == as) { 1297 return true; 1298 } 1299 } 1300 return false; 1301 } 1302 1303 protected boolean isInAllocatedList(Section s) { 1304 for (int i = 0; i < mAllocatedSections.size(); i++) { 1305 if ((mAllocatedSections.get(i)).getSection() == s) { 1306 return true; 1307 } 1308 } 1309 return false; 1310 } 1311 1312 1313 boolean restartPoint = false; 1314 1315 private boolean holdAllocation = false; 1316 1317 protected void holdAllocation(boolean boo) { 1318 holdAllocation = boo; 1319 } 1320 1321 protected boolean holdAllocation() { 1322 return holdAllocation; 1323 } 1324 1325 protected boolean reachedRestartPoint() { 1326 return restartPoint; 1327 } 1328 1329 protected void restart() { 1330 log.debug("{}: restarting", getTrainName()); 1331 restartPoint = false; 1332 holdAllocation(false); 1333 setStatus(WAITING); 1334 if (mAutoActiveTrain != null) { 1335 mAutoActiveTrain.setupNewCurrentSignal(null,true); 1336 } 1337 } 1338 1339 public void terminate() { 1340 mDispatcher.removeDelayedTrain(this); 1341 if (getDelaySensor() != null && delaySensorListener != null) { 1342 getDelaySensor().removePropertyChangeListener(delaySensorListener); 1343 } 1344 if (getRestartSensor() != null && restartSensorListener != null) { 1345 getRestartSensor().removePropertyChangeListener(restartSensorListener); 1346 } 1347 setMode(TERMINATED); 1348 mTransit.setState(Transit.IDLE); 1349 deleteAdHocTransit(mTransit); 1350 } 1351 1352 private void deleteAdHocTransit(Transit sysname) { 1353 Transit adht = sysname; 1354 if (adht != null && adht.getTransitType() == TransitType.DYNAMICADHOC) { 1355 List<Section> tmpSecs = new ArrayList<>(); 1356 for (TransitSection ts : adht.getTransitSectionList()) { 1357 if (ts.getSection().getSectionType() == SectionType.DYNAMICADHOC) { 1358 tmpSecs.add(ts.getSection()); 1359 } 1360 } 1361 InstanceManager.getDefault(jmri.TransitManager.class).deleteTransit(adht); 1362 for (Section ts : tmpSecs) { 1363 InstanceManager.getDefault(jmri.SectionManager.class).deleteSection(ts); 1364 } 1365 } 1366 } 1367 1368 public void dispose() { 1369 if (getTransit()!=null) { 1370 getTransit().removeTemporarySections(); 1371 } 1372 } 1373 1374 // Property Change Support 1375 private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 1376 1377 @OverridingMethodsMustInvokeSuper 1378 protected void firePropertyChange(String p, Object old, Object n) { 1379 pcs.firePropertyChange(p, old, n); 1380 } 1381 1382 @Override 1383 public void addPropertyChangeListener(PropertyChangeListener listener) { 1384 pcs.addPropertyChangeListener(listener); 1385 } 1386 1387 @Override 1388 public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { 1389 pcs.addPropertyChangeListener(propertyName, listener); 1390 } 1391 1392 @Override 1393 public PropertyChangeListener[] getPropertyChangeListeners() { 1394 return pcs.getPropertyChangeListeners(); 1395 } 1396 1397 @Override 1398 public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { 1399 return pcs.getPropertyChangeListeners(propertyName); 1400 } 1401 1402 @Override 1403 public void removePropertyChangeListener(PropertyChangeListener listener) { 1404 pcs.removePropertyChangeListener(listener); 1405 } 1406 1407 @Override 1408 public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { 1409 pcs.removePropertyChangeListener(propertyName, listener); 1410 } 1411 1412 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActiveTrain.class); 1413 1414}