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}