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