93 lines
4.8 KiB
Python
93 lines
4.8 KiB
Python
# Simulator for Dispatcher's AutoActiveTrains
|
|
# while auto train(s) are "moving", repeatedly activate "next" allocated block, and deactivate "last" occupied block
|
|
# waits for sensor changes plus a bit, to allow signals, etc. to respond.
|
|
# Runs as a background thread, ends itself when no trains are found in Dispatcher Active Trains list.
|
|
|
|
# NOTE: to enable logging, see https://www.jmri.org/help/en/html/apps/Debug.shtml
|
|
# Add the Logger Category name "jmri.jmrit.jython.exec" at DEBUG Level.
|
|
|
|
import jmri
|
|
import datetime
|
|
from org.slf4j import Logger;
|
|
from org.slf4j import LoggerFactory;
|
|
|
|
log = LoggerFactory.getLogger("jmri.jmrit.jython.exec.AutoActiveTrains_Simulator");
|
|
minLoopMS = 2000 # minimum time in ms allowed for one loop
|
|
extraDelayMS = 250 # extra time in ms for processing
|
|
|
|
# Optional control sensor. If it exists, the main loop can be paused and resumed by changing
|
|
# sensor state. When there are no active trains, the sensor will be set Inactive and the
|
|
# thread will wait for the sensor to become Active again.
|
|
controlSensorName = ''
|
|
|
|
# create a new class to run as thread
|
|
class AutoActiveTrains_Simulator(jmri.jmrit.automat.AbstractAutomaton) :
|
|
|
|
def init(self):
|
|
self.controlSensor = sensors.getSensor(controlSensorName)
|
|
|
|
def handle(self):
|
|
if self.controlSensor is not None: self.waitSensorActive(self.controlSensor)
|
|
DF = jmri.InstanceManager.getDefault(jmri.jmrit.dispatcher.DispatcherFrame)
|
|
trainsList=DF.getActiveTrainsList() #loop thru all trains
|
|
if (trainsList.size() == 0): # kill the thread if no trains found TODO: add something outside to restart
|
|
if self.controlSensor is not None:
|
|
self.controlSensor.setKnownState(INACTIVE)
|
|
return True
|
|
else:
|
|
log.info("AutoActiveTrains_Simulator thread ended")
|
|
return False # no trains, end
|
|
start_time = datetime.datetime.now()
|
|
# loop through all trains
|
|
for i in range(trainsList.size()):
|
|
at = trainsList.get(i) #: :type at: ActiveTrain
|
|
if (at.getAutoRun()): #ignore if not auto
|
|
aat=at.getAutoActiveTrain()
|
|
targetSpeed = aat.getTargetSpeed()
|
|
bl = at.getBlockList()
|
|
lastBlock = None #most-rear occupied block of train
|
|
nextBlock = None #first unoccupied block allocated to train
|
|
occupiedBlocks = 0
|
|
for j in range(bl.size()): #look for first NOT-allocated block (may be NONE)
|
|
b = bl.get(j)
|
|
if (b.getState()==jmri.Block.OCCUPIED):
|
|
if (lastBlock==None):
|
|
lastBlock = b
|
|
occupiedBlocks += 1
|
|
elif (occupiedBlocks > 0): # ignore any initial unoccupied blocks
|
|
nextBlock = b
|
|
break
|
|
log.debug(at.getTrainName() + ": occupiedBlocks: " + str(occupiedBlocks)
|
|
+ " next:" + ("None" if (nextBlock==None) else str(nextBlock.getDisplayName()))
|
|
+ " last:" + ("None" if (lastBlock==None) else str(lastBlock.getDisplayName()))
|
|
+ " speed:" + str(targetSpeed))
|
|
if ((nextBlock != None) and (targetSpeed > 0)): # occupy next block if moving
|
|
s = nextBlock.getSensor()
|
|
sn = s.getSystemName()
|
|
s = sensors.getSensor(sn)
|
|
if s.getKnownState() != ACTIVE:
|
|
log.debug(at.getTrainName() + ": set {} ON for block {}", sn,
|
|
nextBlock.getDisplayName())
|
|
s.setKnownState(ACTIVE)
|
|
self.waitSensorActive(s)
|
|
self.waitMsec(extraDelayMS) # extra time for handling change
|
|
if occupiedBlocks > 1: # unoccupy trailing block TODO: change this to check train length
|
|
s = lastBlock.getSensor()
|
|
sn = s.getSystemName()
|
|
s = sensors.getSensor(sn)
|
|
if s.getKnownState() != INACTIVE:
|
|
log.debug(at.getTrainName() + ": set {} OFF for block {}", sn,
|
|
lastBlock.getDisplayName())
|
|
s.setKnownState(INACTIVE)
|
|
self.waitSensorInactive(s)
|
|
self.waitMsec(extraDelayMS) # extra time for handling change
|
|
#pause for at least min specified time
|
|
elapsedMS = int((datetime.datetime.now() - start_time).total_seconds() * 1000)
|
|
if elapsedMS < minLoopMS:
|
|
self.waitMsec(minLoopMS - elapsedMS)
|
|
|
|
return True # keep looping
|
|
|
|
aats = AutoActiveTrains_Simulator("AutoActiveTrains_Simulator") # setup the thread class
|
|
aats.start() # run until it ends itself
|