# This script runs a loco around the track, controlling the speed # according to signals and following the blocks. # # Author: Ken Cameron, copyright 2009 # Part of the JMRI distribution # # The next line is maintained by CVS, please don't change it # $Revision: 1.20 $ # # The start button is inactive until data has been entered. # # The test button is to force re-evaluation of all block and signal values. # # If the loco id isn't showing in the block table, do a 'stop'/'start the # run' to reestablish the loco position. # # Notes: # 1. This script expects a fully developed set of blocks and signals # to exist. # 2. Best way for that is using the 'Layout Editor' to develop the # configuration. # 3. However, on turnouts, the non-throat (frog) ends of the turnout should # not have a track segment attached, the block boundary must be the # turnout anchor point. The throat end (points) may have additional # track segments in the same block as the turnout. # 4. The layout must be laid out like a CTC type panel, meaning string line # left to right across the panel. If you need to have multiple lines # to build the whole layout place a track segment of the same block # at the end of the first line and the start of the next line and # use a 'hidden' track segment (same block) to connect # those track segments. # 5. The script keys off the loco id being passed from block to block # by the block manager. If you have detector issues and the blocks # falsely cycle, this may not work. You may have to add internal # sensors to 'buffer' these false cycles using Logix to clean # things up. # 6. FIXED: You no longer need usernames for everything. It will use # system or user names depending on what you have in your panel. # 7. If you create memory values and memory labels on your panel and # tie them to the blocks, you will always see the loco id displayed. # That is the block value that the script is looking for when # tracking the loco. # NEW FOR Version 2 # 8. It now has options for a rate of speed (still needs the matching # throttle setting) for speeds to compute stopping. But it only uses # this for stopping on approach to a red signal. When this is added as # an 'autopilot' to the throttle interface, it will consider more of # these inputs. A zero in the rate will cause it to ignore it. # 9. If the blocks have the lenght set (and the rate above) it will try # to delay stopping in a red block until 80% into the block. # # Still needing work: # 1. Improve the method for 'findNextBlock' as it currently uses the # direction from the current block. To work for 'physically designed' # panels, it would need to focus on the connectivity of the blocks # more and less on the direction attributes. It currently fails # if a block transitioned more than 90 degrees within a single # block. # 2. Add option to delay halting if it looses sight of the loco. # This may help some layouts where the detection 'blinks' out # for a few seconds due to dirt etc... # Currently you must use something like 'DebounceSensor.py' if you # have sensors that don't hold due to the above. # 3. Learn about the neat features the 'operations' data is adding. # This would help with knowing lengths of trains, grades of track, # etc to aid in controlling how to stop within a block. # # Changes: # 07/23/2009 - Added a Halt button that tries to -1 the throttle to # stop now and not be smooth. Plus a few other fixes # like the system vs user name thing. # # 08/30/2009 - Added methods so this can be run from a master script # for running mulitple throttles. Also improved many # reliblity issues in the code. # # Much thanks go to the Medina Railroad Museum who was asked for # something to help out when they have larger number of visitors # and few staff but want the trains to run around without taking # staff to keep them from running into each other. What started # as a simple request grew into this script. I am planning on # turning it into real code as a next step. # http://railroadmuseum.net/ # # Portions of this script are taken from a number of scripts # by Bob Jacobsen # # A AbstractAutomaton is used to get a throttle and run the loco. # # A PropertyChangeListener is used to report block information. # It could have been with AbstractAutomaton but this allows for # independent action with a block change. # # Same thing for also watching signals. # # A WindowListener is used to report when the window is closing and # allow for the removal of the PropertyChangeListener. # import java import javax.swing # set up a throttle with the loco address class LocoThrot(jmri.jmrit.automat.AbstractAutomaton) : # initialize variables locoAddress = None currentBlock = None currentDir = jmri.Path.NONE currentBlocks = [] currentNextBlocks = [] priorBlocks = [] priorNextBlocks = [] currentSignal = None currentSignalAspect = None isRunning = False currentThrottle = None scriptFrame = None scriptFrameOldX = None scriptFarmeOldY = None listenerBlocks = [] listenerBlockListeners = [] listenerSignals = [] listenerSignalListeners = [] greenSignalIcon = None greenFlashSignalIcon = None yellowSignalIcon = None yellowFlashSignalIcon = None redSignalIcon = None redFlashSignalIcon = None darkSignalIcon = None unknownSignalIcon = None redDelayTimer = None redDelayListener = None shrinkGrow = True fullScrollRows = 15 speedPane = None rosterInstance = None oldLocoAddress = None didWeMoveCounter = 0 holdMoving = False stopBlock = None hornDelayTimer = None hornDelayListener = None throttleManager = None askChangeThrottle = False askFinishStartButton = False methodLocoAddress = None methodBlockDirection = None methodLocoDirection = None methodLocoHeadlight = None methodShortHorn = None methodLongHorn = None methodBlockStart = None methodPushShrink = None methodPushStart = None methodPushStop = None methodPushTest = None methodBlockStop = None methodLocoDistanceRedStop = None def init(self): #print("start begin:.\n") self.setup() #print("start end:.\n") return def handle(self): #self.msgText("handle begin:.\n") self.waitMsec(1000) if (self.askChangeThrottle) : self.getNewThrottle() self.askChangeThrottle = False if (self.askFinishStartButton) : self.doFinishStartButton() self.askFinishStartButton = False if (self.methodLocoAddress != None) : self.locoAddress.text = self.methodLocoAddress self.methodLocoAddress = None self.whenLocoChanged(self) if (self.methodBlockDirection != None) : if (self.methodBlockDirection == True) : self.blockDirection.setSelected(True) else : self.blockDirection.setSelected(False) self.methodBlockDirection = None self.whenLocoChanged(self) if (self.methodLocoDirection != None) : if (self.methodLocoDirection == True) : self.locoForward.setSelected(True) else : self.locoForward.setSelected(False) self.methodLocoDirection = None self.whenLocoChanged(self) if (self.methodLocoHeadlight != None) : if (self.methodLocoHeadlight == True) : self.locoHeadlight.setSelected(True) else : self.locoHeadlight.setSelected(False) self.methodLocoHeadlight = None self.whenLocoHeadlight(self) if (self.methodShortHorn != None) : self.methodShortHorn = None self.doShortHorn() if (self.methodLongHorn != None) : self.methodLongHorn = None self.doLongHorn() if (self.methodBlockStart != None) : self.blockStart.text = self.methodBlockStart self.methodBlockStart = None self.whenLocoChanged(self) if (self.methodPushShrink != None) : self.methodPushShrink = None self.whenShrinkButtonClicked(self) if (self.stopButton.isEnabled() == True and self.methodPushStop != None) : self.methodPushStop = None self.whenStopButtonClicked(self) if (self.testButton.isEnabled() == True and self.methodPushTest != None) : self.methodPushTest = None self.callBackForDidWeMove(self) if (self.startButton.isEnabled() == True and self.methodPushStart != None) : self.methodPushStart = None self.whenStartButtonClicked(self) if (self.methodBlockStop != None) : self.blockStop.text = self.methodBlockStop self.methodBlockStop = None self.whenStopBlockChanged(self) if (self.methodLocoDistanceRedStop != None) : self.locoDistanceRedStop.text = self.methodLocoDistanceRedStop self.methodLocoDistanceRedStop = None self.whenLocoChanged(self) # This handles tracking how many overlapping events happened # and insures we run the didWeMove the right number of times if (self.didWeMoveCounter > 0 and self.holdMoving == False) : #self.msgText("didWeMoveCounterCheck: " + self.didWeMoveCounter.toString() + " - calling didWeMove\n") self.didWeMove() self.didWeMoveCounter = 0 #self.msgText("didWeMoveCounterCheck: decremented counter down to " + self.didWeMoveCounter.toString() + "\n") #self.msgText("handle done\n") return 1 #continue if 1, run once if 0 def getNewThrottle(self) : self.holdMoving = True if (self.currentThrottle != None) : oldId = self.currentThrottle.getLocoAddress() self.msgText("stop and release the current loco: " + oldId.toString() + "\n") self.doStop(); self.currentThrottle.release() self.currentThrottle = None self.msgText("Throttle " + oldId.toString() + " released\n") self.msgText("Getting throttle - ") #add text to scroll field id = int(self.locoAddress.text) if (self.throttleManager.addressTypeUnique() == True) : #self.msgText("id values are not ambiguous\n") isLong = self.throttleManager.canBeLongAddress(id) else : self.msgText("id values are ambiguous\n") isLong = True if (self.throttleManager.canBeShortAddress(id)) : self.msgText("id could be a short.\n") if (self.locoLong.isSelected() == False) : isLong = False self.msgText(" getting " + id.toString() + " " + isLong.toString() + " - ") throttle = self.getThrottle(id, isLong) self.currentThrottle = throttle if (self.currentThrottle == None) : self.msgText("Couldn't assign throttle! - Run stopped\n") self.doHalt() else : #self.msgText("got throttle: " + self.currentThrottle.getLocoAddress().toString() + "\n") self.currentThrottle.setIsForward(self.locoForward.isSelected()) self.currentThrottle.setF0(self.locoHeadlight.isSelected()) self.currentThrottle.setF1(self.locoBell.isSelected()) self.holdMoving = False return # return userName if available, else systemName def giveBlockName(self, block) : if (block == None) : return 'None' else : if ((block.getUserName() == None) or (block.getUserName() == '')) : return block.getSystemName() else : return block.getUserName() # return userName if available, else systemName def giveSignalName(self, sig) : if (sig == None) : return 'None' else : if ((sig.getUserName() == None) or (sig.getUserName() == '')) : return sig.getSystemName() else : return sig.getUserName() # return userName if available, else systemName def giveTurnoutName(self, to) : if (to == None) : return 'None' else : if ((to.getUserName() == None) or (to.getUserName() == '')) : return to.getSystemName() else : return to.getUserName() # Isolate the callback handling from the didWeMove processing # this makes dealing with multiple events simpler def callBackForDidWeMove(self, event) : self.didWeMoveCounter = self.didWeMoveCounter + 1 #self.msgText("callBackForDidWeMove(" + event.toString() + ")\n counter = " + self.didWeMoveCounter.toString() + "\n") # the handle() will invoke the didWeMove() as needed return # figure out if we moved and where def didWeMove(self) : if (self.currentThrottle == None) : #self.msgText("didWeMove called while currentThrottle was None\n") return #if (self.currentBlock != None) : #self.msgText("Current block: " + self.giveBlockName(self.currentBlock) + "\n") newCurrentBlocks = self.findCurrentBlocks() if (len(newCurrentBlocks) == 0) : self.msgText("Can't find loco!! Doing halt!!") self.doHalt() # new current block must be farthest connected to current block in current direction chain oldCurrent = self.currentBlock oldSignal = self.currentSignal oldAspect = self.currentSignalAspect tryBlock = self.currentBlock newCurrent = None giveUpTimer = 0 while (giveUpTimer < 10) : giveUpTimer = giveUpTimer + 1 newCurrent = self.findNewCurrentBlock(tryBlock, newCurrentBlocks, self.currentDir) if (newCurrent == None) : newBlockText = "None" else : newBlockText = self.giveBlockName(newCurrent) #self.msgText("try " + giveUpTimer.toString() + " " + self.giveBlockName(tryBlock) + " " + newBlockText + "\n") if ((newCurrent == tryBlock) or (newCurrent == None)) : break else : tryBlock = newCurrent if tryBlock != None : self.blockNow.text = " " self.blockNowLength.text = " " self.blockNext.text = " " self.blockNextLength.text = " " self.blockBeyond.text = " " self.blockBeyondLength.text = " " self.currentBlock = tryBlock self.blockStart.text = self.giveBlockName(tryBlock) self.blockNow.text = self.giveBlockName(self.currentBlock) self.blockNowLength.text = self.currentBlock.getLengthIn().toString() self.nextBlock = self.findNextBlock(self.currentBlock) self.testAddBlockListener(self.currentBlock) if (self.nextBlock != None) : self.blockNext.text = self.giveBlockName(self.nextBlock) self.blockNextLength.text = self.nextBlock.getLengthIn().toString() self.beyondBlock = self.findNextBlock(self.nextBlock) self.testAddBlockListener(self.nextBlock) if (self.beyondBlock != None) : self.blockBeyond.text = self.giveBlockName(self.beyondBlock) self.blockBeyondLength.text = self.beyondBlock.getLengthIn().toString() self.testAddBlockListener(self.beyondBlock) self.priorBlock = oldCurrent self.priorBlocks = self.currentBlocks # find next block from currentBlock if (self.currentBlock != None) : nearSig = jmri.InstanceManager.layoutBlockManagerInstance().getFacingSignalHead(self.currentBlock, self.nextBlock) farSig = jmri.InstanceManager.layoutBlockManagerInstance().getFacingSignalHead(self.nextBlock, self.beyondBlock) if (self.blockAhead2.isSelected() == False) : # if only looking ahead to next block self.msgText("3 block test: " + self.giveBlockName(self.currentBlock) + ":" + self.giveBlockName(self.nextBlock) + ":" + self.giveBlockName(oldCurrent) + " signals:" + self.giveSignalName(oldSignal) + ":" + self.giveSignalName(nearSig) + "\n") if (oldCurrent == None or oldCurrent != self.currentBlock) : # we moved somewhere #self.msgText("We moved or started up.\n") self.findNewSpeed(self.currentBlock, self.nextBlock) elif (oldSignal == None) : # we didn't move, it is startup #self.msgText("Not move, startup.\n") self.findNewSpeed(self.currentBlock, self.nextBlock) elif (oldSignal != nearSig or oldAspect != nearSig.getAppearance()) : # we didn't move, but a signal changed #self.msgText("Not move, signal changed.\n") if (self.compareSignalAspects(oldAspect, nearSig.getAppearance()) < 0) : # signal dropped, that's bad self.msgText("Signal dropped in front of train. Halting!!\n") self.doHalt() else : self.findNewSpeed(self.currentBlock, self.nextBlock) else : # if looking ahead beyond block (4 block system) self.msgText("4 block test: " + self.giveBlockName(self.nextBlock) + "\n") if (self.beyondBlock == None) : self.msgText("failed to find next block: " + self.giveBlockName(self.nextBlock) + ":" + self.giveBlockName(self.beyondBlock) + "\n") self.doHalt() else : if (oldCurrent == None or oldCurrent != self.currentBlock) : # we moved somewhere self.findNewSpeed(self.nextBlock, self.beyondBlock) elif (oldSignal == None) : # we didn't move, it is startup self.findNewSpeed(self.nextBlock, self.beyondBlock) elif (oldSignal != farSig or oldAspect != farSig.getAppearance()) : # we didn't move, but a signal changed if (self.compareSignalAspects(oldAspect, farSig) < 0) : # signal dropped, that's bad self.msgText("Signal dropped in front of train. Halting!!\n") self.doHalt() else : self.findNewSpeed(self.nextBlock, self.beyondBlock) # this is for display updates if (nearSig != None) : self.signalNext.setIcon(self.cvtAppearanceIcon(nearSig)) self.signalNextText.text = self.cvtAppearanceText(nearSig) self.testAddSignalListener(nearSig) if (farSig != None) : self.signalBeyond.setIcon(self.cvtAppearanceIcon(farSig)) self.signalBeyondText.text = self.cvtAppearanceText(nearSig) self.testAddSignalListener(farSig) else : if (self.beyondBlock == None) : self.msgText("No far block, can't get a signal!\n") else : self.msgText("No signal found for far block: " + self.giveBlockName(self.beyondBlock) + "\n") if (self.isRunning) : self.doHalt() else : self.msgText("No signal found for next block: " + self.giveBlockName(self.nextBlock) + "\n") if (self.isRunning) : self.doHalt() # if we have a stop block if (self.stopBlock != None and self.currentBlock == self.stopBlock) : if (self.currentThrottle.getSpeedSetting() == 0) : self.msgText("Found stop block, doing stop.\n") self.doStop() #self.msgText("didMove: done\n") return # return true if thing is in thingList def isInList(self, thing, thingList) : found = False for b in thingList : if (b == thing) : found = True return found # see if block is in the listenerBlocks, add listener if not def testAddBlockListener(self, bk) : if (self.isInList(bk, self.listenerBlocks) == False) : # isn't in list, setup listener and add to list bl = self.BlockListener() bl.setCallBack(self.callBackForDidWeMove) bk.addPropertyChangeListener(bl) self.listenerBlocks.append(bk) self.listenerBlockListeners.append(bl) return # see if signal is in the listenerSignals, add listener if not def testAddSignalListener(self, sig) : if (self.isInList(sig, self.listenerSignals) == False) : # isn't in list, setup listener and add to list sl = self.SignalListener() sl.setCallBack(self.callBackForDidWeMove) sig.addPropertyChangeListener(sl) self.listenerSignals.append(sig) self.listenerSignalListeners.append(sl) return # release all listeners def releaseAllListeners(self, event) : i = 0 while(len(self.listenerBlockListeners) > i) : b = self.listenerBlocks[i] self.msgText("releasing listener for block " + self.giveBlockName(b) + "\n") b.removePropertyChangeListener(self.listenerBlockListeners[i]) i = i + 1 i = 0 while(len(self.listenerSignalListeners) > i) : s = self.listenerSignals[i] self.msgText("releasing listener for signal " + self.giveSignalName(s) + "\n") s.removePropertyChangeListener(self.listenerSignalListeners[i]) i = i + 1 if (self.redDelayTimer != None) : for i in self.redDelayTimer.getActionListeners() : self.redDelayTimer.removeActionListener(i) if (self.hornDelayTimer != None) : for i in self.hornDelayTimer.getActionListeners() : self.hornDelayTimer.removeActionListener(i) if (self.currentThrottle != None) : self.currentThrottle.release() return # take list of new current blocks, a current block, and a current direction # return new current block at edge of current blocks def findNewCurrentBlock(self, cBlock, cList, cDir) : nBlock = None if (cDir == jmri.Path.NONE) : if (self.blockDirection.isSelected() == True) : cDir = cDir or jmri.Path.EAST else : cDir = cDir or jmri.Path.WEST if (cBlock == None) : self.msgText("findNewCurrentBlock, bad current block passed!\n") return None if (len(cList) <= 0) : self.msgText("findNewCurrentBlock, empty cList\n") else : pList = cBlock.getPaths() for p in pList : pB = p.getBlock() if (p.checkPathSet()) : dirTest = p.getToBlockDirection() #self.msgText("findNewCurrentBlock testing for " + jmri.Path.decodeDirection(cDir) + " from " + self.giveBlockName(cBlock) + " vs " + self.giveBlockName(pB) + " pointing " + jmri.Path.decodeDirection(dirTest) + "\n") if (cDir & dirTest == cDir) : for c in cList : if (c == pB) : nBlock = pB #self.msgText("findNewCurrentBlock found " + self.giveBlockName(pB) + "\n") break #else : #self.msgText("findNewCurrentBlock not in cList: " + self.giveBlockName(c) + "\n") if (nBlock != None) : break #else : #self.msgText("findNewCurrentBlock path not traversable: " + self.giveBlockName(cBlock) + " to " + self.giveBlockName(pB) + "\n") return nBlock # figure out signal names and decide speeds def findNewSpeed(self, cBlock, nBlock) : if (self.isRunning) : if (cBlock == None) : if (nBlock == None) : self.msgText("Failed to find either blocks\n") self.doHalt() else : self.msgText("Failed to find current block\n") self.doHalt() else : if (nBlock == None) : self.msgText("Failed to find next block\n") self.doHalt() else : self.msgText("looking for signal between " + self.giveBlockName(cBlock) + " and " + self.giveBlockName(nBlock) + "\n") s = jmri.InstanceManager.layoutBlockManagerInstance().getFacingSignalHead(cBlock, nBlock) if (s != None) : self.msgText("Found signal: " + self.giveSignalName(s) + " displaying: " + self.cvtAppearanceText(s) + "\n") self.speedFromAppearance(s.getAppearance()) self.currentSignal = s self.currentSignalAspect = s.getAppearance() else : self.msgText("Failed finding signal!\n") self.doHalt() return # convert signal appearance to a ranked value def rankSignalAspect(self, sigAspect) : ret = 0 if (sigAspect & RED != 0) : ret = 1 elif (sigAspect & FLASHRED != 0) : ret = 2 elif (sigAspect & YELLOW != 0) : ret = 3 elif (sigAspect & FLASHYELLOW != 0) : ret = 4 elif (sigAspect & GREEN != 0) : ret = 5 elif (sigAspect & FLASHGREEN != 0) : ret = 6 return ret # convert signal appearance to text def textSignalAspect(self, sigAspect) : ret = "???" if (sigAspect & RED != 0) : ret = "RED" elif (sigAspect & FLASHRED != 0) : ret = "FLASHRED" elif (sigAspect & YELLOW != 0) : ret = "YELLOW" elif (sigAspect & FLASHYELLOW != 0) : ret = "FLASHYELLOW" elif (sigAspect & GREEN != 0) : ret = "GREEN" elif (sigAspect & FLASHGREEN != 0) : ret = "FLASHGREEN" return ret # compare two signal appearances def compareSignalAspects(self, oldSigState, newSigState) : ret = "0" if (newSigState == None or oldSigState == None) : # this is wrong self.msgText("compare signals got a None for a signal\n") self.doHalt() return newSigValue = self.rankSignalAspect(newSigState) oldSigValue = self.rankSignalAspect(oldSigState) self.msgText("compareSignalAspects: " + self.textSignalAspect(oldSigState) + " went " + self.textSignalAspect(newSigState) + "\n") if (newSigValue < oldSigValue) : ret = -1 elif (newSigValue > oldSigValue) : ret = 1 return ret # set speed from signal appearance def speedFromAppearance(self, sigState) : rep = "" if (sigState == RED or self.currentBlock == self.stopBlock) : rep = rep + "doRed " self.doSpeedRed() elif (sigState == FLASHRED) : rep = rep + "doRedFlash " self.doSpeedRedFlash() elif (sigState == YELLOW or self.nextBlock == self.stopBlock) : rep = rep + "doYellow " self.doSpeedYellow() elif (sigState == FLASHYELLOW) : rep = rep + "doYellowFlash " self.doSpeedYellowFlash() elif (sigState == GREEN) : rep = rep + "doGreen " self.doSpeedGreen() elif (sigState == FLASHGREEN) : rep = rep + "doGreenFlash " self.doSpeedGreenFlash() else : rep = rep + "unknown " self.msgText("speedFromAppearance, unknown value! " + sigState.toString() + "\n") self.doHalt() #self.msgText("speedFromAppearance: " + self.giveSignalName(sig) + " displaying: " + self.cvtAppearanceText(sig) + " so we did: " + rep + "\n") return # convert signal appearance to english def cvtAppearanceText(self, sig) : rep = "" if (sig.getHeld()) : rep = rep + "Held " if (sig.getLit()) : rep = rep + "Lit " sigState = sig.getAppearance() if (sigState == RED) : rep = rep + "Red " elif (sigState == FLASHRED) : rep = rep + "Flashing Red " elif (sigState == YELLOW) : rep = rep + "Yellow " elif (sigState == FLASHYELLOW) : rep = rep + "Flashing Yellow " elif (sigState == GREEN) : rep = rep + "Green " elif (sigState == FLASHGREEN) : rep = rep + "Flashing Green " elif (sigState == DARK) : rep = rep + "Dark " else : rep = rep + "Unknown " #self.msgText("cvtAppearanceText: " + self.giveSignalName(sig) + " displaying: " + rep + "\n") return rep # convert signal appearance to icon def cvtAppearanceIcon(self, sig) : rep = self.darkSignalIcon if (sig.getLit()) : sigState = sig.getAppearance() if (sigState == RED) : rep = self.redSignalIcon elif (sigState == FLASHRED) : rep = self.redFlashSignalIcon elif (sigState == YELLOW) : rep = self.yellowSignalIcon elif (sigState == FLASHYELLOW) : rep = self.yellowFlashSignalIcon elif (sigState == GREEN) : rep = self.greenSignalIcon elif (sigState == FLASHGREEN) : rep = self.greenFlashSignalIcon else : rep = self.unknownSignalIcon #self.msgText("cvtAppearanceIcon: " + self.giveSignalName(sig) + " displaying: " + rep + "\n") return rep # compare two lists, reply true or false def compareLists(self, aList, bList) : self.msgText("comparing lists\n") doesMatchA = True doesMatchB = True for a in aList : try : i = bList.index(a) except : doesMatchA = False if (doesMatchA) : self.msgText("comparing lists: all of a in b\n") for b in bList : try : i = aList.index(b) except : doesMatchB = False if (doesMatchB) : self.msgText("comparing lists: all of b in a\n") return doesMatchA and doesMatchB def doSpeedGreenFlash(self): if (self.redDelayTimer != None) : self.redDelayTimer.stop() if (self.currentThrottle != None) : i = int(self.locoSpeedGreenFlash.text) * 0.01 self.currentThrottle.setSpeedSetting(i) self.msgText("doSpeedGreenFlash: " + i.toString() + "\n") self.locoSpeed.text = self.locoSpeedGreenFlash.text return def doSpeedGreen(self): if (self.redDelayTimer != None) : self.redDelayTimer.stop() if (self.currentThrottle != None) : i = int(self.locoSpeedGreen.text) * 0.01 self.currentThrottle.setSpeedSetting(i) self.msgText("doSpeedGreen: " + i.toString() + "\n") self.locoSpeed.text = self.locoSpeedGreen.text return def doSpeedYellowFlash(self): if (self.redDelayTimer != None) : self.redDelayTimer.stop() if (self.currentThrottle != None) : i = int(self.locoSpeedYellowFlash.text) * 0.01 self.currentThrottle.setSpeedSetting(i) self.msgText("doSpeedYellowFlash: " + i.toString() + "\n") self.locoSpeed.text = self.locoSpeedYellowFlash.text return def doSpeedYellow(self): if (self.redDelayTimer != None) : self.redDelayTimer.stop() if (self.currentThrottle != None) : i = int(self.locoSpeedYellow.text) * 0.01 self.currentThrottle.setSpeedSetting(i) self.msgText("doSpeedYellow: " + i.toString() + "\n") self.locoSpeed.text = self.locoSpeedYellow.text return def doSpeedRedFlash(self): if (self.redDelayTimer != None) : self.redDelayTimer.stop() if (self.currentThrottle != None) : i = int(self.locoSpeedRedFlash.text) * 0.01 self.currentThrottle.setSpeedSetting(i) self.msgText("doSpeedRedFlash: " + i.toString() + "\n") self.locoSpeed.text = self.locoSpeedRedFlash.text return def doSpeedRed(self): if (self.currentThrottle != None and self.currentThrottle.getSpeedSetting() != 0 and (self.redDelayTimer == None or self.redDelayTimer.isRunning() == False)) : i = int(self.locoSpeedRed.text) * 0.01 self.currentThrottle.setSpeedSetting(i) self.msgText("doSpeedRed: " + i.toString() + "\n") self.locoSpeed.text = self.locoSpeedRed.text # compute how long to delay stopping dist = self.currentBlock.getLengthIn() rate = float(self.locoRateRed.text) stopDist = float(self.locoDistanceRedStop.text) if (dist != 0 and rate != 0) : # the stop distance is the reserved space plus 10% from far end of block # the less one covers the delay of the handle() routine delay = ((dist - stopDist) / rate * 0.90) - 1 self.msgText("doSpeedRed: dist: " + dist.toString() + " rate: " + rate.toString() + " stopDist: " + stopDist.toString() + " delay: " + delay.toString() + "\n") if (delay > 1) : currentDelay = 0 if (self.redDelayTimer == None) : self.redDelayListener = self.RedStopTimeoutReceiver() self.redDelayListener.setCallBack(self.redDelayHandler) self.redDelayTimer = javax.swing.Timer(int(delay * 0), self.redDelayListener) self.redDelayTimer.setInitialDelay(int(delay * 1000)) self.redDelayTimer.setRepeats(False); self.redDelayTimer.setInitialDelay(int(delay * 1000)) self.redDelayTimer.start() else : self.msgText("stop delay less that 1 second") self.doStop() else : self.doStop() return # handle the timeout for stopping on red def redDelayHandler(self, event) : self.msgText("redDelayHandler, stopping now!\n") self.redDelayTimer.stop() self.doStop() return # stopping for normal issues, allows for restarting automaticly def doStop(self): if (self.redDelayTimer != None) : self.redDelayTimer.stop() if (self.currentThrottle != None) : self.currentThrottle.setSpeedSetting(0) self.msgText("doStop\n") self.locoSpeed.text = "0" if (self.currentBlock != None) : self.blockStart.text = self.giveBlockName(self.currentBlock) if (self.stopBlock != None and self.currentBlock == self.stopBlock) : self.isRunning = False self.stopButton.setEnabled(False) self.haltButton.setEnabled(False) self.startButton.setEnabled(True) return # doHalt is for stopping due to error conditions, won't restart def doHalt(self) : self.isRunning = False if (self.currentThrottle != None) : self.currentThrottle.setSpeedSetting(-1) self.msgText("doHalt, something was in error!!\n") self.locoSpeed.text = "0" if (self.currentBlock != None) : self.blockStart.text = self.giveBlockName(self.currentBlock) self.msgText("*** Run halted ***\n") self.stopButton.setEnabled(False) self.haltButton.setEnabled(False) self.startButton.setEnabled(True) return # process the stop block def whenStopBlockChanged(self, event) : self.blockStop.text = self.blockStop.text.strip() if (self.blockStop.text == "") : self.stopBlock = None else : self.stopBlock = blocks.getBlock(self.blockStop.text) return # enable the button when OK def whenLocoChanged(self, event) : # keep track of whether both fields have been changed if (self.isRunning) : self.doStop() self.msgText("whenLocoChanged, was running, now stopped\n") isOk = True startBlock = None self.locoAddress.text = self.locoAddress.text.strip() if (self.locoAddress.text == "") : isOk = False else : self.scriptFrame.setTitle("Run Loco " + self.locoAddress.text) if (self.locoAddress.text != self.oldLocoAddress) : self.oldLocoAddress = self.locoAddress.text if (self.loadFromRoster.isSelected() == True) : # take the loco id and try looking up values in roster if (self.rosterInstance == None) : self.rosterInstance = jmri.jmrit.roster.Roster.instance() self.msgText("got roster instance\n") rosterEntries = self.rosterInstance.matchingList(None, None, self.locoAddress.text, None, None, None, None) self.msgText("found " + rosterEntries.size().toString() + " entries matching |" + id.toString() + "|\n") for ent in rosterEntries : self.msgText("posible entries: " + ent.fileName + "\n") if (rosterEntries.size() == 1) : ent = rosterEntries.get(0) self.msgText("Reading roster: " + ent.fileName + "\n") v = ent.getAttribute('RT_locoSpeedGreenFlash') if (v != None and v != "") : self.locoSpeedGreenFlash.text = v v = ent.getAttribute('RT_locoRateGreenFlash') if (v != None and v != "") : self.locoRateGreenFlash.text = v v = ent.getAttribute('RT_locoSpeedGreen') if (v != None and v != "") : self.locoSpeedGreen.text = v v = ent.getAttribute('RT_locoRateGreen') if (v != None and v != "") : self.locoRateGreen.text = v v = ent.getAttribute('RT_locoSpeedYellowFlash') if (v != None and v != "") : self.locoSpeedYellowFlash.text = v v = ent.getAttribute('RT_locoRateYellowFlash') if (v != None and v != "") : self.locoRateYellowFlash.text = v v = ent.getAttribute('RT_locoSpeedYellow') if (v != None and v != "") : self.locoSpeedYellow.text = v v = ent.getAttribute('RT_locoRateYellow') if (v != None and v != "") : self.locoRateYellow.text = v v = ent.getAttribute('RT_locoSpeedRedFlash') if (v != None and v != "") : self.locoSpeedRedFlash.text = v v = ent.getAttribute('RT_locoRateRedFlash') if (v != None and v != "") : self.locoRateRedFlash.text = v v = ent.getAttribute('RT_locoSpeedRed') if (v != None and v != "") : self.locoSpeedRed.text = v v = ent.getAttribute('RT_locoRateRed') if (v != None and v != "") : self.locoRateRed.text = v v = ent.getAttribute('RT_locoDistanceRedStop') if (v != None and v != "") : self.locoDistanceRedStop.text = v self.locoLong.setSelected(ent.isLongAddress()) self.msgText("Read completed: " + ent.fileName + "\n") self.oldLocoAddress = self.locoAddress.text self.locoSpeedRed.text = self.locoSpeedRed.text.strip() self.locoRateRed.text = self.locoRateRed.text.strip() self.locoSpeedRedFlash.text = self.locoSpeedRedFlash.text.strip() self.locoRateRedFlash.text = self.locoRateRedFlash.text.strip() self.locoSpeedYellow.text = self.locoSpeedYellow.text.strip() self.locoRateYellow.text = self.locoRateYellow.text.strip() self.locoSpeedYellowFlash.text = self.locoSpeedYellowFlash.text.strip() self.locoRateYellowFlash.text = self.locoRateYellowFlash.text.strip() self.locoSpeedGreen.text = self.locoSpeedGreen.text.strip() self.locoRateGreen.text = self.locoRateGreen.text.strip() self.locoSpeedGreenFlash.text = self.locoSpeedGreenFlash.text.strip() self.locoRateGreenFlash.text = self.locoRateGreenFlash.text.strip() if (self.locoSpeedRed.text == "") : isOk = False if (self.locoSpeedRedFlash.text == "") : isOk = False if (self.locoSpeedYellow.text == "") : isOk = False if (self.locoSpeedYellowFlash.text == "") : isOk = False if (self.locoSpeedGreen.text == "") : isOk = False if (self.locoSpeedGreenFlash.text == "") : isOk = False self.blockStart.text = self.blockStart.text.strip() if (self.blockStart.text == "") : isOk = False else : startBlock = blocks.getBlock(self.blockStart.text) if (self.testIfBlockNameValid(self.blockStart.text) == False) : self.msgText("Invalid block name: " + self.blockStart.text + " please try again\n") isOk = False else: if (startBlock.getState() != ACTIVE) : self.msgText("Block: " + self.blockStart.text + " is not occupied!\n") isOk = False if (isOk) : self.startButton.setEnabled(True) self.haltButton.setEnabled(True) self.testAddBlockListener(blocks.getBlock(self.blockStart.text)) self.msgText("Enabled Start\n") return # handle the horn button on def whenLocoHornOn(self, event) : self.doLocoHorn(event, True) return # handle the horn button off def whenLocoHornOff(self, event) : self.doLocoHorn(event, False) return def doLocoHorn(self, event, state) : if (self.currentThrottle != None) : wasState = self.currentThrottle.getF2() self.currentThrottle.setF2(state) self.msgText("changed horn to: " + state.toString() + " was " + wasState.toString() + "\n") return def doShortHorn(self) : self.doTimedHorn(1*1000) return def doLongHorn(self) : self.doTimedHorn(2*1000) return def doTimedHorn(self, delay) : if (self.currentThrottle != None) : self.currentThrottle.setF2(True) if (self.hornDelayTimer == None) : self.hornDelayListener = self.HornTimeoutReceiver() self.hornDelayListener.setCallBack(self.hornDelayHandler) self.hornDelayTimer = javax.swing.Timer(int(delay), self.hornDelayListener) self.hornDelayTimer.setInitialDelay(int(delay)) self.hornDelayTimer.setRepeats(False); self.hornDelayTimer.setInitialDelay(int(delay)) self.hornDelayTimer.start() self.msgText("Started Timed Horn\n") return def hornDelayHandler(self, event) : if (self.hornDelayTimer != None) : self.hornDelayTimer.stop() if (self.currentThrottle != None) : self.currentThrottle.setF2(False) self.msgText("Stopped Timed Horn\n") return # handle the Headlight button def whenLocoHeadlight(self, event) : if (self.currentThrottle != None) : wasState = self.currentThrottle.getF0() state = self.locoHeadlight.isSelected() self.currentThrottle.setF0(state) self.msgText("changed light to: " + state.toString() + " was " + wasState.toString() + "\n") return # handle the Bell button def whenLocoBell(self, event) : if (self.currentThrottle != None) : wasState = self.currentThrottle.getF1() state = self.locoBell.isSelected() self.currentThrottle.setF1(state) self.msgText("changed bell to: " + state.toString() + " was " + wasState.toString() + "\n") return # test for block name def testIfBlockNameValid(self, userName) : foundStart = False b = blocks.getByUserName(userName) if (b != None and self.giveBlockName(b) == userName) : foundStart = True return foundStart # define what button does when clicked and attach that routine to the button def whenStartButtonClicked(self, event) : self.msgText("Run started\n") # add text if (self.testIfBlockNameValid(self.blockStart.text) == False) : self.msgText("Invalid block name: " + self.blockStart.text + " please try again\n") else : c = blocks.getBlock(self.blockStart.text) if (c == None) : self.msgText("Invalid block name: " + self.blockStart.text + " please try again\n") else : c.setValue(self.locoAddress.text) self.currentBlock = c self.currentBlocks = self.findCurrentBlocks() self.priorBlocks = self.currentBlocks if (self.blockDirection.isSelected() == True) : self.currentDirection = jmri.Path.EAST self.currentBlock.setDirection(jmri.Path.EAST) else : self.currentDirection = jmri.Path.WEST self.currentBlock.setDirection(jmri.Path.WEST) self.currentSignal = None self.currentSignalAspect = None # set flags so things get done from handle() routine self.askChangeThrottle = True self.askFinishStartButton = True self.msgText("whenStartButtonClicked, done\n") # add text return # split out so it can happen from the handle() routine def doFinishStartButton(self) : self.msgText("Change button states\n") # add text self.stopButton.setEnabled(True) self.haltButton.setEnabled(True) self.startButton.setEnabled(False) self.isRunning = True self.didWeMoveCounter = self.didWeMoveCounter + 1 if (self.isRunning) : self.msgText("Starting current:" + self.giveBlockName(self.currentBlock) + "\n") return def whenStopButtonClicked(self, event): self.msgText("Slow loco to stop\n") # add text self.doStop() self.msgText("*** Run stopped ***\n") self.stopButton.setEnabled(False) self.haltButton.setEnabled(False) self.startButton.setEnabled(True) self.isRunning = False self.whenLocoChanged(event) return def whenHaltButtonClicked(self, event): self.msgText("Button Halt loco NOW!\n") # add text self.doHalt() self.msgText("*** Run halted ***\n") self.stopButton.setEnabled(False) self.haltButton.setEnabled(False) self.startButton.setEnabled(True) self.isRunning = False self.whenLocoChanged(event) return def whenShrinkButtonClicked(self, event): if (self.shrinkGrow == True) : self.msgText("Shrink Display!\n") # add text self.speedPane.setVisible(False) self.shrinkGrow = False self.fullScrollRows = self.scrollArea.getRows() self.scrollArea.setRows(self.fullScrollRows / 2) self.scriptFrame.pack() else : self.msgText("Grow Display!\n") self.speedPane.setVisible(True) self.shrinkGrow = True self.scrollArea.setRows(self.fullScrollRows) self.scriptFrame.pack() return def whenSaveToRosterButtonClicked(self, event): if (self.locoAddress.text != "") : if (self.rosterInstance == None) : self.rosterInstance = jmri.jmrit.roster.Roster.instance() self.msgText("got roster instance\n") id = int(self.locoAddress.text) rosterEntries = self.rosterInstance.matchingList(None, None, id.toString(), None, None, None, None) self.msgText("found " + rosterEntries.size().toString() + " entries matching |" + id.toString() + "|\n") for ent in rosterEntries : self.msgText("posible entries: " + ent.fileName + "\n") if (rosterEntries.size() == 1) : ent = rosterEntries.get(0) self.msgText("Saving to roster: " + ent.fileName + "\n") ent.putAttribute('RT_locoSpeedGreenFlash', self.locoSpeedGreenFlash.text) ent.putAttribute('RT_locoRateGreenFlash', self.locoRateGreenFlash.text) ent.putAttribute('RT_locoSpeedGreen', self.locoSpeedGreen.text) ent.putAttribute('RT_locoRateGreen', self.locoRateGreen.text) ent.putAttribute('RT_locoSpeedYellowFlash', self.locoSpeedYellowFlash.text) ent.putAttribute('RT_locoRateYellowFlash', self.locoRateYellowFlash.text) ent.putAttribute('RT_locoSpeedYellow', self.locoSpeedYellow.text) ent.putAttribute('RT_locoRateYellow', self.locoRateYellow.text) ent.putAttribute('RT_locoSpeedRedFlash', self.locoSpeedRedFlash.text) ent.putAttribute('RT_locoRateRedFlash', self.locoRateRedFlash.text) ent.putAttribute('RT_locoSpeedRed', self.locoSpeedRed.text) ent.putAttribute('RT_locoRateRed', self.locoRateRed.text) ent.putAttribute('RT_locoDistanceRedStop', self.locoDistanceRedStop.text) ent.updateFile() self.rosterInstance.writeRosterFile() self.msgText("Save completed: " + ent.fileName + "\n") return def findCurrentBlocks(self) : # search the block list for the matching loco blockList = [] for x in blocks.getSystemNameList().toArray() : b = blocks.getBySystemName(x) if (b.getValue() == self.locoAddress.text and b.getState() == ACTIVE) : blockList.append(b) return blockList def findNextBlock(self, cB) : # look down list of getToBlockDirection for match # use 'suggestion' flag if current block doesn't have direction nB = None dirFlag = cB.getDirection() if (dirFlag == jmri.Path.NONE) : if (self.blockDirection.isSelected() == True) : dirFlag = dirFlag or jmri.Path.EAST else : dirFlag = dirFlag or jmri.Path.WEST pathList = cB.getPaths() for p in pathList : blockTest = p.getBlock() if (p.checkPathSet()) : dirTest = p.getToBlockDirection() #self.msgText("findNextBlock path traversable: " + self.giveBlockName(cB) + " to " + self.giveBlockName(blockTest) + " dirTest: " + jmri.Path.decodeDirection(dirTest) + ":" + dirTest.toString() + " dirFlag: " + jmri.Path.decodeDirection(dirFlag) + ":" + dirFlag.toString() + " result: " + (dirTest & dirFlag).toString() + "\n") if (dirTest & dirFlag != 0) : nB = blockTest #self.msgText("findNextBlock Found " + self.giveBlockName(blockTest) + "\n") #break #else : #self.msgText("findNextBlock path not traversable: " + self.giveBlockName(cB) + " to " + self.giveBlockName(blockTest) + "\n") return nB # ActionListener - used for the stop timeout class RedStopTimeoutReceiver(java.awt.event.ActionListener): cb = None def actionPerformed(self, event) : if (self.cb != None) : self.cb(event) return def setCallBack(self, cbf) : self.cb = cbf return # ActionListener - used for the horn timeout class HornTimeoutReceiver(java.awt.event.ActionListener): cb = None def actionPerformed(self, event) : if (self.cb != None) : self.cb(event) return def setCallBack(self, cbf) : self.cb = cbf return # WindowListener is a interface class and therefore all of it's # methods should be implemented even if not used to avoid AttributeErrors class WinListener(java.awt.event.WindowListener): f = None cleanUp = None def setCallBack(self, fr, c): self.f = fr self.cleanUp = c return def windowClosing(self, event): if (self.cleanUp != None) : self.cleanUp(event) self.f.dispose() # close the pane (window) return def windowActivated(self,event): return def windowDeactivated(self,event): return def windowOpened(self,event): return def windowClosed(self,event): return def windowIconified(self, event): return def windowDeiconified(self, event): return #To detect block status, first define the listener. class BlockListener(java.beans.PropertyChangeListener): cb = None def setCallBack(self, ptr) : self.cb = ptr return def propertyChange(self, event): if (self.cb != None) : self.cb(event) return #To detect block status, first define the listener. class SignalListener(java.beans.PropertyChangeListener): cb = None def setCallBack(self, ptr) : self.cb = ptr return def propertyChange(self, event): if (self.cb != None) : self.cb(event) return # handle adding to message window def msgText(self, txt) : self.scrollArea.append(txt) if (self.autoScroll.isSelected() == True) : self.scrollArea.setCaretPosition(self.scrollArea.getDocument().getLength()) return # setup the user interface def setup(self) : # get other setup things self.greenSignalIcon = jmri.jmrit.catalog.NamedIcon("resources/icons/smallschematics/searchlights/right-green-short.gif", "GreenCabSignal") self.greenFlashSignalIcon = jmri.jmrit.catalog.NamedIcon("resources/icons/smallschematics/searchlights/right-flashgreen-short.gif", "GreenFlashCabSignal") self.yellowSignalIcon = jmri.jmrit.catalog.NamedIcon("resources/icons/smallschematics/searchlights/right-yellow-short.gif", "YellowCabSignal") self.yellowFlashSignalIcon = jmri.jmrit.catalog.NamedIcon("resources/icons/smallschematics/searchlights/right-flashyellow-short.gif", "YellowFlashCabSignal") self.redSignalIcon = jmri.jmrit.catalog.NamedIcon("resources/icons/smallschematics/searchlights/right-red-short.gif", "RedCabSignal") self.redFlashSignalIcon = jmri.jmrit.catalog.NamedIcon("resources/icons/smallschematics/searchlights/right-flashred-short.gif", "RedFlashCabSignal") self.darkSignalIcon = jmri.jmrit.catalog.NamedIcon("resources/icons/smallschematics/searchlights/right-dark-short.gif", "DarkCabSignal") self.unknownSignalIcon = jmri.jmrit.catalog.NamedIcon("resources/icons/misc/Question-black.gif", "UnknownCabSignal") self.throttleManager = jmri.InstanceManager.throttleManagerInstance() # start to initialise the GUI sizeRateField = 4 sizeSpeedField = 3 # create buttons and define action self.startButton = javax.swing.JButton("Start") self.startButton.setEnabled(False) # button starts as grayed out (disabled) self.startButton.actionPerformed = self.whenStartButtonClicked self.stopButton = javax.swing.JButton("Stop") self.stopButton.setEnabled(False) # button starts as grayed out (disabled) self.stopButton.setToolTipText("Stops the run - there is a delay as the loco slows") self.stopButton.actionPerformed = self.whenStopButtonClicked self.haltButton = javax.swing.JButton("Halt") self.haltButton.setEnabled(False) # button starts as grayed out (disabled) self.haltButton.setToolTipText("Emergency halt the run - should be an abrupt stop") self.haltButton.actionPerformed = self.whenHaltButtonClicked self.shrinkButton = javax.swing.JButton("Shrink") self.shrinkButton.setEnabled(True) self.shrinkButton.setToolTipText("Shrink/Grow the window") self.shrinkButton.actionPerformed = self.whenShrinkButtonClicked self.testButton = javax.swing.JButton("Test") self.testButton.setEnabled(True) # button starts as grayed out (disabled) self.testButton.setToolTipText("run the didWeMove test") self.testButton.actionPerformed = self.callBackForDidWeMove self.saveToRosterButton = javax.swing.JButton("Save Settings") self.saveToRosterButton.setEnabled(True) self.saveToRosterButton.setToolTipText("Saves setting in roster, if found.") self.saveToRosterButton.actionPerformed = self.whenSaveToRosterButtonClicked # address of the loco self.locoAddress = javax.swing.JTextField(5) # sized to hold 5 characters, initially empty self.locoAddress.actionPerformed = self.whenLocoChanged # if user hit return or enter self.locoAddress.focusLost = self.whenLocoChanged # if user tabs away self.locoAddress.requestFocusInWindow() # long/short address flag self.locoLong = javax.swing.JCheckBox() self.locoLong.setToolTipText("Check to use long address") self.locoLong.setSelected(True) if (self.throttleManager.addressTypeUnique() == True) : self.locoLong.setEnabled(False) self.locoLong.actionPerformed = self.whenLocoChanged self.locoLong.focusLost = self.whenLocoChanged # loco direction flag self.locoForward = javax.swing.JCheckBox() self.locoForward.setToolTipText("Sets forward loco direction") self.locoForward.setSelected(True) self.locoForward.actionPerformed = self.whenLocoChanged self.locoForward.focusLost = self.whenLocoChanged # loco headlight flag self.locoHeadlight = javax.swing.JCheckBox() self.locoHeadlight.setToolTipText("Controls loco hightlight") self.locoHeadlight.actionPerformed = self.whenLocoHeadlight self.locoHeadlight.focusLost = self.whenLocoHeadlight # loco bell flag self.locoBell = javax.swing.JCheckBox() self.locoBell.setToolTipText("Controls loco bell") self.locoBell.actionPerformed = self.whenLocoBell self.locoBell.focusLost = self.whenLocoBell # loco horn/whistle flag self.locoHorn = javax.swing.JButton("Horn") self.locoHorn.setToolTipText("Controls loco horn") self.locoHorn.mousePressed = self.whenLocoHornOn self.locoHorn.mouseReleased = self.whenLocoHornOff # create the speed fields for a Green Flash Signal self.locoSpeedGreenFlash = javax.swing.JTextField(sizeSpeedField) # sized to hold 5 characters self.locoSpeedGreenFlash.setToolTipText("Green Flash Speed is a number from 1 to 100%") self.locoSpeedGreenFlash.actionPerformed = self.whenLocoChanged self.locoSpeedGreenFlash.focusLost = self.whenLocoChanged self.locoSpeedGreenFlash.text = "70" # create the physical speed field for a Green Flash Signal self.locoRateGreenFlash = javax.swing.JTextField(sizeRateField) # sized to hold 5 characters self.locoRateGreenFlash.setToolTipText("Throttle as Distance/Second, approaching green flash signal") self.locoRateGreenFlash.actionPerformed = self.whenLocoChanged self.locoRateGreenFlash.focusLost = self.whenLocoChanged self.locoRateGreenFlash.text = "0" # create the speed fields for a Green Signal self.locoSpeedGreen = javax.swing.JTextField(sizeSpeedField) # sized to hold 5 characters self.locoSpeedGreen.setToolTipText("Green Speed is a number from 1 to 100%") self.locoSpeedGreen.actionPerformed = self.whenLocoChanged self.locoSpeedGreen.focusLost = self.whenLocoChanged self.locoSpeedGreen.text = "45" # create the physical speed field for a Green Signal self.locoRateGreen = javax.swing.JTextField(sizeRateField) # sized to hold 5 characters self.locoRateGreen.setToolTipText("Throttle as Distance/Second, approaching Green signal") self.locoRateGreen.actionPerformed = self.whenLocoChanged self.locoRateGreen.focusLost = self.whenLocoChanged self.locoRateGreen.text = "9" # create the speed fields for a Yellow Flash Signal self.locoSpeedYellowFlash = javax.swing.JTextField(sizeSpeedField) # sized to hold 5 characters self.locoSpeedYellowFlash.setToolTipText("Yellow Flash Speed is a number from 1 to 100%") self.locoSpeedYellowFlash.actionPerformed = self.whenLocoChanged self.locoSpeedYellowFlash.focusLost = self.whenLocoChanged self.locoSpeedYellowFlash.text = "45" # create the physical speed field for a Yellow Flash Signal self.locoRateYellowFlash = javax.swing.JTextField(sizeRateField) # sized to hold 5 characters self.locoRateYellowFlash.setToolTipText("Throttle as Distance/Second, approaching yello flash signal") self.locoRateYellowFlash.actionPerformed = self.whenLocoChanged self.locoRateYellowFlash.focusLost = self.whenLocoChanged self.locoRateYellowFlash.text = "0" # create the speed fields for a Yellow Signal self.locoSpeedYellow = javax.swing.JTextField(sizeSpeedField) # sized to hold 5 characters self.locoSpeedYellow.setToolTipText("Yellow Speed is a number from 1 to 100%") self.locoSpeedYellow.actionPerformed = self.whenLocoChanged self.locoSpeedYellow.focusLost = self.whenLocoChanged self.locoSpeedYellow.text = "30" # create the physical speed field for a Yellow Signal self.locoRateYellow = javax.swing.JTextField(sizeRateField) # sized to hold 5 characters self.locoRateYellow.setToolTipText("Throttle as Distance/Second, approaching yellow signal") self.locoRateYellow.actionPerformed = self.whenLocoChanged self.locoRateYellow.focusLost = self.whenLocoChanged self.locoRateYellow.text = "6" # create the speed fields for a Red Flash Signal self.locoSpeedRedFlash = javax.swing.JTextField(sizeSpeedField) # sized to hold 5 characters self.locoSpeedRedFlash.setToolTipText("Red Flash Speed is a number from 1 to 100%") self.locoSpeedRedFlash.actionPerformed = self.whenLocoChanged self.locoSpeedRedFlash.focusLost = self.whenLocoChanged self.locoSpeedRedFlash.text = "20" # create the physical speed field for a Red Flash Signal self.locoRateRedFlash = javax.swing.JTextField(sizeRateField) # sized to hold 5 characters self.locoRateRedFlash.setToolTipText("Throttle as Distance/Second, approaching red flash signal") self.locoRateRedFlash.actionPerformed = self.whenLocoChanged self.locoRateRedFlash.focusLost = self.whenLocoChanged self.locoRateRedFlash.text = "0" # create the speed fields for a Red Signal self.locoSpeedRed = javax.swing.JTextField(sizeSpeedField) # sized to hold 5 characters self.locoSpeedRed.setToolTipText("Red Speed is a number from 1 to 100%, creep to Red Signal") self.locoSpeedRed.actionPerformed = self.whenLocoChanged self.locoSpeedRed.focusLost = self.whenLocoChanged self.locoSpeedRed.text = "15" # create the physical speed field for a Red Signal self.locoRateRed = javax.swing.JTextField(sizeRateField) # sized to hold 5 characters self.locoRateRed.setToolTipText("Throttle as Distance/Second, approaching red signal") self.locoRateRed.actionPerformed = self.whenLocoChanged self.locoRateRed.focusLost = self.whenLocoChanged self.locoRateRed.text = "3" # create the distance field for a Red Signal self.locoDistanceRedStop = javax.swing.JTextField(5) # sized to hold 5 characters self.locoDistanceRedStop.setToolTipText("Distance to stop before Red signal, inches") self.locoDistanceRedStop.actionPerformed = self.whenLocoChanged self.locoDistanceRedStop.focusLost = self.whenLocoChanged self.locoDistanceRedStop.text = "10" # create current speed display self.locoSpeed = javax.swing.JLabel() self.locoSpeed.text = "0" # create the starting block field self.blockStart = javax.swing.JTextField(10) self.blockStart.setToolTipText("Starting Block Name") self.blockStart.actionPerformed = self.whenLocoChanged self.blockStart.focusLost = self.whenLocoChanged # create the starting block direction self.blockDirection = javax.swing.JCheckBox() self.blockDirection.setToolTipText("Starting Block Direction") self.blockDirection.actionPerformed = self.whenLocoChanged self.blockDirection.focusLost = self.whenLocoChanged self.blockDirection.setSelected(True) # create flag for looking any extra block ahead self.blockAhead2 = javax.swing.JCheckBox() self.blockAhead2.setSelected(False) self.blockAhead2.setToolTipText("for 4 block mode") # create the stopping block field self.blockStop = javax.swing.JTextField(10) self.blockStop.setToolTipText("Stopping Block Name") self.blockStop.actionPerformed = self.whenStopBlockChanged self.blockStop.focusLost = self.whenStopBlockChanged # create the current block field self.blockNow = javax.swing.JLabel() self.blockNowLength = javax.swing.JLabel() # create the current/next signal field self.signalNext = javax.swing.JLabel() self.signalNextText = javax.swing.JLabel() # create the next block field self.blockNext = javax.swing.JLabel() self.blockNextLength = javax.swing.JLabel() # create the next/beyond signal field self.signalBeyond = javax.swing.JLabel() self.signalBeyondText = javax.swing.JLabel() # create the beyond block field self.blockBeyond = javax.swing.JLabel() self.blockBeyondLength = javax.swing.JLabel() # load from roster flag self.loadFromRoster = javax.swing.JCheckBox() self.loadFromRoster.setToolTipText("Load settings from roster entry if found.") self.loadFromRoster.setSelected(True) # auto-scroll message window flag self.autoScroll = javax.swing.JCheckBox() self.autoScroll.setToolTipText("Sets message window to auto-scroll") self.autoScroll.setSelected(True) # create a text area self.scrollArea = javax.swing.JTextArea(15, 70) # define a text area with it's size self.msgText("Enter the loco number, direction and addr mode\nSet min and max speed\nEnter block name loco is in\n") srcollField = javax.swing.JScrollPane(self.scrollArea) # put text area in scroll field # create a frame to hold the buttons and fields # also create a window listener. This is used mainly to remove the property change listener # when the window is closed by clicking on the window close button w = self.WinListener() self.scriptFrame = javax.swing.JFrame("Run Loco") # argument is the frames title self.scriptFrame.contentPane.setLayout(javax.swing.BoxLayout(self.scriptFrame.contentPane, javax.swing.BoxLayout.Y_AXIS)) self.scriptFrame.addWindowListener(w) w.setCallBack(self.scriptFrame, self.releaseAllListeners) # put the text field on a line preceded by a label temppanel1 = javax.swing.JPanel() temppanel1.add(javax.swing.JLabel("Loco Address:")) temppanel1.add(self.locoAddress) temppanel1.add(javax.swing.JLabel(" LongAddr?")) temppanel1.add(self.locoLong) temppanel1.add(javax.swing.JLabel(" Forward?")) temppanel1.add(self.locoForward) temppanel1a = javax.swing.JPanel() temppanel1a.add(javax.swing.JLabel("Headlight:")) temppanel1a.add(self.locoHeadlight) temppanel1a.add(javax.swing.JLabel("Bell:")) temppanel1a.add(self.locoBell) temppanel1a.add(javax.swing.JLabel("Horn:")) temppanel1a.add(self.locoHorn) # build speed table gLayout = java.awt.GridBagLayout() gConstraints = java.awt.GridBagConstraints() self.speedPane = javax.swing.JPanel() pane2Border = javax.swing.BorderFactory.createEtchedBorder() pane2Titled = javax.swing.BorderFactory.createTitledBorder(pane2Border, "Speed Settings") self.speedPane.setBorder(pane2Titled) self.speedPane.setLayout(gLayout) gConstraints.gridx = 0 gConstraints.gridy = 0 gConstraints.gridwidth = 1 gConstraints.gridheight = 1 gConstraints.ipadx = 12 gConstraints.ipady = 3 gConstraints.insets = java.awt.Insets(3, 3, 3, 3) self.speedPane.add(javax.swing.JLabel("Indication"), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("Throttle"), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(" "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("Inch/Sec"), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(" "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("Indication"), gConstraints) gConstraints.gridx = gConstraints.gridx+ 1 self.speedPane.add(javax.swing.JLabel("Throttle"), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(" "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("Inch/Sec"), gConstraints) gConstraints.gridx = 0 gConstraints.gridy = gConstraints.gridy + 1 self.speedPane.add(javax.swing.JLabel(self.greenFlashSignalIcon), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoSpeedGreenFlash, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("%"), gConstraints) gConstraints.gridx = gConstraints.gridx+ 1 self.speedPane.add(self.locoRateGreenFlash, gConstraints) gConstraints.gridx = gConstraints.gridx+ 1 self.speedPane.add(javax.swing.JLabel(" "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(self.greenSignalIcon), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoSpeedGreen, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("%"), gConstraints) gConstraints.gridx = gConstraints.gridx+ 1 self.speedPane.add(self.locoRateGreen, gConstraints) gConstraints.gridx = 0 gConstraints.gridy = gConstraints.gridy + 1 self.speedPane.add(javax.swing.JLabel(self.yellowFlashSignalIcon), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoSpeedYellowFlash, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("%"), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoRateYellowFlash, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(" "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(self.yellowSignalIcon), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoSpeedYellow, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("%"), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoRateYellow, gConstraints) gConstraints.gridx = 0 gConstraints.gridy = gConstraints.gridy + 1 self.speedPane.add(javax.swing.JLabel(self.redFlashSignalIcon), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoSpeedRedFlash, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("%"), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoRateRedFlash, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(" "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(self.redSignalIcon), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoSpeedRed, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel("%"), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoRateRed, gConstraints) gConstraints.gridx = 0 gConstraints.gridy = gConstraints.gridy + 1 self.speedPane.add(javax.swing.JLabel("Load From Roster: "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.loadFromRoster, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.saveToRosterButton, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(" Stopping Distance: "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(self.locoDistanceRedStop, gConstraints) gConstraints.gridx = gConstraints.gridx + 1 self.speedPane.add(javax.swing.JLabel(" inches "), gConstraints) gConstraints.gridx = gConstraints.gridx + 1 # build block info temppanel3 = javax.swing.JPanel() temppanel3.add(javax.swing.JLabel("Block Starting: ")) temppanel3.add(self.blockStart) temppanel3.add(javax.swing.JLabel(" Eastbound: ")) temppanel3.add(self.blockDirection) temppanel3.add(javax.swing.JLabel(" Check block ahead: ")) temppanel3.add(self.blockAhead2) temppanel3.add(javax.swing.JLabel(" AutoScroll Messages: ")) temppanel3.add(self.autoScroll) temppanel3.add(javax.swing.JLabel(" Stopping Block: ")) temppanel3.add(self.blockStop) temppanel4 = javax.swing.JPanel() temppanel4.add(javax.swing.JLabel(" Current: "), gConstraints) temppanel4.add(self.locoSpeed, gConstraints) temppanel4.add(javax.swing.JLabel("% "), gConstraints) temppanel4.add(javax.swing.JLabel("Block Status")) temppanel4.add(javax.swing.JLabel(" Now:")) temppanel4.add(self.blockNow) temppanel4.add(javax.swing.JLabel(" ")) temppanel4.add(self.signalNext) temppanel4.add(javax.swing.JLabel(" ")) temppanel4.add(self.blockNowLength) temppanel4.add(javax.swing.JLabel("in Next:")) temppanel4.add(self.blockNext) temppanel4.add(javax.swing.JLabel(" ")) temppanel4.add(self.signalBeyond) temppanel4.add(javax.swing.JLabel(" ")) temppanel4.add(self.blockNextLength) temppanel4.add(javax.swing.JLabel("in Beyond:")) temppanel4.add(self.blockBeyond) temppanel4.add(javax.swing.JLabel(" Length: ")) temppanel4.add(self.blockBeyondLength) temppanel4.add(javax.swing.JLabel("in ")) self.greenFlashSignalIcon.setRotation(1, temppanel4) self.greenSignalIcon.setRotation(1, temppanel4) self.yellowFlashSignalIcon.setRotation(1, temppanel4) self.yellowSignalIcon.setRotation(1, temppanel4) self.redFlashSignalIcon.setRotation(1, temppanel4) self.redSignalIcon.setRotation(1, temppanel4) self.darkSignalIcon.setRotation(1, temppanel4) butPanel = javax.swing.JPanel() butPanel.add(self.startButton) butPanel.add(self.stopButton) butPanel.add(self.testButton) butPanel.add(self.haltButton) butPanel.add(self.shrinkButton) # Put contents in frame and display self.scriptFrame.contentPane.add(temppanel1) self.scriptFrame.contentPane.add(temppanel1a) self.scriptFrame.contentPane.add(self.speedPane) self.scriptFrame.contentPane.add(temppanel3) self.scriptFrame.contentPane.add(temppanel4) self.scriptFrame.contentPane.add(srcollField) self.scriptFrame.contentPane.add(butPanel) self.scriptFrame.pack() self.scriptFrame.show() return def setLoco(self, locoId) : self.methodLocoAddress = locoId return def setLocoEast(self) : self.methodBlockDirection = True return def setLocoWest(self) : self.methodBlockDirection = False return def setLocoForward(self) : self.methodLocoForward = True return def setLocoReverse(self) : self.methodLocoForward = False return def locoHeadlightOn(self) : self.methodLocoHeadlight = True return def locoHeadlightOff(self) : self.methodLocoHeadlight = False return def soundShortHorn(self) : self.methodShortHorn = True return def soundLongHorn(self) : self.methodLongHorn = True return def pushShrink(self) : self.methodPushShrink = True return def setStartBlock(self, blockId) : self.methodBlockStart = blockId return def pushStart(self) : self.methodPushStart = True return def pushStop(self) : self.methodPushStop = True return def pushTest(self) : self.methodPushTest = Ture return def setStopBlock(self, blockId) : self.methodBlockStop = blockId return def setStopDistance(self, dist) : self.methodLocoDistanceRedStop = dist return def updateMemoryWithCurrentSpeed(self, memoryId) : mem = jmri.InstanceManager.memoryManagerInstance().provideMemory(memoryId) if (mem != None) : if (self.currentThrottle != None) : mem.setValue(((int)(round(self.currentThrottle.getSpeedSetting() * 100, 0))).toString()) else : mem.setValue("0") return def returnCurrentSpeed(self) : if (self.currentThrottle != None) : v = (((int)(round(self.currentThrottle.getSpeedSetting() * 100, 0))).toString()) else : v = "0" return(v) def updateMemoryWithCurrentBlock(self, memoryId) : mem = jmri.InstanceManager.memoryManagerInstance().provideMemory(memoryId) if (mem != None) : if (self.currentBlock != None) : mem.setValue(self.giveBlockName(self.currentBlock)) else : mem.setValue("") return def returnCurrentBlock(self) : if (self.currentBlock != None) : v = self.giveBlockName(self.currentBlock) else : v = "" return(v) # if you are running the RobotThrottle completely interactive, the following two lines are all you need rb1 = LocoThrot() rb1.start() # However, if you are automating the automation, then ## Options for doing more via scripts or Logix Jython command line option ## this will set the loco number, if a matching roster entry is found, it will load the values ## rb1.setLoco("111") ## rb1.setLocoEast() ## rb1.setLocoWest() ## rb1.locoHeadlightOn() ## rb1.locoHeadlightOff() ## rb1.soundShortHorn() ## rb1.soundLongHorn() ## this will set the starting block ## rb1.setStartBlock("LB25") ## this will enable the loco to move according to the signals ## rb1.pushStart() ## rb1.pushStop() ## this sets a block to stop at ## rb1.setStopBlock("LB27") ## shrink the display size ## rb1.pushShrink()