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