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