# YetAnotherAutoTrain.py -- Data driven automatic train # Use a list of actions to automatically run a train. # v1.3 -- Add line numbers to the compiler error messages. # v1.4 -- Add a master controller that can be used to terminate all of the threads. # v1.5 -- Add signal mast and signal head options. # v1.6 -- Add "Loop" to separate one time startup actions from the main loop. # Add "Repeat" to separate one time finish/clieanup actions from the main loop. # Add "Set route" to invoke a JMRI Route. # Add "Print" to send text to the Script output or system console. # v2.0 -- Add nested If/Else/Endif support. # Add GoSub, Sub and EndSub to support sub routines. # Convert execution flow from multiple "execute" and "skip" modes to treating the token list as program addresses. # v2.1 -- Add support to trigger Dispatcher automatic trains. # Add an optional source for the trainList. The train list statements can be in the TrainList.txt file located in # the yaat directory in the user files location: preference:yaat/TrainList.txt # v2.2 -- Add the ability to use "compiled" trains. # Add the ability to create custom extensions. # v2.3 -- Add Set turntable position command. # v3.0 -- Eliminate the need to modify the script. # Options are at preference:/yaat/config.txt # The preference:/yaat/TrainList.txt file contains the train file names to be loaded during script startup. # Support for inline train definitions has been removed. # Custom extension support has been removed. # Trains can be loaded and started by setting a memory variable to a train file name. # v3.1 -- The wait for seconds command has support for random wait times: Wait for [to ] seconds. # The function key limit has been changed to 68. # v3.2 -- Add hold and release signal heads and masts, misc bug fixes, code improvements. # v3.3 -- Add 'If value for memory is ' # Add 'Wait for memory value to change' # v3.4 -- Add the ability to include text content using '@include '. # # Author: Dave Sand copyright (c) 2018 - 2025 # The help information is available at https://jmri.org/help/en/html/scripthelp/yaat/YAAT.shtml import io import os from time import time import pickle import java import jmri import re from javax.swing import JOptionPane # Location for the yaat config, the train list and the train defintion files. yaatPath = jmri.util.FileUtil.getUserFilesPath() + 'yaat' jmri.util.FileUtil.createDirectory(yaatPath) yaatLocation = yaatPath + jmri.util.FileUtil.SEPARATOR yaatIncludes = None # Load YAAT configuration options. If not found, create a file with default values. try: with open(yaatLocation + 'config.txt') as file: exec(file.read()) except IOError: logLevel = 0 saveYAATcompiles = False enableIncludes = False masterSensor = '' statusSensor = '' yaatMemory = '' # Create the config.txt file with open(yaatLocation + 'config.txt', 'w') as file: file.write("logLevel = 0 # 0 for no output, 4 for the most detail\n") file.write("saveYAATcompiles = False # Load/Save compiled trains\n") file.write("enableIncludes = False # Enable @include support\n") file.write("masterSensor = '' # Optional sensor to stop all threads\n") file.write("statusSensor = '' # Optional sensor to notify JMRI if any threads are active\n") file.write("yaatMemory = '' # Optional memory variable that contains a filename for starting a train\n") if logLevel > 2: print 'yaatLocation: {}'.format(yaatLocation) try: if enableIncludes: jmri.util.FileUtil.createDirectory(yaatPath + jmri.util.FileUtil.SEPARATOR + 'includes') yaatIncludes = yaatLocation + 'includes' + jmri.util.FileUtil.SEPARATOR if logLevel > 2: print 'yaatIncludes location: {}'.format(yaatIncludes) except NameError: # enableIncludes was added at 3.4 and might not be in the existing xconfig file. if logLevel > 3: print 'Handle enableIncludes not found' enableIncludes = False # Set optional objects, will be None if not found yaatMaster = sensors.getSensor(masterSensor) yaatRunning = sensors.getSensor(statusSensor) trainMemory = memories.getMemory(yaatMemory) def setYaatMaster(state): if yaatMaster is not None: yaatMaster.setKnownState(state) def setYaatRunnning(state): if yaatRunning is not None: yaatRunning.setKnownState(state) class YetAnotherAutoTrain(jmri.jmrit.automat.AbstractAutomaton): threadCount = 0 def init(self): self.throttle = None YetAnotherAutoTrain.threadCount += 1 def setup(self, actionList, compileNeeded, fileName): self.actionTokens = [] self.compileMessages = [] self.lineNumber = 0 self.threadName = self.getName() # Note: The Loop, CallSub, Sub, If, Else and Endif addresses are actually the statement after the key word. self.progAddr = 0 self.loopAddr = -1 self.ifList = {} # The list of if statements. The key is the "If" program address. # The value is a tuple with program addresses for Else, and Endif. # The Else address can be zero. self.ifStack = [] # The active "If" program addresses. self.subList = {} # The program address for each sub, keyed using the sub name self.subStack = [] # The active return program addresses if compileNeeded: if logLevel > 1: print 'Compile train {}'.format(self.threadName) self.compile(actionList) if saveYAATcompiles and fileName != '': pickleList = [] pickleList.append(self.actionTokens) pickleList.append(self.ifList) pickleList.append(self.subList) pickleList.append(self.loopAddr) file = open(fileName, 'wb') pickle.dump(pickleList, file) file.close() else: if logLevel > 1: print 'Use the pickle file for train {}'.format(self.threadName) file = open(fileName, 'rb') pickleList = pickle.load(file) file.close() self.actionTokens = pickleList[0] self.ifList = pickleList[1] self.subList = pickleList[2] self.loopAddr = pickleList[3] if logLevel > 2: for ifKey in self.ifList.keys(): elseAddr, endAddr = self.ifList[ifKey] print "if = {}, else = {}, endif = {}".format(ifKey, elseAddr, endAddr) if len(self.ifStack) > 0: self.compileMessages.append('{} - Missing Endif(s)'.format(self.threadName)) if len(self.subStack) > 0: self.compileMessages.append('{} - Missing EndSub(s)'.format(self.threadName)) if len(self.compileMessages) > 1: self.displayMessage("\n".join(self.compileMessages)) YetAnotherAutoTrain.threadCount -= 1 if YetAnotherAutoTrain.threadCount < 1: setYaatRunnning(INACTIVE) return False if len(self.actionTokens) == 0: self.displayMessage('{} - The action list is empty, terminating'.format(self.threadName)) return False setYaatRunnning(ACTIVE) return True def handle(self): if logLevel > 0: print '{} - Start YAAT Program'.format(self.threadName) while True: if self.progAddr >= len(self.actionTokens): self.progAddr = 0 continue action = self.actionTokens[self.progAddr] self.progAddr += 1 if len(action) == 0: self.displayMessage('Empty Action row') continue if logLevel > 2: print '{:3d} :: {} - Action: {}'.format(self.progAddr, self.threadName, action) actionKey = action[0] if actionKey == 'Assign': self.doAssign(action) elif actionKey == 'CallSub': self.doCallSub(action) elif actionKey == 'Dispatch': self.doDispatch(action) elif actionKey == 'Else': ifKey = self.ifStack.pop() elseAddr, endIfAddr = self.ifList[ifKey] self.progAddr = endIfAddr continue # End True block elif actionKey == 'Endif': ifKey = self.ifStack.pop() continue # End Else block elif actionKey == 'EndSub': self.doEndSub(action) elif actionKey == 'Halt': break # Direct execution elif actionKey == 'HoldHead': self.doHoldSignalHead(action) elif actionKey == 'HoldMast': self.doHoldSignalMast(action) elif actionKey == 'IfBlock': self.doIfBlock(action) elif actionKey == 'IfMemoryValue': self.doIfMemoryValue(action) elif actionKey == 'IfSensor': self.doIfSensor(action) elif actionKey == 'IfHead': self.doIfHead(action) elif actionKey == 'IfMast': self.doIfMast(action) elif actionKey == 'IfSpeed': self.doIfSpeed(action) elif actionKey == 'Loop': continue elif actionKey == 'Print': self.doPrint(action) elif actionKey == 'ReleaseHead': self.doReleaseSignalHead(action) elif actionKey == 'ReleaseMast': self.doReleaseSignalMast(action) elif actionKey == 'ReleaseThrottle': self.doReleaseThrottle(action) elif actionKey == 'Repeat': if self.doRepeat(action): self.progAddr = self.loopAddr continue elif actionKey == 'SetBlock': self.doSetBlock(action) elif actionKey == 'SetDirection': self.doSetDirection(action) elif actionKey == 'SetFKey': self.doSetFKey(action) elif actionKey == 'SetMemory': self.doSetMemory(action) elif actionKey == 'SetRoute': self.doSetRoute(action) elif actionKey == 'SetSensor': self.doSetSensor(action) elif actionKey == 'SetSpeed': self.doSetSpeed(action) elif actionKey == 'SetTurnout': self.doSetTurnout(action) elif actionKey == 'SetTurntable': self.doSetTurntable(action) elif actionKey == 'Start': self.doStart(action) elif actionKey == 'Stop': if not self.doStop(action): if logLevel > 0: print '>> Stop YAAT for {} <<'.format(self.threadName) YetAnotherAutoTrain.threadCount -= 1 if YetAnotherAutoTrain.threadCount == 0: setYaatRunnning(INACTIVE) break; elif actionKey == 'Sub': self.progAddr = 0 continue elif actionKey == 'WaitBlock': self.doWaitBlock(action) elif actionKey == 'WaitMemory': self.doWaitMemory(action) elif actionKey == 'WaitSensor': self.doWaitSensor(action) elif actionKey == 'WaitHead': self.doWaitHead(action) elif actionKey == 'WaitMast': self.doWaitMast(action) elif actionKey == 'WaitSpeed': self.doWaitSpeed(action) elif actionKey == 'WaitTime': self.doWaitTime(action) else: self.displayMessage('Action, {}, is not valid'.format(actionKey)) if logLevel > 0: print '{} - End YAAT Program'.format(self.threadName) return False # ------ Perform token commands ------ def doAssign(self, action): if self.throttle != None: return # Throttle already assigned. Normal for subsequent loops act, dccAddress, addrType, trainName, startBlock = action if addrType == 'long': dccLong = True elif addrType == 'short': dccLong = False else: self.displayMessage('{} - DCC address length, {}, is not valid'.format(self.threadName, addrType)) return self.throttle = self.getThrottle(dccAddress, dccLong) if self.throttle == None: self.displayMessage('{} - Unable to assign a throttle.\nCheck the system log for errors.\nScript stopping.'.format(self.threadName)) self.stop() if trainName != '' and startBlock != '': layoutBlock = layoutblocks.getLayoutBlock(startBlock) if layoutBlock is not None: layoutBlock.getBlock().setValue(trainName) def doCallSub(self, action): act, subName = action if not subName in self.subList: self.displayMessage('{} - Sub routine {} not found'.format(self.threadName, subName)) return subAddress = self.subList[subName] returnAddress = self.progAddr self.subStack.append(returnAddress) self.progAddr = subAddress def doDispatch(self, action): act, dispFile, dispType, dispValue = action dispFrame = jmri.InstanceManager.getDefault(jmri.jmrit.dispatcher.DispatcherFrame) x = dispFrame.loadTrainFromTrainInfo(dispFile, dispType, dispValue) if x != 0: self.displayMessage('{} - Dispatcher failed to start, reason code = {}'.format(self.threadName, x)) def doEndSub(self, action): returnAddress = self.subStack.pop() self.progAddr = returnAddress def doHoldSignalHead(self, action): act, headName = action head = signals.getSignalHead(headName) if head is None: self.displayMessage('{} - Signal head {} not found'.format(self.threadName, headName)) return head.setHeld(True) def doHoldSignalMast(self, action): act, mastName = action mast = masts.getSignalMast(mastName) if mast is None: self.displayMessage('{} - Signal mast {} not found'.format(self.threadName, mastName)) return mast.setHeld(True) def doIfBlock(self, action): act, blockName, blockState = action layoutBlock = layoutblocks.getLayoutBlock(blockName) if layoutBlock is None: self.displayMessage('{} - Layout block {} not found'.format(self.threadName, blockName)) return sensor = layoutBlock.getOccupancySensor() if sensor is None: self.displayMessage('{} - Sensor for layout block {} not found'.format(self.threadName, blockName)) return if blockState == 'occupied': currentState = True if sensor.getKnownState() == ACTIVE else False elif blockState == 'unoccupied': currentState = True if sensor.getKnownState() == INACTIVE else False elif blockState == 'reserved': currentState = True if layoutBlock.getUseExtraColor() else False elif blockState == 'free': currentState = True if not layoutBlock.getUseExtraColor() else False else: self.displayMessage('{} - block state, {}, is not valid'.format(self.threadName, blockState)) return self.pushIfState(currentState) def doIfMemoryValue(self, action): act, memoryName, operator, value = action memory = memories.getMemory(memoryName) if memory is None: self.displayMessage('{} - Memory {} not found'.format(self.threadName, memoryName)) return checkState = False currentValue = str(memory.getValue()) if currentValue: if operator == 'eq': if currentValue == value: checkState = True elif operator == 'ne': if currentValue != value: checkState = True elif operator == 'gt': if currentValue > value: checkState = True elif operator == 'lt': if currentValue < value: checkState = True elif operator == 'ge': if currentValue >= value: checkState = True elif operator == 'le': if currentValue <= value: checkState = True else: self.displayMessage('{} - Invalid operator: {}'.format(self.threadName, operator)) return self.pushIfState(checkState) def doIfSensor(self, action): act, sensorName, sensorState = action sensor = sensors.getSensor(sensorName) if sensor is None: self.displayMessage('{} - Sensor {} not found'.format(self.threadName, sensorName)) return if sensorState == 'active': currentState = True if sensor.getKnownState() == ACTIVE else False elif sensorState == 'inactive': currentState = True if sensor.getKnownState() == INACTIVE else False else: self.displayMessage('{} - Sensor state, {}, is not valid'.format(self.threadName, sensorState)) return self.pushIfState(currentState) def doIfHead(self, action): act, headName, stateList, notOption = action head = signals.getSignalHead(headName) if head is None: self.displayMessage('{} - Signal head {} not found'.format(self.threadName, headName)) return currentAppearance = head.getAppearance() checkState = False if notOption: if currentAppearance not in stateList: checkState = True else: if currentAppearance in stateList: checkState = True self.pushIfState(checkState) def doIfMast(self, action): act, mastName, aspectList, notOption = action mast = masts.getSignalMast(mastName) if mast is None: self.displayMessage('{} - Signal mast {} not found'.format(self.threadName, mastName)) return currentAspect = mast.getAspect() checkState = False if notOption: if currentAspect not in aspectList: checkState = True else: if currentAspect in aspectList: checkState = True self.pushIfState(checkState) def doIfSpeed(self, action): act, mastName, operator, speedName = action mast = masts.getSignalMast(mastName) if mast is None: self.displayMessage('{} - Signal mast {} not found'.format(self.threadName, mastName)) return checkState = False signalSystem = mast.getSignalSystem() speedMap = jmri.InstanceManager.getDefault(jmri.implementation.SignalSpeedMap) checkSpeed = speedMap.getSpeed(speedName) aspectName = mast.getAspect() if aspectName is not None: aspectSpeedName = speedMap.getAspectSpeed(aspectName, signalSystem) currentSpeed = speedMap.getSpeed(aspectSpeedName) if operator == 'eq': if currentSpeed == checkSpeed: checkState = True elif operator == 'ne': if currentSpeed != checkSpeed: checkState = True elif operator == 'gt': if currentSpeed > checkSpeed: checkState = True elif operator == 'lt': if currentSpeed < checkSpeed: checkState = True elif operator == 'ge': if currentSpeed >= checkSpeed: checkState = True elif operator == 'le': if currentSpeed <= checkSpeed: checkState = True else: self.displayMessage('{} - Invalid operator: {}'.format(self.threadName, operator)) return else: self.displayMessage('{} - Aspect for signal mast {} not found'.format(self.threadName, mastName)) return self.pushIfState(checkState) def doPrint(self, action): act, printText = action print '{} - {}'.format(self.threadName, printText) def doReleaseSignalHead(self, action): act, headName = action head = signals.getSignalHead(headName) if head is None: self.displayMessage('{} - Signal head {} not found'.format(self.threadName, headName)) return head.setHeld(False) def doReleaseSignalMast(self, action): act, mastName = action mast = masts.getSignalMast(mastName) if mast is None: self.displayMessage('{} - Signal mast {} not found'.format(self.threadName, mastName)) return mast.setHeld(False) def doReleaseThrottle(self, action): if self.throttle is not None: self.throttle.release(None) self.throttle = None def doRepeat(self, action): act, repeatName, repeatState = action sensor = sensors.getSensor(repeatName) if repeatState == 'active': chkState = ACTIVE elif repeatState == 'inactive': chkState = INACTIVE else: self.displayMessage('{} - Repeat sensor state, {}, is not valid'.format(self.threadName, repeatState)) return False return sensor.getKnownState() == chkState def doSetBlock(self, action): act, blockName, blockState = action layoutBlock = layoutblocks.getLayoutBlock(blockName) if layoutBlock is None: self.displayMessage('{} - Layout block {} not found'.format(self.threadName, blockName)) return if blockState in ['occupied', 'unoccupied']: sensor = layoutBlock.getOccupancySensor() if sensor is None: self.displayMessage('{} - Sensor for layout block {} not found'.format(self.threadName, blockName)) return if blockState == 'occupied': layoutBlock.getOccupancySensor().setKnownState(ACTIVE) else: layoutBlock.getOccupancySensor().setKnownState(INACTIVE) return useExtra = False # Default to free if blockState == 'reserved': useExtra = True layoutBlock.setUseExtraColor(useExtra) def doSetDirection(self, action): if self.throttle == None: self.displayMessage('{} - Cannot set a direction until a throttle has been assigned'.format(self.threadName)) return act, direction = action if direction == 'forward': dirForward = True elif direction == 'reverse': dirForward = False else: self.displayMessage('{} - Direction value, {}, is not valid'.format(self.threadName, direction)) return self.throttle.setIsForward(dirForward) def doSetFKey(self, action): if self.throttle == None: self.displayMessage('{} - Cannot set a function key until a throttle has been assigned'.format(self.threadName)) return act, keyNum, keyState, keyDuration = action if keyState == 'on': keyOn = True keyOff = False elif keyState == 'off': keyOn = False keyOff = True else: self.displayMessage('{} - Key state value, {}, is not valid'.format(self.threadName, keyState)) return if keyDuration == 0: self.setKey(keyNum, keyOn) else: self.setKey(keyNum, keyOn) self.waitMsec(keyDuration) self.setKey(keyNum, keyOff) def doSetMemory(self, action): act, memoryName, memoryValue = action memory = memories.getMemory(memoryName) if memory is None: self.displayMessage('{} - Memory {} not found'.format(self.threadName, memoryName)) return memory.setValue(memoryValue) def doSetRoute(self, action): act, routeName = action route = routes.getRoute(routeName) if route is None: self.displayMessage('{} - Route {} not found'.format(self.threadName, routeName)) return route.activateRoute() route.setRoute() self.waitMsec(100) def doSetSensor(self, action): act, sensorName, sensorState = action sensor = sensors.getSensor(sensorName) if sensor is None: self.displayMessage('{} - Sensor {} not found'.format(self.threadName, sensorName)) return if sensorState == 'active': newState = ACTIVE elif sensorState == 'inactive': newState = INACTIVE else: self.displayMessage('{} - Sensor state, {}, is not valid'.format(self.threadName, sensorState)) return sensor.setKnownState(newState) def doSetSpeed(self, action): if self.throttle == None: self.displayMessage('{} - Cannot set the speed until a throttle has been assigned'.format(self.threadName)) return act, newSpeed = action self.throttle.setSpeedSetting(newSpeed) def doSetTurnout(self, action): act, turnoutName, turnoutState, turnoutDelay = action turnout = turnouts.getTurnout(turnoutName) if turnout is None: self.displayMessage('{} - Turnout {} not found'.format(self.threadName, turnoutName)) return if turnoutState == 'closed': newState = CLOSED elif turnoutState == 'thrown': newState = THROWN else: self.displayMessage('{} - Turnout state, {}, is not valid'.format(self.threadName, turnoutState)) return turnout.setCommandedState(newState) # Wait up to 5 seconds for feedback for i in range(0, 20): if turnout.getKnownState() == newState: break; if logLevel > 2: print 'Turnout feedback loop: {}'.format(i) self.waitMsec(250) self.waitMsec(turnoutDelay) def doSetTurntable(self, action): act, turntableName, panelName, rayIndex = action editorManager = jmri.InstanceManager.getDefault(jmri.jmrit.display.EditorManager) for layout in editorManager.getAll(jmri.jmrit.display.layoutEditor.LayoutEditor): if layout.getTitle() == panelName: for turntable in layout.getLayoutTurntableViews(): if turntable.getId() == turntableName: for raytrack in turntable.getRayTrackList(): if raytrack.getConnectionIndex() == rayIndex: turntable.setPosition(rayIndex) return self.displayMessage.append('{} - Turntable error: the ray index, {}, is not found'.format(self.threadName, rayIndex)) return self.displayMessage.append('{} - Turntable error : the turntable name, {}, is not found'.format(self.threadName, turntableName)) return self.displayMessage.append('{} - Turntable error: the layout name, {}, is not found'.format(self.threadName, panelName)) def doStart(self, action): act, startName, startState = action sensor = sensors.getSensor(startName) if startState == 'active': self.waitSensorActive(sensor) elif startState == 'inactive': self.waitSensorInactive(sensor) else: self.displayMessage('{} - Start sensor state, {}, is not valid'.format(self.threadName, startState)) def doStop(self, action): act, stopName, stopState = action sensor = sensors.getSensor(stopName) if stopState == 'active': chkState = ACTIVE elif stopState == 'inactive': chkState = INACTIVE else: self.displayMessage('{} - Stop sensor state, {}, is not valid'.format(self.threadName, stopState)) return if sensor.getKnownState() == chkState: # Release throttle if self.throttle is not None: self.throttle.release(None) return False return True def doWaitBlock(self, action): act, blockName, blockState = action layoutBlock = layoutblocks.getLayoutBlock(blockName) if layoutBlock is None: self.displayMessage('{} - Layout block {} not found'.format(self.threadName, blockName)) return if blockState in ['occupied', 'unoccupied']: # Block sensor changes limited to simulation mode sensor = layoutBlock.getOccupancySensor() if sensor is None: self.displayMessage('{} - Sensor for layout block {} not found'.format(self.threadName, blockName)) return if blockState == 'occupied': self.waitSensorActive(sensor) else: self.waitSensorInactive(sensor) return # wait for free - no sensor available so do it the hard way while layoutBlock.getUseExtraColor(): self.waitMsec(1000) return def doWaitMemory(self, action): act, memoryName = action memory = memories.getMemory(memoryName) if memory is None: self.displayMessage('{} - Memory {} not found'.format(self.threadName, memoryName)) return currValue = str(memory.getValue()) newValue = currValue while newValue == currValue: self.waitChange([memory]) newValue = str(memory.getValue()) def doWaitSensor(self, action): act, sensorName, sensorState = action sensor = sensors.getSensor(sensorName) if sensor is None: self.displayMessage('{} - Sensor {} not found'.format(self.threadName, sensorName)) return if sensorState == 'active': self.waitSensorActive(sensor) elif sensorState == 'inactive': self.waitSensorInactive(sensor) else: self.displayMessage('{} - Sensor state, {}, is not valid'.format(self.threadName, sensorState)) def doWaitHead(self, action): act, headName, stateList, notOption = action head = signals.getSignalHead(headName) if head is None: self.displayMessage('{} - Signal head {} not found'.format(self.threadName, headName)) return while True: currentAppearance = head.getAppearance() if notOption: if currentAppearance not in stateList: return else: if currentAppearance in stateList: return self.waitChange([head]) def doWaitMast(self, action): act, mastName, aspectList, notOption = action mast = masts.getSignalMast(mastName) if mast is None: self.displayMessage('{} - Signal mast {} not found'.format(self.threadName, mastName)) return while True: currentAspect = mast.getAspect() if notOption: if currentAspect not in aspectList: return else: if currentAspect in aspectList: return self.waitChange([mast]) def doWaitSpeed(self, action): act, mastName, aspectSpeed = action mast = masts.getSignalMast(mastName) if mast is None: self.displayMessage('{} - Signal mast {} not found'.format(self.threadName, mastName)) return signalSystem = mast.getSignalSystem() speedMap = jmri.InstanceManager.getDefault(jmri.implementation.SignalSpeedMap) while True: aspectName = mast.getAspect() if aspectName is not None: speedName = speedMap.getAspectSpeed(aspectName, signalSystem) currentSpeed = speedMap.getSpeed(speedName) print aspectName, speedName, currentSpeed if currentSpeed >= aspectSpeed: return self.waitChange([mast]) def doWaitTime(self, action): act, time1, time2 = action delay = time1 if time1 != time2: delay = java.util.Random().nextInt(time2 - time1) + time1 if logLevel > 1: print 'Randon time delay = {} :: {} :: {}'.format(delay, time1, time2) self.waitMsec(delay) def setKey(self, keyNum, keyOn): if logLevel > 2: print "{} - Function key = {}, On = {}".format(self.threadName, keyNum, keyOn) command = 'self.throttle.setF' + str(keyNum) if keyOn: command += '(True)' else: command += '(False)' exec(command) # ------ General Functions ------ def displayMessage(self, msg): JOptionPane.showMessageDialog(None, msg, 'YAAT Error', JOptionPane.WARNING_MESSAGE) def createIf(self): key = len(self.actionTokens) self.ifList[key] = (0, 0) self.ifStack.append(key) def pushIfState(self, state): key = self.progAddr if not key in self.ifList: self.displayMessage('{} - ifList entry not found for key {}'.format(self.threadName, key)) return elseAddr, endIfAddr = self.ifList[key] if state: # Condition is true, execute the first or only block self.ifStack.append(key) else: if elseAddr != 0: # Condition is false, jump to the Else address and execute the Else block self.progAddr = elseAddr self.ifStack.append(key) else: self.progAddr = endIfAddr # Condition is false with no Else, jump to the Endif address # ------ Convert the text phrases to tokens ------ def compile(self, actionList): self.compileMessages.append('---- {} Compiler Errors ----'.format(self.threadName)) for line in actionList: self.lineNumber += 1 words = line.split() if len(words) == 0: continue if words[0][:1] == '#': continue if words[0] == 'Assign': self.compileAssign(line) elif words[0] == 'CallSub': self.compileCallSub(line) elif words[0] == 'Dispatch': self.compileDispatch(line) elif words[0] == 'Else': self.compileElse(line) elif words[0] == 'Endif': self.compileEndIf(line) elif words[0] == 'EndSub': self.compileEndSub(line) elif words[0] == 'Halt': if logLevel > 2: print ' {} - {}'.format(self.threadName, line) self.actionTokens.append(['Halt']) elif words[0] == 'Hold' and words[1] == 'signal' and words[2] == 'head': self.compileHoldSignalHead(line) elif words[0] == 'Hold' and words[1] == 'signal' and words[2] == 'mast': self.compileHoldSignalMast(line) elif words[0] == 'If' and words[1] == 'block': self.compileIfBlock(line) elif words[0] == 'If' and words[1] == 'value' and words[3] == 'memory': self.compileIfMemoryValue(line) elif words[0] == 'If' and words[1] == 'sensor': self.compileIfSensor(line) elif words[0] == 'If' and words[1] == 'signal' and words[2] == 'head': self.compileIfSignalHead(line) elif words[0] == 'If' and words[1] == 'signal' and words[2] == 'mast': self.compileIfSignalMast(line) elif words[0] == 'If' and words[1] == 'speed' and words[4] == 'mast': self.compileIfSignalSpeed(line) elif words[0] == 'Loop': self.compileLoop(line) elif words[0] == 'Print': self.compilePrint(line) elif words[0] == 'Repeat': self.compileRepeat(line) elif words[0] == 'Release' and words[1] == 'signal' and words[2] == 'head': self.compileReleaseSignalHead(line) elif words[0] == 'Release' and words[1] == 'signal' and words[2] == 'mast': self.compileReleaseSignalMast(line) elif words[0] == 'Release': self.compileReleaseThrottle(line) elif words[0] == 'Set' and words[1] == 'block': self.compileSetBlock(line) elif words[0] == 'Set' and words[1] == 'direction': self.compileSetDirection(line) elif words[0] == 'Set' and words[1] == 'function': self.compileSetFKey(line) elif words[0] == 'Set' and words[1] == 'memory': self.compileSetMemory(line) elif words[0] == 'Set' and words[1] == 'route': self.compileSetRoute(line) elif words[0] == 'Set' and words[1] == 'sensor': self.compileSetSensor(line) elif words[0] == 'Set' and words[1] == 'speed': self.compileSetSpeed(line) elif words[0] == 'Set' and words[1] == 'turnout': self.compileSetTurnout(line) elif words[0] == 'Set' and words[1] == 'turntable': self.compileSetTurntable(line) elif words[0] == 'Start': self.compileStart(line) elif words[0] == 'Stop': self.compileStop(line) elif words[0] == 'Sub': self.compileSub(line) elif words[0] == 'Wait' and words[1] == 'for' and words[2] == 'block': self.compileWaitBlock(line) elif words[0] == 'Wait' and words[1] == 'for' and words[2] == 'memory': self.compileWaitMemory(line) elif words[0] == 'Wait' and words[1] == 'for' and words[2] == 'sensor': self.compileWaitSensor(line) elif words[0] == 'Wait' and words[1] == 'for' and words[3] == 'head': self.compileSignalHead(line) elif words[0] == 'Wait' and words[1] == 'for' and words[3] == 'mast': self.compileSignalMast(line) elif words[0] == 'Wait' and words[1] == 'while' and words[2] == 'signal': self.compileSignalSpeed(line) elif words[0] == 'Wait' and words[1] == 'for' and 'second' in line: self.compileWaitTime(line) else: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) def compileAssign(self, line): # Assign address [[ as ] in ] if logLevel > 2: print ' {} - {}'.format(self.threadName, line) words = line.split() flds = 2 regex = '\s*Assign\s+(long|short)\s+address\s+(\d+)' if 'as' in words: regex += '\s+as\s+(.+\S)' flds += 1 if 'in' in words: regex += '\s+in\s+(.+\S)' flds += 1 pattern = re.compile(regex) result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != flds: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return grps = result[0] addrSize = grps[0] try: num = int(grps[1]) except ValueError: self.compileMessages.append('{} - Assign error at line {}: the DCC address, {}, is not a number'.format(self.threadName, self.lineNumber, grps[1])) return addrNum = num trainName = '' if flds < 3 else grps[2] blockName = '' if flds < 4 else grps[3] if blockName != '': layoutBlock = layoutblocks.getLayoutBlock(blockName) if layoutBlock is None: self.compileMessages.append('{} - Assign error at line {}: start block "{}" does not exist'.format(self.threadName, self.lineNumber, blockName)) return self.actionTokens.append(['Assign', addrNum, addrSize, trainName, blockName]) def compileCallSub(self, line): # CallSub if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*CallSub\s+(.+\S)') result = re.findall(pattern, line) if len(result) == 0: # or len(result[0]) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return self.actionTokens.append(['CallSub', result[0]]) def compileDispatch(self, line): # Dispatch using file [, type | ROSTER, value | OPERATIONS, value >] if logLevel > 2: print ' {} - {}'.format(self.threadName, line) regex = '\s*Dispatch\s+using\s+file\s+(.+?\S)($|,\s+type\s+(USER|ROSTER|OPERATIONS),\s+value\s+(.+))' pattern = re.compile(regex) result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 4: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return grps = result[0] dispFile = grps[0] dispType = grps[2] dispValue = grps[3] if len(dispType) == 0: dispType = 'NONE' dispValue = '' # Validate type values if dispType == 'USER': try: num = int(dispValue) except ValueError: self.compileMessages.append('{} - Value error at line {}: the DCC address, {}, is not a number' .format(self.threadName, self.lineNumber, dispValue)) return elif dispType == 'ROSTER': rosterEntry = jmri.jmrit.roster.Roster.getDefault().getEntryForId(dispValue) if rosterEntry is None: self.compileMessages.append('{} - Value error at line {}: the roster entry, {}, does not exist' .format(self.threadName, self.lineNumber, dispValue)) return elif dispType == 'OPERATIONS': opsTrain = jmri.InstanceManager.getDefault(jmri.jmrit.operations.trains.TrainManager).getTrainByName(dispValue) if opsTrain is None: self.compileMessages.append('{} - Value error at line {}: the operations train, {}, does not exist' .format(self.threadName, self.lineNumber, dispValue)) return self.actionTokens.append(['Dispatch', dispFile, dispType, dispValue]) def compileElse(self, line): # Else if logLevel > 2: print ' {} - {}'.format(self.threadName, line) if len(self.ifStack) == 0: self.compileMessages.append('{} - Else error at line {}: There is no matching If statement'.format(self.threadName, self.lineNumber)) return key = self.ifStack[len(self.ifStack) - 1] # Get the current if key elseAddr, endAddr = self.ifList[key] # And the Else and Endif addresses elseAddr = len(self.actionTokens) + 1 # Update the Else address self.ifList[key] = (elseAddr, endAddr) # Update the list self.actionTokens.append(['Else']) def compileEndIf(self, line): # Endif if logLevel > 2: print ' {} - {}'.format(self.threadName, line) if len(self.ifStack) == 0: self.compileMessages.append('{} - EndIf error at line {}: There is no matching If statement'.format(self.threadName, self.lineNumber)) return key = self.ifStack.pop() # Get the current if key elseAddr, endAddr = self.ifList[key] # And the If, Else and Endif indexes endAddr = len(self.actionTokens) + 1 # Update the EndIf index self.ifList[key] = (elseAddr, endAddr) # Update the list self.actionTokens.append(['Endif']) def compileEndSub(self, line): # EndSub if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*EndSub\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0: # or len(result[0]) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return self.actionTokens.append(['EndSub', result[0]]) def compileHoldSignalHead(self, line): # Hold signal head if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Hold\s+signal\s+head\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return headName = result[0] head = signals.getSignalHead(headName) if head is None: self.compileMessages.append('{} - Hold signal head error at line {}: head "{}" not found'.format(self.threadName, self.lineNumber, headName)) return if logLevel > 2: print 'HoldHead', headName self.actionTokens.append(['HoldHead', headName]) def compileHoldSignalMast(self, line): # Hold signal mast if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Hold\s+signal\s+mast\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return mastName = result[0] mast = masts.getSignalMast(mastName) if mast is None: self.compileMessages.append('{} - Hold signal mast error at line {}: head "{}" not found'.format(self.threadName, self.lineNumber, mastName)) return self.actionTokens.append(['HoldMast', mastName]) def compileIfBlock(self, line): # If block is if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*If\s+block\s+(.+\S)\s+is\s+(occupied|unoccupied|reserved|free)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return blockName, blockState = result[0] layoutBlock = layoutblocks.getLayoutBlock(blockName) if layoutBlock is None: self.compileMessages.append('{} - Block error at line {}: block {} not found'.format(self.threadName, self.lineNumber, blockName)) return if layoutBlock.getOccupancySensor() is None: self.compileMessages.append('{} - Block error at line {}: occupancy sensor for block {} not found'.format(self.threadName, self.lineNumber, blockName)) return self.actionTokens.append(['IfBlock', blockName, blockState]) self.createIf() def compileIfMemoryValue(self, line): # If value for memory is if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*If\s+value\s+for\s+memory\s+(.+\S)\s+is\s+(eq|ne|gt|lt|ge|le)\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 3: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return memoryName, operator, value = result[0] memory = memories.getMemory(memoryName) if memory is None: self.compileMessages.append('{} - If memory value error at line {}: memory "{}" not found'.format(self.threadName, self.lineNumber, memoryName)) return if not value: self.compileMessages.append('{} - If memory value error at line {}: the value is empty'.format(self.threadName, self.lineNumber)) return if logLevel > 2: print 'IfMemoryValue', memoryName, operator, value self.actionTokens.append(['IfMemoryValue', memoryName, operator, value]) self.createIf() def compileIfSensor(self, line): # If sensor is if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*If\s+sensor\s+(.+\S)\s+is\s+(active|inactive)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return sensorName, sensorState = result[0] if sensors.getSensor(sensorName) is None: self.compileMessages.append('{} - If sensor error at line {}: sensor {} not found'.format(self.threadName, self.lineNumber, sensorName)) return self.actionTokens.append(['IfSensor', sensorName, sensorState]) self.createIf() def compileIfSignalHead(self, line): # If signal head does [not] show [or ...] if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*If\s+signal\s+head\s+(.+\S)\s+does\s+(not\s)?show\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 3: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return headName, optionalNot, headStates = result[0] head = signals.getSignalHead(headName) if head is None: self.compileMessages.append('{} - If signal head error at line {}: head "{}" not found'.format(self.threadName, self.lineNumber, headName)) return notOption = False if optionalNot == 'not ': notOption = True stateList = headStates.split(' or ') stateMap = {} for stateNumber in head.getValidStates(): stateName = head.getAppearanceName(stateNumber) stateMap[stateName] = stateNumber stateNums = [] for state in stateList: state = state.strip() if state in stateMap: stateNums.append(stateMap[state]) else: self.compileMessages.append('{} - If signal head error at line {}: "{}" is not a valid appearance'.format(self.threadName, self.lineNumber, state)) return if len(stateNums) == 0: self.compileMessages.append('{} - If signal head error at line {}: no signal head states found'.format(self.threadName, self.lineNumber)) return if logLevel > 2: print 'IfHead', headName, stateNums, notOption self.actionTokens.append(['IfHead', headName, stateNums, notOption]) self.createIf() def compileIfSignalMast(self, line): # If signal mast does [not] display [or ...] if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*If\s+signal\s+mast\s+(.+\S)\s+does\s+(not\s+)?display\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 3: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return mastName, optionalNot, mastStates = result[0] mast = masts.getSignalMast(mastName) if mast is None: self.compileMessages.append('{} - If signal mast error at line {}: mast "{}" not found'.format(self.threadName, self.lineNumber, mastName)) return notOption = False if optionalNot == 'not ': notOption = True aspectList = mastStates.split(' or ') aspectMap = mast.getValidAspects() aspectNames = [] for aspect in aspectList: aspect = aspect.strip() if aspect in aspectMap: aspectNames.append(aspect) else: self.compileMessages.append('{} - If signal mast error at line {}: "{}" is not a valid aspect'.format(self.threadName, self.lineNumber, aspect)) return if len(aspectNames) == 0: self.compileMessages.append('{} - If signal mast error at line {}: no valid signal mast aspects found'.format(self.threadName, self.lineNumber)) return self.actionTokens.append(['IfMast', mastName, aspectNames, notOption]) self.createIf() def compileIfSignalSpeed(self, line): # If speed for signal mast is if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*If\s+speed\s+for\s+signal\s+mast\s+(.+\S)\s+is\s+(eq|ne|gt|lt|ge|le)\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 3: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return mastName, operator, speedName = result[0] mast = masts.getSignalMast(mastName) if mast is None: self.compileMessages.append('{} - If mast speed error at line {}: mast "{}" not found'.format(self.threadName, self.lineNumber, mastName)) return speedMap = jmri.InstanceManager.getDefault(jmri.implementation.SignalSpeedMap) if speedMap is None: self.compileMessages.append('{} - If mast speed error at line {}: Unexpected error: get SpeedMap'.format(self.threadName, self.lineNumber)) return speedNameList = speedMap.getValidSpeedNames() if speedNameList is None: self.compileMessages.append('{} - If mast speed error at line {}: Unexpected error: getValidSpeedNames'.format(self.threadName, self.lineNumber)) return if speedName not in speedNameList: self.compileMessages.append('{} - If mast speed error at line {}: "{}" is not a valid speed name'.format(self.threadName, self.lineNumber, speedName)) return if logLevel > 2: print 'IfSpeed', mastName, operator, speedName self.actionTokens.append(['IfSpeed', mastName, operator, speedName]) self.createIf() def compileLoop(self, line): # Loop if logLevel > 2: print ' {} - {}'.format(self.threadName, line) if self.loopAddr != -1: self.compileMessages.append('{} - Loop error at line {}: Duplicate Loop statement, only 1 allowed'.format(self.threadName, self.lineNumber)) return self.actionTokens.append(['Loop']) self.loopAddr = len(self.actionTokens) print 'loopAddr = {}'.format(self.loopAddr) def compilePrint(self, line): # Print message text if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Print\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0: # or len(result[0]) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return self.actionTokens.append(['Print', result[0]]) def compileReleaseSignalHead(self, line): # Release signal head if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Release\s+signal\s+head\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return headName = result[0] head = signals.getSignalHead(headName) if head is None: self.compileMessages.append('{} - Release signal head error at line {}: head "{}" not found'.format(self.threadName, self.lineNumber, headName)) return if logLevel > 2: print 'ReleaseHead', headName self.actionTokens.append(['ReleaseHead', headName]) def compileReleaseSignalMast(self, line): # Release signal mast if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Release\s+signal\s+mast\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return mastName = result[0] mast = masts.getSignalMast(mastName) if mast is None: self.compileMessages.append('{} - Release signal mast error at line {}: head "{}" not found'.format(self.threadName, self.lineNumber, mastName)) return self.actionTokens.append(['ReleaseMast', mastName]) def compileReleaseThrottle(self, line): # Release throttle if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Release\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or result[0] != 'throttle': self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return self.actionTokens.append(['ReleaseThrottle']) def compileRepeat(self, line): # Repeat if sensor is if logLevel > 2: print ' {} - {}'.format(self.threadName, line) if self.loopAddr == -1: self.compileMessages.append('{} - Repeat error at line {}: A Loop point has not been defined'.format(self.threadName, self.lineNumber)) return pattern = re.compile('\s*Repeat\s+if\s+sensor\s+(.+\S)\s+is\s+(active|inactive)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return sensorName, sensorState = result[0] if sensors.getSensor(sensorName) is None: self.compileMessages.append('{} - Repeat error at line {}: sensor {} does not exist'.format(self.threadName, self.lineNumber, sensorName)) return self.actionTokens.append(['Repeat', sensorName, sensorState]) def compileSetBlock(self, line): # Set block if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Set\s+block\s+(.+\S)\s+(occupied|unoccupied|reserved|free)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return blockName, blockState = result[0] layoutBlock = layoutblocks.getLayoutBlock(blockName) if layoutBlock is None: self.compileMessages.append('{} - Block error at line {}: block "{}" not found'.format(self.threadName, self.lineNumber, blockName)) return if layoutBlock.getOccupancySensor() is None: self.compileMessages.append('{} - Block error at line {}: occupancy sensor for block {} not found'.format(self.threadName, self.lineNumber, blockName)) return self.actionTokens.append(['SetBlock', blockName, blockState]) def compileSetDirection(self, line): # Set direction to if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Set\s+direction\s+to\s+(forward|reverse)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return self.actionTokens.append(['SetDirection', result[0]]) def compileSetFKey(self, line): # Set function key [, wait seconds] if logLevel > 2: print ' {} - {}'.format(self.threadName, line) words = line.split() flds = 2 regex = '\s*Set\s+function\s+key\s+(\d+)\s+(on|off)' if 'wait' in words: regex += ', wait (\d+) second' flds += 1 pattern = re.compile(regex) result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != flds: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return grps = result[0] try: keyNum = int(grps[0]) except ValueError: self.compileMessages.append('{} - Function key error at line {}: the key value, {}, is not an integer'.format(self.threadName, self.lineNumber, grps[0])) return else: if keyNum < 0 or keyNum > 68: self.compileMessages.append('{} - Function key error at line {}: the key value, {}, is not in the range 0-68'.format(self.threadName, self.lineNumber, grps[0])) return keyState = grps[1] if flds ==2: keyWait = 0 else: try: keyWait = float(grps[2]) except ValueError: self.compileMessages.append('{} - Function key error at line {}: the wait time, {}, is not a number'.format(self.threadName, self.lineNumber, grps[2])) return self.actionTokens.append(['SetFKey', keyNum, keyState, int(keyWait * 1000)]) def compileSetMemory(self, line): # Set memory to if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Set memory\s+(.+\S)\s+to\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return memoryName, memoryValue = result[0] memory = memories.getMemory(memoryName) if memory is None: self.compileMessages.append('{} - Memory error at line {}: memory "{}" not found'.format(self.threadName, self.lineNumber, memoryName)) return self.actionTokens.append(['SetMemory', memoryName, memoryValue]) def compileSetRoute(self,line): # Set route if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Set\s+route\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0: # or len(result[0]) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return routeName = result[0] if routes.getRoute(routeName) is None: self.compileMessages.append('{} - Route error at line {}: route "{}" not found'.format(self.threadName, self.lineNumber, routeName)) return self.actionTokens.append(['SetRoute', routeName]) def compileSetSensor(self, line): # Set sensor if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Set\s+sensor\s+(.+\S)\s+(active|inactive)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return sensorName, sensorState = result[0] if sensors.getSensor(sensorName) is None: self.compileMessages.append('{} - Sensor error at line {}: sensor "{}" not found'.format(self.threadName, self.lineNumber, sensorName)) return self.actionTokens.append(['SetSensor', sensorName, sensorState]) def compileSetSpeed(self, line): # Set speed to <0 to 1.0> if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Set\s+speed\s+to\s+(\S+)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return try: num = float(result[0]) except ValueError: self.compileMessages.append('{} - Train speed error at line {}: the speed, {}, is not a number'.format(self.threadName, self.lineNumber, result[0])) else: if num < 0.0: num = 0.0 if num > 1.0: num = 1.0 self.actionTokens.append(['SetSpeed', num]) def compileSetTurnout(self, line): # Set turnout [, wait seconds] if logLevel > 2: print ' {} - {}'.format(self.threadName, line) words = line.split() flds = 2 regex = '\s*Set\s+turnout\s+(.+\S)\s+(closed|thrown)' if 'wait' in words: regex += ', wait (\d+) second' flds += 1 pattern = re.compile(regex) result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != flds: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return grps = result[0] turnoutName = grps[0] turnoutState = grps[1] turnoutWait = 0 if flds < 3 else grps[2] if turnouts.getTurnout(turnoutName) is None: self.compileMessages.append('{} - Turnout error at line {}: turnout {} not found'.format(self.threadName, self.lineNumber, grps[0])) return try: num = float(turnoutWait) except ValueError: self.compileMessages.append('{} - Turnout error at line {}: the wait time, {}, is not a number'.format(self.threadName, self.lineNumber, turnoutWait)) else: self.actionTokens.append(['SetTurnout', turnoutName, turnoutState, int(num * 1000)]) def compileSetTurntable(self, line): # Set turntable on panel to ray <#> if logLevel > 2: print ' {} - {}'.format(self.threadName, line) words = line.split() flds = 3 regex = '\s*Set\s+turntable\s+(.+)\s+on\s+panel\s(.+)\sto\s+ray\s(\d+)' pattern = re.compile(regex) result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != flds: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return grps = result[0] turntableName = grps[0] panelName = grps[1] rayIndex = grps[2] try: num = int(rayIndex) except ValueError: self.compileMessages.append('{} - Turntable error at line {}: the ray index, {}, is not a number'.format(self.threadName, self.lineNumber, turnoutWait)) return editorManager = jmri.InstanceManager.getDefault(jmri.jmrit.display.EditorManager) for layout in editorManager.getAll(jmri.jmrit.display.layoutEditor.LayoutEditor): if layout.getTitle() == panelName: for turntable in layout.getLayoutTurntableViews(): if turntable.getId() == turntableName: for raytrack in turntable.getRayTrackList(): if raytrack.getConnectionIndex() == num: self.actionTokens.append(['SetTurntable', turntableName, panelName, num]) return self.compileMessages.append('{} - Turntable error at line {}: the ray index, {}, is not found'.format(self.threadName, self.lineNumber, num)) return self.compileMessages.append('{} - Turntable error at line {}: the turntable name, {}, is not found'.format(self.threadName, self.lineNumber, turntableName)) return self.compileMessages.append('{} - Turntable error at line {}: the layout name, {}, is not found'.format(self.threadName, self.lineNumber, panelName)) def compileStart(self, line): # Start when sensor is if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Start\s+when\s+sensor\s+(.+\S)\s+is\s+(active|inactive)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('Syntax error at line {}: {}'.format(line)) return sensorName, sensorState = result[0] if sensors.getSensor(sensorName) is None: self.compileMessages.append('{} - Start error at line {}: sensor {} does not exist'.format(self.threadName, self.lineNumber, sensorName)) return self.actionTokens.append(['Start', sensorName, sensorState]) def compileStop(self, line): # Stop if sensor is if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Stop\s+if\s+sensor\s+(.+\S)\s+is\s+(active|inactive)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return sensorName, sensorState = result[0] if sensors.getSensor(sensorName) is None: self.compileMessages.append('{} - Stop error at line {}: sensor {} does not exist'.format(self.threadName, self.lineNumber, sensorName)) return self.actionTokens.append(['Stop', sensorName, sensorState]) def compileSub(self, line): # Sub if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Sub\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0: # or len(result[0]) != 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return self.actionTokens.append(['Sub', result[0]]) self.subList[result[0]] = len(self.actionTokens) def compileWaitBlock(self, line): # Wait for block to become if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Wait\s+for\s+block\s+(.+\S)\s+to\s+become\s+(occupied|unoccupied|free)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return blockName, blockState = result[0] layoutBlock = layoutblocks.getLayoutBlock(blockName) if layoutBlock is None: self.compileMessages.append('{} - Wait block error at line {}: block {} not found'.format(self.threadName, self.lineNumber, blockName)) return if layoutBlock.getOccupancySensor() is None: self.compileMessages.append('{} - Wait block error at line {}: occupancy sensor for block {} not found'.format(self.threadName, self.lineNumber, blockName)) return self.actionTokens.append(['WaitBlock', blockName, blockState]) return def compileWaitMemory(self, line): # Wait for memory value to change if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Wait\s+for\s+memory\s+(.+\S)\s+value\s+to\s+change') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return memoryName = result[0] if memories.getMemory(memoryName) is None: self.compileMessages.append('{} - Wait memory error at line {}: memory {} not found'.format(self.threadName, self.lineNumber, memoryName)) return self.actionTokens.append(['WaitMemory', memoryName]) return def compileWaitSensor(self, line): # Wait for sensor to become if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Wait\s+for\s+sensor\s+(.+\S)\s+to\s+become\s+(active|inactive)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return sensorName, sensorState = result[0] if sensors.getSensor(sensorName) is None: self.compileMessages.append('{} - Wait sensor error at line {}: sensor {} not found'.format(self.threadName, self.lineNumber, sensorName)) return self.actionTokens.append(['WaitSensor', sensorName, sensorState]) def compileWaitTime(self, line): # Wait for [to ] seconds if logLevel > 2: print ' {} - {}'.format(self.threadName, line) flds = 1 regex = '\s*Wait\s+(for)\s+(\S+)' # Note: capture the word 'for' to force a tuple result if ' to ' in line: regex += '\s+to\s+(\S+)' flds += 1 regex += '\s+second' pattern = re.compile(regex) result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != flds + 1: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return times = result[0] # The first tuple entry is 'for', the 2nd and possible third entries are seconds duration numbers try: time1 = float(times[1]) except ValueError: self.compileMessages.append('{} - Wait time error at line {}: the wait time, {}, is not a number'.format(self.threadName, self.lineNumber, times[1])) return else: if time1 < 0.0: self.compileMessages.append('{} - Wait time error at line {}: the wait time, {}, is less than zero'.format(self.threadName, self.lineNumber, time1)) return time2 = time1 if flds == 2: try: time2 = float(times[2]) except ValueError: self.compileMessages.append('{} - Wait seconds error at line {}: the second time, {}, is not a number'.format(self.threadName, self.lineNumber, times[2])) return else: if not time2 > time1: self.compileMessages.append('{} - Wait seconds error at line {}: the second time, {}, is not greater than the first seconds'.format(self.threadName, self.lineNumber, time2)) return self.actionTokens.append(['WaitTime', int(time1 * 1000), int(time2 * 1000)]) def compileSignalHead(self, line): # Wait for signal head to [not] show [or ...] if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Wait\s+for\s+signal\s+head\s+(.+\S)\s+to\s+(not\s)?show\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 3: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return headName, optionalNot, headStates = result[0] head = signals.getSignalHead(headName) if head is None: self.compileMessages.append('{} - Wait signal head error at line {}: head "{}" not found'.format(self.threadName, self.lineNumber, headName)) return notOption = False if optionalNot == 'not ': notOption = True stateList = headStates.split(' or ') stateMap = {} for stateNumber in head.getValidStates(): stateName = head.getAppearanceName(stateNumber) stateMap[stateName] = stateNumber stateNums = [] for state in stateList: state = state.strip() if state in stateMap: stateNums.append(stateMap[state]) else: self.compileMessages.append('{} - Wait signal head error at line {}: "{}" is not a valid appearance'.format(self.threadName, self.lineNumber, state)) return if len(stateNums) == 0: self.compileMessages.append('{} - Wait signal head error at line {}: no signal head states found'.format(self.threadName, self.lineNumber)) return if logLevel > 2: print 'WaitHead', headName, stateNums, notOption self.actionTokens.append(['WaitHead', headName, stateNums, notOption]) def compileSignalMast(self, line): # Wait for signal mast to [not] display [or ...] if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Wait\s+for\s+signal\s+mast\s+(.+\S)\s+to\s+(not\s)?display\s+(.+\S)') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 3: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return mastName, optionalNot, mastStates = result[0] mast = masts.getSignalMast(mastName) if mast is None: self.compileMessages.append('{} - Wait signal mast error at line {}: mast "{}" not found'.format(self.threadName, self.lineNumber, mastName)) return notOption = False if optionalNot == 'not ': notOption = True aspectList = mastStates.split(' or ') aspectMap = mast.getValidAspects() aspectNames = [] for aspect in aspectList: aspect = aspect.strip() if aspect in aspectMap: aspectNames.append(aspect) else: self.compileMessages.append('{} - Wait signal mast error at line {}: "{}" is not a valid aspect'.format(self.threadName, self.lineNumber, aspect)) return if len(aspectNames) == 0: self.compileMessages.append('{} - Wait signal mast error at line {}: no valid signal mast aspects found'.format(self.threadName, self.lineNumber)) return self.actionTokens.append(['WaitMast', mastName, aspectNames, notOption]) def compileSignalSpeed(self, line): # Wait while signal mast speed is less than speed if logLevel > 2: print ' {} - {}'.format(self.threadName, line) pattern = re.compile('\s*Wait\s+while\s+signal\s+mast\s+(.+\S)\s+speed\s+is\s+less\s+than\s+(.+\S)\s+speed') result = re.findall(pattern, line) if logLevel > 3: print ' {} - result = {}'.format(self.threadName, result) if len(result) == 0 or len(result[0]) != 2: self.compileMessages.append('{} - Syntax error at line {}: {}'.format(self.threadName, self.lineNumber, line)) return mastName, aspectName = result[0] mast = masts.getSignalMast(mastName) if mast is None: self.compileMessages.append('{} - Wait mast speed error at line {}: mast "{}" not found'.format(self.threadName, self.lineNumber, mastName)) return aspectMap = mast.getValidAspects() if aspectName not in aspectMap: self.compileMessages.append('{} - Wait mast speed error at line {}: "{}" is not a valid aspect'.format(self.threadName, self.lineNumber, aspectName)) return signalSystem = mast.getSignalSystem() if signalSystem is None: self.compileMessages.append('{} - Wait mast speed error at line {}: Unexpected error: getSignalSystem'.format(self.threadName, self.lineNumber)) return speedMap = jmri.InstanceManager.getDefault(jmri.implementation.SignalSpeedMap) if speedMap is None: self.compileMessages.append('{} - Wait mast speed error at line {}: Unexpected error: get SpeedMap'.format(self.threadName, self.lineNumber)) return speedName = speedMap.getAspectSpeed(aspectName, signalSystem) if speedName is None: self.compileMessages.append('{} - Wait mast speed error at line {}: Unexpected error: getAspectSpeed'.format(self.threadName, self.lineNumber)) return aspectSpeed = speedMap.getSpeed(speedName) if logLevel > 2: print 'WaitSpeed', mastName, aspectName, speedName, aspectSpeed self.actionTokens.append(['WaitSpeed', mastName, aspectSpeed]) # End of class YetAnotherAutoTrain class YAATMaster(jmri.jmrit.automat.AbstractAutomaton): def init(self): if logLevel > 0: print 'Create Master Thread' def setup(self): if yaatMaster is None: return False setYaatMaster(INACTIVE) return True def handle(self): self.waitSensorActive(yaatMaster) for thread in instanceList: if thread is not None: if thread.isRunning(): if logLevel > 0: print 'Stop "{}" thread'.format(thread.getName()) if thread.throttle is not None: thread.throttle.setSpeedSetting(0.0) thread.throttle.release(None) thread.stop() setYaatRunnning(INACTIVE) setYaatMaster(INACTIVE) if memoryListener is not None: memoryListener.removeListener() return False; # End of class YAATMaster # Compile and run a train when the memory variable contains a train file name. class YAATMemoryListener(java.beans.PropertyChangeListener): def __init__(self): if trainMemory is None: return None trainMemory.setValue('') # Clear any residual values trainMemory.removePropertyChangeListener(self) trainMemory.addPropertyChangeListener(self) return def propertyChange(self, event): if event.getPropertyName() == 'value': fileName = str(event.getNewValue()) if fileName is not None and len(fileName) > 4: startTrain(fileName.strip()) def removeListener(self): if trainMemory is not None: trainMemory.removePropertyChangeListener(self) # End of class YAATMemoryListener ## # Check if a compile is needed. The compile content is stored in a file using the Python pickle process. # True if save option not active or source modification time is greater than the pickle file time. ## def compileRequired(fullPath): # Always true when the compile option is not active if not saveYAATcompiles: return (True, '') # Create the yaatp directory if necessary pickleLocation = jmri.util.FileUtil.getUserFilesPath() + 'yaatp' jmri.util.FileUtil.createDirectory(pickleLocation) sourceTime = 0 pickleTime = 0 sourceName = os.path.basename(fullPath) fullPickleLocation = pickleLocation + os.sep + sourceName if os.path.exists(fullPath): sourceTime = os.path.getmtime(fullPath) if os.path.exists(fullPickleLocation): pickleTime = os.path.getmtime(fullPickleLocation) if logLevel > 1: print 'Source file = {}, source time = {}, compile time = {}'.format(sourceName, sourceTime, pickleTime) return (sourceTime > pickleTime, fullPickleLocation) # Check the trainLines list for the @include keyword. Replace the line with the lines from # the included file. def expandIncludes(sourceLines): expandedLines = [] for line in sourceLines: if logLevel > 2: print 'expandIncludes: input line = {}'.format(line) if line[:8] == '@include': includeFName = line[9:].strip() if logLevel > 2: print 'expandIncludes: included file name = {}'.format(includeFName) includeFile = yaatIncludes + includeFName try: with open(includeFile) as file: expandedLines.extend([includeLine.strip() for includeLine in file]) except IOError, e: print 'Error loading include file, error :: {}'.format(e) else: expandedLines.append(line) return expandedLines instanceList = [] # List of train instances def startTrain(fileName): if len(fileName) == 0: return fullName = yaatLocation + fileName trainName = fileName[:-4] compileNeeded, pickleName = compileRequired(fullName) trainLines = [] if compileNeeded: try: with open(fullName) as file: trainLines = [line.strip() for line in file] if enableIncludes: trainLines = expandIncludes(trainLines) except IOError, e: print 'Error loading trainLines, error :: {}'.format(e) idx = len(instanceList) instanceList.append(YetAnotherAutoTrain()) # Add a new instance instanceList[idx].setName(trainName) # Set the instance name if instanceList[idx].setup(trainLines, compileNeeded, pickleName): # Compile the train actions instanceList[idx].start() # Compile was successful print 'YAAT v3.4' startTime = time() try: with open(yaatLocation + 'LoadTrains.txt') as file: for line in file: if len(line) > 4: startTrain(line.strip()) except IOError, e: pass # Ignore file errors since this file is optional endTime = time() if logLevel > 1: print '\nTiming' if logLevel > 1: print (' Load duration: {}').format(endTime - startTime) # Start YAAT memory listener memoryListener = YAATMemoryListener() # Keep last -- create the master thread master = YAATMaster() if master.setup(): master.setName('YAAT Master') master.start()