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