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