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
279    // start up instance variables
280    private boolean mStarted = false;
281
282    //
283    // Access methods
284    //
285    public boolean getStarted() {
286        return mStarted;
287    }
288
289    public void setDispatcher(DispatcherFrame df) {
290        mDispatcher = df;
291        mSignalType = df.getSignalType();
292        if (mTransit.getTransitType() == TransitType.DYNAMICADHOC) {
293            mSignalType = DispatcherFrame.SECTIONSALLOCATED;
294        }
295    }
296
297    public void setStarted() {
298        mStarted = true;
299        mStatus = RUNNING;
300        holdAllocation(false);
301        setStatus(WAITING);
302        if (mAutoActiveTrain != null && mDispatcher.getSignalType() == DispatcherFrame.SIGNALMAST) {
303            mAutoActiveTrain.setupNewCurrentSignal(null,false);
304        }
305    }
306
307    public Transit getTransit() {
308        return mTransit;
309    }
310
311    public String getTransitName() {
312        return mTransit.getDisplayName();
313    }
314
315    public String getActiveTrainName() {
316        return (mTrainName + " / " + getTransitName());
317    }
318
319    // Note: Transit and Train may not be changed once an ActiveTrain is created.
320    public String getTrainName() {
321        return mTrainName;
322    }
323
324    public int getTrainSource() {
325        return mTrainSource;
326    }
327
328    public void setRosterEntry(jmri.jmrit.roster.RosterEntry re) {
329        mRoster = re;
330    }
331
332    public jmri.jmrit.roster.RosterEntry getRosterEntry() {
333        if (mRoster == null && getTrainSource() == ROSTER) {
334            //Try to resolve the roster based upon the train name
335            mRoster = jmri.jmrit.roster.Roster.getDefault().getEntryForId(getTrainName());
336        } else if (getTrainSource() != ROSTER) {
337            mRoster = null;
338        }
339        return mRoster;
340    }
341
342    public int getStatus() {
343        return mStatus;
344    }
345
346    public void setStatus(int status) {
347        if (restartPoint) {
348            return;
349        }
350        if ((status == RUNNING) || (status == PAUSED) || (status == WAITING) || (status == WORKING)
351                || (status == READY) || (status == STOPPED) || (status == DONE)) {
352            if (mStatus != status) {
353                int old = mStatus;
354                mStatus = status;
355                firePropertyChange(PROPERTY_STATUS, old, mStatus);
356                if (mStatus == DONE) {
357                    mDispatcher.terminateActiveTrain(this,terminateWhenFinished,true);
358                }
359            }
360        } else {
361            log.error("Invalid ActiveTrain status - {}", status);
362        }
363    }
364
365    public void setControlingSignal(Object oldSignal, Object newSignal) {
366        firePropertyChange(PROPERTY_SIGNAL, oldSignal, newSignal);
367    }
368
369    public String getStatusText() {
370        if (mStatus == RUNNING) {
371            return Bundle.getMessage("RUNNING");
372        } else if (mStatus == PAUSED) {
373            return Bundle.getMessage("PAUSED");
374        } else if (mStatus == WAITING) {
375            if (!mStarted) {
376                if (mDelayedStart == TIMEDDELAY) {
377                    return jmri.jmrit.beantable.LogixTableAction.formatTime(mDepartureTimeHr,
378                            mDepartureTimeMin) + " " + Bundle.getMessage("START");
379                } else if (mDelayedStart == SENSORDELAY) {
380                    return (Bundle.getMessage("BeanNameSensor") + " " + getDelaySensorName());
381                }
382            }
383            return Bundle.getMessage("WAITING");
384        } else if (mStatus == WORKING) {
385            return Bundle.getMessage("WORKING");
386        } else if (mStatus == READY) {
387            if (restartPoint && getDelayedRestart() == TIMEDDELAY) {
388                return jmri.jmrit.beantable.LogixTableAction.formatTime(restartHr,
389                        restartMin) + " " + Bundle.getMessage("START");
390            } else if (restartPoint && getDelayedRestart() == SENSORDELAY) {
391                return (Bundle.getMessage("BeanNameSensor") + " " + getRestartSensorName());
392            }
393            return Bundle.getMessage("READY");
394        } else if (mStatus == STOPPED) {
395            return Bundle.getMessage("STOPPED");
396        } else if (mStatus == DONE) {
397            return Bundle.getMessage("DONE");
398        }
399        return ("");
400    }
401
402    /**
403     * sets the train detection type
404     * @param value {@link ActiveTrain.TrainDetection}
405     */
406    public void setTrainDetection(TrainDetection value) {
407        trainDetection = value;
408    }
409
410    /**
411     * Gets the train detection type
412     * @return {@link ActiveTrain.TrainDetection}
413     */
414    public TrainDetection getTrainDetection() {
415        return trainDetection;
416    }
417
418    public boolean isTransitReversed() {
419        return mTransitReversed;
420    }
421
422    public void setTransitReversed(boolean set) {
423        mTransitReversed = set;
424    }
425
426    public boolean isAllocationReversed() {
427        return mAllocationReversed;
428    }
429
430    public void setAllocationReversed(boolean set) {
431        mAllocationReversed = set;
432    }
433
434    public int getDelayedStart() {
435        return mDelayedStart;
436    }
437
438    public void setNextTrain(String nextTrain) {
439        mNextTrain = nextTrain;
440    }
441
442    public String getNextTrain() {
443        return mNextTrain;
444    }
445
446    public void setDelayedStart(int delay) {
447        mDelayedStart = delay;
448    }
449
450    public int getDelayedRestart() {
451        return mDelayedRestart;
452    }
453
454    public void setDelayedRestart(int delay) {
455        mDelayedRestart = delay;
456    }
457
458    public int getDelayReverseRestart() {
459        return mDelayReverseRestart;
460    }
461
462    public void setReverseDelayRestart(int delay) {
463        mDelayReverseRestart = delay;
464    }
465
466    public int getDepartureTimeHr() {
467        return mDepartureTimeHr;
468    }
469
470    public void setDepartureTimeHr(int hr) {
471        mDepartureTimeHr = hr;
472    }
473
474    public int getDepartureTimeMin() {
475        return mDepartureTimeMin;
476    }
477
478    public void setDepartureTimeMin(int min) {
479        mDepartureTimeMin = min;
480    }
481
482    public void setRestartDelay(int min) {
483        mRestartDelay = min;
484    }
485
486    public int getRestartDelay() {
487        return mRestartDelay;
488    }
489
490    int mReverseRestartDelay;
491    public int getReverseRestartDelay() {
492        return mReverseRestartDelay;
493    }
494    public void setReverseRestartDelay(int min) {
495        mReverseRestartDelay = min;
496    }
497
498    int restartHr = 0;
499    int restartMin = 0;
500
501    public int getRestartDepartHr() {
502        return restartHr;
503    }
504
505    public int getRestartDepartMin() {
506        return restartMin;
507    }
508
509    public void setTerminateWhenDone(boolean boo) {
510        terminateWhenFinished = boo;
511    }
512
513    public Sensor getDelaySensor() {
514        if (mStartSensor == null) {
515            return null;
516        }
517        return mStartSensor.getBean();
518    }
519
520    public String getDelaySensorName() {
521        if (mStartSensor == null) {
522            return null;
523        }
524        return mStartSensor.getName();
525    }
526
527    public void setDelaySensor(Sensor s) {
528        if (s == null) {
529            mStartSensor = null;
530            return;
531        }
532        mStartSensor = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(s.getDisplayName(), s);
533    }
534
535    public void setResetStartSensor(boolean b) {
536        resetStartSensor = b;
537    }
538
539    public boolean getResetStartSensor() {
540        return resetStartSensor;
541    }
542
543    public Sensor getReverseRestartSensor() {
544        if (mReverseRestartSensor == null) {
545            return null;
546        }
547        return mReverseRestartSensor.getBean();
548    }
549
550    public String getReverseRestartSensorName() {
551        if (mReverseRestartSensor == null) {
552            return null;
553        }
554        return mReverseRestartSensor.getName();
555    }
556
557    public void setReverseDelaySensor(Sensor s) {
558        if (s == null) {
559            mReverseRestartSensor = null;
560            return;
561        }
562        mReverseRestartSensor = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(s.getDisplayName(), s);
563    }
564
565    public void setReverseResetRestartSensor(boolean b) {
566        resetReverseRestartSensor = b;
567    }
568
569    public boolean getResetReverseRestartSensor() {
570        return resetReverseRestartSensor;
571    }
572
573    public Sensor getRestartSensor() {
574        if (mRestartSensor == null) {
575            return null;
576        }
577        return mRestartSensor.getBean();
578    }
579
580    public String getRestartSensorName() {
581        if (mRestartSensor == null) {
582            return null;
583        }
584        return mRestartSensor.getName();
585    }
586
587    public void setRestartSensor(Sensor s) {
588        if (s == null) {
589            mRestartSensor = null;
590            return;
591        }
592        mRestartSensor = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(s.getDisplayName(), s);
593    }
594
595    public void setResetRestartSensor(boolean b) {
596        resetRestartSensor = b;
597    }
598
599    public boolean getResetRestartSensor() {
600        return resetRestartSensor;
601    }
602
603    public int getSignalType() {
604        return mSignalType;
605    }
606
607    private java.beans.PropertyChangeListener delaySensorListener = null;
608    private java.beans.PropertyChangeListener restartSensorListener = null;
609    private java.beans.PropertyChangeListener restartAllocationSensorListener = null;
610
611    public void initializeDelaySensor() {
612        if (mStartSensor == null) {
613            log.error("Call to initialise delay on start sensor, but none specified");
614            return;
615        }
616        if (delaySensorListener == null) {
617            final ActiveTrain at = this;
618            delaySensorListener = e -> {
619                if (Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())
620                        && ((Integer) e.getNewValue()) == Sensor.ACTIVE) {
621                    getDelaySensor().removePropertyChangeListener(delaySensorListener);
622                    mDispatcher.removeDelayedTrain(at);
623                    setStarted();
624                    mDispatcher.queueScanOfAllocationRequests();
625                    if (resetStartSensor) {
626                        try {
627                            getDelaySensor().setKnownState(Sensor.INACTIVE);
628                            log.debug("Start sensor {} set back to inActive", getDelaySensor().getDisplayName(USERSYS));
629                        } catch (jmri.JmriException ex) {
630                            log.error("Error resetting start sensor {} back to inActive",
631                                getDelaySensor().getDisplayName(USERSYS));
632                        }
633                    }
634                }
635            };
636        }
637        getDelaySensor().addPropertyChangeListener(delaySensorListener);
638    }
639
640    public void initializeRestartSensor(Sensor restartSensor, boolean resetSensor) {
641        if (restartSensor == null) {
642            log.error("Call to initialise delay on restart sensor, but none specified");
643            return;
644        }
645        if (restartSensorListener == null) {
646            final ActiveTrain at = this;
647            restartSensorListener = e -> {
648                if (Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())
649                        && ((Integer) e.getNewValue()) == Sensor.ACTIVE) {
650                    restartSensor.removePropertyChangeListener(restartSensorListener);
651                    restartSensorListener = null;
652                    mDispatcher.removeDelayedTrain(at);
653                    restart();
654                    mDispatcher.queueScanOfAllocationRequests();
655                    if (resetSensor) {
656                        try {
657                            restartSensor.setKnownState(Sensor.INACTIVE);
658                            log.debug("Restart sensor {} set back to inActive",
659                                getRestartSensor().getDisplayName(USERSYS));
660                        } catch (jmri.JmriException ex) {
661                            log.error("Error resetting restart sensor back to inActive");
662                        }
663                    }
664                }
665            };
666        }
667        restartSensor.addPropertyChangeListener(restartSensorListener);
668    }
669
670    public void initializeRestartAllocationSensor(NamedBeanHandle<Sensor> restartAllocationSensor) {
671        if (restartAllocationSensor == null) {
672            log.error("Call to initialise delay on restart allocation sensor, but none specified");
673            return;
674        }
675        if (restartAllocationSensorListener == null) {
676            restartAllocationSensorListener = e -> {
677                if (Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())
678                        && (((Integer) e.getNewValue()) == Sensor.INACTIVE)) {
679                    restartAllocationSensor.getBean().removePropertyChangeListener(restartAllocationSensorListener);
680                    restartAllocationSensorListener = null;
681                    mDispatcher.queueScanOfAllocationRequests();
682                }
683            };
684        }
685        restartAllocationSensor.getBean().addPropertyChangeListener(restartAllocationSensorListener);
686    }
687
688    public void setTrainType(int type) {
689        mTrainType = type;
690    }
691
692    /**
693     * set train type using localized string name as stored
694     *
695     * @param sType  name, such as "LOCAL_PASSENGER"
696     */
697    public void setTrainType(String sType) {
698        if (sType.equals(Bundle.getMessage("LOCAL_FREIGHT"))) {
699            setTrainType(LOCAL_FREIGHT);
700        } else if (sType.equals(Bundle.getMessage("LOCAL_PASSENGER"))) {
701            setTrainType(LOCAL_PASSENGER);
702        } else if (sType.equals(Bundle.getMessage("THROUGH_FREIGHT"))) {
703            setTrainType(THROUGH_FREIGHT);
704        } else if (sType.equals(Bundle.getMessage("THROUGH_PASSENGER"))) {
705            setTrainType(THROUGH_PASSENGER);
706        } else if (sType.equals(Bundle.getMessage("EXPRESS_FREIGHT"))) {
707            setTrainType(EXPRESS_FREIGHT);
708        } else if (sType.equals(Bundle.getMessage("EXPRESS_PASSENGER"))) {
709            setTrainType(EXPRESS_PASSENGER);
710        } else if (sType.equals(Bundle.getMessage("MOW"))) {
711            setTrainType(MOW);
712        }
713    }
714
715    public int getTrainType() {
716        return mTrainType;
717    }
718
719    public String getTrainTypeText() {
720        if (mTrainType == LOCAL_FREIGHT) {
721            return Bundle.getMessage("LOCAL_FREIGHT");
722        } else if (mTrainType == LOCAL_PASSENGER) {
723            return Bundle.getMessage("LOCAL_PASSENGER");
724        } else if (mTrainType == THROUGH_FREIGHT) {
725            return Bundle.getMessage("THROUGH_FREIGHT");
726        } else if (mTrainType == THROUGH_PASSENGER) {
727            return Bundle.getMessage("THROUGH_PASSENGER");
728        } else if (mTrainType == EXPRESS_FREIGHT) {
729            return Bundle.getMessage("EXPRESS_FREIGHT");
730        } else if (mTrainType == EXPRESS_PASSENGER) {
731            return Bundle.getMessage("EXPRESS_PASSENGER");
732        } else if (mTrainType == MOW) {
733            return Bundle.getMessage("MOW");
734        }
735        return ("");
736    }
737
738    public int getMode() {
739        return mMode;
740    }
741
742    public void forcePassNextSafeSection() {
743        for (AllocatedSection as: mAllocatedSections) {
744            if (as.getTransitSection().getSection() == mLastAllocatedSection
745                    && as.getTransitSection().isSafe()
746                    && as.getNextSection().getOccupancy() == Section.UNOCCUPIED) {
747                mLastAllocOverrideSafe = mLastAllocatedSection;
748            }
749        }
750    }
751
752    public void setMode(int mode) {
753        if ((mode == AUTOMATIC) || (mode == MANUAL)
754                || (mode == DISPATCHED || mode == TERMINATED)) {
755            int old = mMode;
756            mMode = mode;
757            firePropertyChange(PROPERTY_MODE, old, mMode);
758        } else {
759            log.error("Attempt to set ActiveTrain mode to illegal value - {}", mode);
760        }
761    }
762
763    @Nonnull
764    public String getModeText() {
765        switch (mMode) {
766            case AUTOMATIC:
767                return Bundle.getMessage("AUTOMATIC");
768            case MANUAL:
769                return Bundle.getMessage("MANUAL");
770            case DISPATCHED:
771                return Bundle.getMessage("DISPATCHED");
772            case TERMINATED:
773                return Bundle.getMessage("TERMINATED");
774            default:
775                return "";
776        }
777    }
778
779    public void setAutoActiveTrain(AutoActiveTrain aat) {
780        mAutoActiveTrain = aat;
781    }
782
783    public AutoActiveTrain getAutoActiveTrain() {
784        return mAutoActiveTrain;
785    }
786
787    public int getRunningDirectionFromSectionAndSeq(Section s, int seqNo) {
788        int dir = mTransit.getDirectionFromSectionAndSeq(s, seqNo);
789        if (mTransitReversed) {
790            if (dir == Section.FORWARD) {
791                dir = Section.REVERSE;
792            } else {
793                dir = Section.FORWARD;
794            }
795        }
796        return dir;
797    }
798
799    public int getAllocationDirectionFromSectionAndSeq(Section s, int seqNo) {
800        int dir = mTransit.getDirectionFromSectionAndSeq(s, seqNo);
801        if (mAllocationReversed) {
802            if (dir == Section.FORWARD) {
803                dir = Section.REVERSE;
804            } else {
805                dir = Section.FORWARD;
806            }
807        }
808        return dir;
809    }
810
811    public void addAllocatedSection(AllocatedSection as) {
812        if (as != null) {
813            mAllocatedSections.add(as);
814            if (as.getSection() == mNextSectionToAllocate) {
815                // this  is the next Section in the Transit, update pointers
816                mLastAllocatedSection = as.getSection();
817                mLastAllocOverrideSafe = null;
818                mLastAllocatedSectionSeqNumber = mNextSectionSeqNumber;
819                mNextSectionToAllocate = as.getNextSection();
820                mNextSectionSeqNumber = as.getNextSectionSequence();
821                mNextSectionDirection = getAllocationDirectionFromSectionAndSeq(
822                        mNextSectionToAllocate, mNextSectionSeqNumber);
823                as.setAllocationNumber(mNextAllocationNumber);
824                mNextAllocationNumber++;
825            } else {
826                // this is an extra allocated Section
827                as.setAllocationNumber(-1);
828            }
829            if ((mStatus == WAITING) && mStarted) {
830                setStatus(RUNNING);
831            }
832            if (as.getSequence() == 2) {
833                mSecondAllocatedSection = as.getSection();
834            }
835            if (mDispatcher.getNameInAllocatedBlock()) {
836                if (mDispatcher.getRosterEntryInBlock() && getRosterEntry() != null) {
837                    as.getSection().setNameFromActiveBlock(getRosterEntry());
838                } else {
839                    as.getSection().setNameInBlocks(mTrainName);
840                }
841                as.getSection().suppressNameUpdate(true);
842            }
843            if (mDispatcher.getExtraColorForAllocated()) {
844                as.getSection().setAlternateColorFromActiveBlock(true);
845            }
846            // notify anyone interested
847            firePropertyChange(PROPERTY_SECTION_ALLOCATED,as , null);
848            refreshPanel();
849        } else {
850            log.error("Null Allocated Section reference in addAllocatedSection of ActiveTrain");
851        }
852    }
853
854    private void refreshPanel() {
855        var editorManager = InstanceManager.getDefault(jmri.jmrit.display.EditorManager.class);
856        for (var panel : editorManager.getAll(jmri.jmrit.display.layoutEditor.LayoutEditor.class)) {
857            panel.redrawPanel();
858        }
859    }
860
861    public void removeAllocatedSection(AllocatedSection as) {
862        if (as == null) {
863            log.error("Null AllocatedSection reference in removeAllocatedSection of ActiveTrain");
864            return;
865        }
866        int index = -1;
867        for (int i = 0; i < mAllocatedSections.size(); i++) {
868            if (as == mAllocatedSections.get(i)) {
869                index = i;
870            }
871        }
872        if (index < 0) {
873            log.error("Attempt to remove an unallocated Section {}", as.getSection().getDisplayName(USERSYS));
874            return;
875        }
876        mAllocatedSections.remove(index);
877        if (mDispatcher.getNameInAllocatedBlock()) {
878            as.getSection().clearNameInUnoccupiedBlocks();
879            as.getSection().suppressNameUpdate(false);
880        }
881        for (Block b: as.getSection().getBlockList()) {
882            if (!mDispatcher.checkForBlockInAllocatedSection(b, as.getSection())) {
883                String userName = b.getUserName();
884                if (userName != null) {
885                    LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
886                    if (lb != null) {
887                        lb.setUseExtraColor(false);
888                    }
889                }
890            }
891        }
892        // notify anyone interested
893        firePropertyChange(PROPERTY_SECTION_DEALLOCATED,as , null);
894        refreshPanel();
895        if (as.getSection() == mLastAllocatedSection) {
896            mLastAllocatedSection = null;
897            mLastAllocOverrideSafe = null;
898            if (!mAllocatedSections.isEmpty()) {
899                mLastAllocatedSection = mAllocatedSections.get(
900                        mAllocatedSections.size() - 1).getSection();
901                mLastAllocatedSectionSeqNumber = mAllocatedSections.size() - 1;
902            }
903        }
904    }
905
906    /**
907     * This resets the state of the ActiveTrain so that it can be reallocated.
908     */
909    public void allocateAFresh() {
910        setStatus(WAITING);
911        holdAllocation = false;
912        setTransitReversed(false);
913        List<AllocatedSection> sectionsToRelease = new ArrayList<>();
914        for (AllocatedSection as : mDispatcher.getAllocatedSectionsList()) {
915            if (as.getActiveTrain() == this) {
916                sectionsToRelease.add(as);
917            }
918        }
919        for (AllocatedSection as : sectionsToRelease) {
920            mDispatcher.releaseAllocatedSection(as, true); // need to find Allocated Section
921            mDispatcher.queueWaitForEmpty(); //ensure release processed before proceding.
922            as.getSection().setState(Section.FREE);
923        }
924        if (mLastAllocatedSection != null) {
925            mLastAllocatedSection.setState(Section.FREE);
926        }
927        resetAllAllocatedSections();
928        clearAllocations();
929        setAllocationReversed(false);
930        // wait for AutoAllocate to do complete.
931        mDispatcher.queueWaitForEmpty();
932        if (mAutoRun) {
933            mAutoActiveTrain.allocateAFresh();
934        }
935        mDispatcher.allocateNewActiveTrain(this);
936    }
937
938    public void clearAllocations() {
939        for (AllocatedSection as : getAllocatedSectionList()) {
940            removeAllocatedSection(as);
941        }
942    }
943
944    public List<AllocatedSection> getAllocatedSectionList() {
945        List<AllocatedSection> list = new ArrayList<>();
946        for (int i = 0; i < mAllocatedSections.size(); i++) {
947            list.add(mAllocatedSections.get(i));
948        }
949        return list;
950    }
951
952    /**
953     * Returns list of all Blocks occupied by or allocated to this train. They
954     * are in order from the tail of the train to the head of the train then on
955     * to the forward-most allocated block. Note that unoccupied blocks can
956     * exist before and after the occupied blocks.
957     *
958     * TODO: doesn't handle reversing of adjacent multi-block sections well
959     *
960     * @return the list of blocks order of occupation
961     */
962    public List<Block> getBlockList() {
963        List<Block> list = new ArrayList<>();
964        for (int i = 0; i < mAllocatedSections.size(); i++) { // loop thru allocated sections, then all blocks for each section
965            Section s = mAllocatedSections.get(i).getSection();
966            List<Block> bl = s.getBlockList();
967            if (bl.size() > 1) { //sections with multiple blocks need extra logic
968
969                boolean blocksConnected = true;
970                //determine if blocks should be added in forward or reverse order based on connectivity
971                if (i == 0) { //for first section, compare last block to first of next section
972                    if (mAllocatedSections.size() > 1
973                            && //only one section, assume forward
974                            !connected(bl.get(bl.size() - 1), mAllocatedSections.get(i + 1).getSection().getBlockList().get(0))) {
975                        blocksConnected = false;
976                    }
977                } else { //not first section, check for connectivity between last block in list, and first block in this section
978                    if (!connected(list.get(list.size() - 1), bl.get(0))) { //last block is not connected to first block, add reverse
979                        blocksConnected = false;
980                    }
981                }
982                if (blocksConnected) { //blocks were connected, so add to outgoing in forward order
983                    for (int j = 0; j < bl.size(); j++) {
984                        Block b = bl.get(j);
985                        list.add(b);
986                        log.trace("block {} ({}) added to list for Section {} (fwd)", b.getDisplayName(USERSYS),
987                                (b.getState() == Block.OCCUPIED ? "OCCUPIED" : "UNOCCUPIED"),
988                                s.getDisplayName(USERSYS));
989                    }
990                } else { //not connected, add in reverse order
991                    for (int j = bl.size() - 1; j >= 0; j--) {
992                        Block b = bl.get(j);
993                        list.add(b);
994                        log.trace("block {} ({}) added to list for Section {} (rev)", b.getDisplayName(USERSYS),
995                                (b.getState() == Block.OCCUPIED ? "OCCUPIED" : "UNOCCUPIED"),
996                                s.getDisplayName(USERSYS));
997                    }
998                }
999
1000            } else { //single block sections are simply added to the outgoing list
1001                Block b = bl.get(0);
1002                list.add(b);
1003                log.trace("block {} ({}) added to list for Section {} (one)", b.getDisplayName(USERSYS),
1004                        (b.getState() == Block.OCCUPIED ? "OCCUPIED" : "UNOCCUPIED"),
1005                        s.getDisplayName(USERSYS));
1006            }
1007        }
1008        return list;
1009    }
1010
1011    /* copied from Section.java */
1012    private boolean connected(Block b1, Block b2) {
1013        if ((b1 != null) && (b2 != null)) {
1014            List<Path> paths = b1.getPaths();
1015            for (int i = 0; i < paths.size(); i++) {
1016                if (paths.get(i).getBlock() == b2) {
1017                    return true;
1018                }
1019            }
1020        }
1021        return false;
1022    }
1023
1024    public Section getLastAllocatedSection() {
1025        return mLastAllocatedSection;
1026    }
1027
1028    public Section getLastAllocOverrideSafe() {
1029        return mLastAllocOverrideSafe;
1030    }
1031
1032    public int getLastAllocatedSectionSeqNumber() {
1033        return mLastAllocatedSectionSeqNumber;
1034    }
1035
1036    public String getLastAllocatedSectionName() {
1037        if (mLastAllocatedSection == null) {
1038            return "<" + Bundle.getMessage("None").toLowerCase() + ">"; // <none>
1039        }
1040        return getSectionName(mLastAllocatedSection);
1041    }
1042
1043    public Section getNextSectionToAllocate() {
1044        return mNextSectionToAllocate;
1045    }
1046
1047    public int getNextSectionSeqNumber() {
1048        return mNextSectionSeqNumber;
1049    }
1050
1051    public String getNextSectionToAllocateName() {
1052        if (mNextSectionToAllocate == null) {
1053            return "<" + Bundle.getMessage("None").toLowerCase() + ">"; // <none>
1054        }
1055        return getSectionName(mNextSectionToAllocate);
1056    }
1057
1058    private String getSectionName(@Nonnull Section sc) {
1059        return sc.getDisplayName();
1060    }
1061
1062    public Block getStartBlock() {
1063        return mStartBlock;
1064    }
1065
1066    public void setStartBlock(Block sBlock) {
1067        mStartBlock = sBlock;
1068    }
1069
1070    public int getStartBlockSectionSequenceNumber() {
1071        return mStartBlockSectionSequenceNumber;
1072    }
1073
1074    public void setStartBlockSectionSequenceNumber(int sBlockSeqNum) {
1075        mStartBlockSectionSequenceNumber = sBlockSeqNum;
1076    }
1077
1078    public Block getEndBlock() {
1079        return mEndBlock;
1080    }
1081
1082    public void setEndBlock(Block eBlock) {
1083        mEndBlock = eBlock;
1084    }
1085
1086    public Section getEndBlockSection() {
1087        return mEndBlockSection;
1088    }
1089
1090    public void setEndBlockSection(Section eSection) {
1091        mEndBlockSection = eSection;
1092    }
1093
1094    public int getEndBlockSectionSequenceNumber() {
1095        return mEndBlockSectionSequenceNumber;
1096    }
1097
1098    public void setEndBlockSectionSequenceNumber(int eBlockSeqNum) {
1099        mEndBlockSectionSequenceNumber = eBlockSeqNum;
1100    }
1101
1102    public int getPriority() {
1103        return mPriority;
1104    }
1105
1106    public void setPriority(int priority) {
1107        mPriority = priority;
1108    }
1109
1110    public boolean getAutoRun() {
1111        return mAutoRun;
1112    }
1113
1114    public void setAutoRun(boolean autoRun) {
1115        mAutoRun = autoRun;
1116    }
1117
1118    public String getDccAddress() {
1119        return mDccAddress;
1120    }
1121
1122    public void setDccAddress(String dccAddress) {
1123        mDccAddress = dccAddress;
1124    }
1125
1126    public boolean getResetWhenDone() {
1127        return mResetWhenDone;
1128    }
1129
1130    public void setResetWhenDone(boolean s) {
1131        mResetWhenDone = s;
1132    }
1133
1134    public boolean getReverseAtEnd() {
1135        return mReverseAtEnd;
1136    }
1137
1138    public void setReverseAtEnd(boolean s) {
1139        mReverseAtEnd = s;
1140    }
1141
1142    protected Section getSecondAllocatedSection() {
1143        return mSecondAllocatedSection;
1144    }
1145
1146    /**
1147     * Returns the AllocateM Method to be used by autoAllocate
1148     *
1149     * @return The number of Blocks ahead to be allocated or 0 = Allocate By Safe
1150     *         sections or -1 - Allocate All The Way.
1151     */
1152    public int getAllocateMethod() {
1153        return mAllocateMethod;
1154    }
1155
1156    /**
1157     * Sets the Allocation Method to be used bu autoAllocate
1158     * @param i The number of Blocks ahead to be allocated or 0 = Allocate By Safe
1159     *          sections or -1 - Allocate All The Way.
1160     */
1161    public void setAllocateMethod(int i) {
1162        mAllocateMethod = i;
1163    }
1164
1165    //
1166    // Operating methods
1167    //
1168    public AllocationRequest initializeFirstAllocation() {
1169        if (!mAllocatedSections.isEmpty()) {
1170            log.error("ERROR - Request to initialize first allocation, when allocations already present");
1171            return null;
1172        }
1173        if ((mStartBlockSectionSequenceNumber > 0) && (mStartBlock != null)) {
1174            mNextSectionToAllocate = mTransit.getSectionFromBlockAndSeq(mStartBlock,
1175                    mStartBlockSectionSequenceNumber);
1176            if (mNextSectionToAllocate == null) {
1177                mNextSectionToAllocate = mTransit.getSectionFromConnectedBlockAndSeq(mStartBlock,
1178                        mStartBlockSectionSequenceNumber);
1179                if (mNextSectionToAllocate == null) {
1180                    log.error("ERROR - Cannot find Section for first allocation of ActiveTrain{}", getActiveTrainName());
1181                    return null;
1182                }
1183            }
1184            mNextSectionSeqNumber = mStartBlockSectionSequenceNumber;
1185            mNextSectionDirection = getAllocationDirectionFromSectionAndSeq(mNextSectionToAllocate,
1186                    mNextSectionSeqNumber);
1187        } else {
1188            log.error("ERROR - Insufficient information to initialize first allocation");
1189            return null;
1190        }
1191        if (!mDispatcher.requestAllocation(this,
1192                mNextSectionToAllocate, mNextSectionDirection, mNextSectionSeqNumber, true, null, true)) {
1193            log.error("Allocation request failed for first allocation of {}", getActiveTrainName());
1194        }
1195        if (mDispatcher.getRosterEntryInBlock() && getRosterEntry() != null) {
1196            mStartBlock.setValue(getRosterEntry());
1197        } else if (mDispatcher.getShortNameInBlock()) {
1198            mStartBlock.setValue(mTrainName);
1199        }
1200        AllocationRequest ar = mDispatcher.findAllocationRequestInQueue(mNextSectionToAllocate,
1201                mNextSectionSeqNumber, mNextSectionDirection, this);
1202        return ar;
1203    }
1204
1205    protected boolean addEndSection(Section s, int seq) {
1206        AllocatedSection as = mAllocatedSections.get(mAllocatedSections.size() - 1);
1207        if (!as.setNextSection(s, seq)) {
1208            return false;
1209        }
1210        setEndBlockSection(s);
1211        setEndBlockSectionSequenceNumber(seq);
1212        //At this stage the section direction hasn't been set, by default the exit block returned is the reverse if the section is free
1213        setEndBlock(s.getExitBlock());
1214        mNextSectionSeqNumber = seq;
1215        mNextSectionToAllocate = s;
1216        return true;
1217    }
1218
1219    /*This is for use where the transit has been extended, then the last section has been cancelled no
1220     checks are performed, these should be done by a higher level code*/
1221    protected void removeLastAllocatedSection() {
1222        AllocatedSection as = mAllocatedSections.get(mAllocatedSections.size() - 1);
1223        //Set the end block using the AllocatedSections exit block before clearing the next section in the allocatedsection
1224        setEndBlock(as.getExitBlock());
1225
1226        as.setNextSection(null, 0);
1227        setEndBlockSection(as.getSection());
1228
1229        setEndBlockSectionSequenceNumber(getEndBlockSectionSequenceNumber() - 1);
1230        // In theory the following values should have already been set if there are no more sections to allocate.
1231        mNextSectionSeqNumber = 0;
1232        mNextSectionToAllocate = null;
1233    }
1234
1235    protected AllocatedSection reverseAllAllocatedSections() {
1236        AllocatedSection aSec = null;
1237        for (int i = 0; i < mAllocatedSections.size(); i++) {
1238            aSec = mAllocatedSections.get(i);
1239            int dir = mTransit.getDirectionFromSectionAndSeq(aSec.getSection(), aSec.getSequence());
1240            if (dir == Section.FORWARD) {
1241                aSec.getSection().setState(Section.REVERSE);
1242            } else {
1243                aSec.getSection().setState(Section.FORWARD);
1244            }
1245            aSec.setStoppingSensors();
1246        }
1247        return aSec;
1248    }
1249
1250    protected void resetAllAllocatedSections() {
1251        for (int i = 0; i < mAllocatedSections.size(); i++) {
1252            AllocatedSection aSec = mAllocatedSections.get(i);
1253            int dir = mTransit.getDirectionFromSectionAndSeq(aSec.getSection(), aSec.getSequence());
1254            aSec.getSection().setState(dir);
1255            aSec.setStoppingSensors();
1256        }
1257    }
1258
1259    protected void setRestart(int delayType, int restartDelay, Sensor delaySensor, boolean resetSensorAfter) {
1260        if (delayType == NODELAY) {
1261            holdAllocation(false);
1262            return;
1263        }
1264
1265        setStatus(READY);
1266        restartPoint = true;
1267        if (delayType == TIMEDDELAY) {
1268            Date now = InstanceManager.getDefault(jmri.Timebase.class).getTime();
1269            @SuppressWarnings("deprecation") // Date.getHours
1270            int nowHours = now.getHours();
1271            @SuppressWarnings("deprecation") // Date.getMinutes
1272            int nowMinutes = now.getMinutes();
1273            int hours = restartDelay / 60;
1274            int minutes = restartDelay % 60;
1275            restartHr = nowHours + hours + ((nowMinutes + minutes) / 60);
1276            restartMin = ((nowMinutes + minutes) % 60);
1277            if (restartHr>23){
1278                restartHr=restartHr-24;
1279            }
1280        }
1281        mDispatcher.addDelayedTrain(this, delayType, delaySensor, resetSensorAfter );
1282    }
1283
1284    protected boolean isInAllocatedList(AllocatedSection as) {
1285        for (int i = 0; i < mAllocatedSections.size(); i++) {
1286            if (mAllocatedSections.get(i) == as) {
1287                return true;
1288            }
1289        }
1290        return false;
1291    }
1292
1293    protected boolean isInAllocatedList(Section s) {
1294        for (int i = 0; i < mAllocatedSections.size(); i++) {
1295            if ((mAllocatedSections.get(i)).getSection() == s) {
1296                return true;
1297            }
1298        }
1299        return false;
1300    }
1301
1302
1303    boolean restartPoint = false;
1304
1305    private boolean holdAllocation = false;
1306
1307    protected void holdAllocation(boolean boo) {
1308        holdAllocation = boo;
1309    }
1310
1311    protected boolean holdAllocation() {
1312        return holdAllocation;
1313    }
1314
1315    protected boolean reachedRestartPoint() {
1316        return restartPoint;
1317    }
1318
1319    protected void restart() {
1320        log.debug("{}: restarting", getTrainName());
1321        restartPoint = false;
1322        holdAllocation(false);
1323        setStatus(WAITING);
1324        if (mAutoActiveTrain != null) {
1325            mAutoActiveTrain.setupNewCurrentSignal(null,true);
1326        }
1327    }
1328
1329    public void terminate() {
1330        mDispatcher.removeDelayedTrain(this);
1331        if (getDelaySensor() != null && delaySensorListener != null) {
1332            getDelaySensor().removePropertyChangeListener(delaySensorListener);
1333        }
1334        if (getRestartSensor() != null && restartSensorListener != null) {
1335            getRestartSensor().removePropertyChangeListener(restartSensorListener);
1336        }
1337        setMode(TERMINATED);
1338        mTransit.setState(Transit.IDLE);
1339        deleteAdHocTransit(mTransit);
1340    }
1341
1342    private void deleteAdHocTransit(Transit sysname) {
1343        Transit adht = sysname;
1344        if (adht != null && adht.getTransitType() == TransitType.DYNAMICADHOC) {
1345            List<Section> tmpSecs = new ArrayList<>();
1346            for (TransitSection ts : adht.getTransitSectionList()) {
1347                if (ts.getSection().getSectionType() == SectionType.DYNAMICADHOC) {
1348                    tmpSecs.add(ts.getSection());
1349                }
1350            }
1351            InstanceManager.getDefault(jmri.TransitManager.class).deleteTransit(adht);
1352            for (Section ts : tmpSecs) {
1353                InstanceManager.getDefault(jmri.SectionManager.class).deleteSection(ts);
1354            }
1355        }
1356    }
1357
1358    public void dispose() {
1359        if (getTransit()!=null) {
1360            getTransit().removeTemporarySections();
1361        }
1362    }
1363
1364    // Property Change Support
1365    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
1366
1367    @OverridingMethodsMustInvokeSuper
1368    protected void firePropertyChange(String p, Object old, Object n) {
1369        pcs.firePropertyChange(p, old, n);
1370    }
1371
1372    @Override
1373    public void addPropertyChangeListener(PropertyChangeListener listener) {
1374        pcs.addPropertyChangeListener(listener);
1375    }
1376
1377    @Override
1378    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
1379        pcs.addPropertyChangeListener(propertyName, listener);
1380    }
1381
1382    @Override
1383    public PropertyChangeListener[] getPropertyChangeListeners() {
1384        return pcs.getPropertyChangeListeners();
1385    }
1386
1387    @Override
1388    public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
1389        return pcs.getPropertyChangeListeners(propertyName);
1390    }
1391
1392    @Override
1393    public void removePropertyChangeListener(PropertyChangeListener listener) {
1394        pcs.removePropertyChangeListener(listener);
1395    }
1396
1397    @Override
1398    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
1399        pcs.removePropertyChangeListener(propertyName, listener);
1400    }
1401
1402    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActiveTrain.class);
1403
1404}