# This script displays LocoNet messages. # The frame contains JTextFields, a scroll field, check boxes and a button. # The frame contains Panel arranged such that only the scroll field moves # when the window is resized. # Switch, feedback and sensor messages can be filtered. # # Author: Bill Robinson with help from Bob Jacobsen # # $Revision: 1.0 $ 1/8/07 # $Revision: 1.1 $ 11/2/10 added second SN address field # $Revision: 1.2 $ 6/3/11 added SN address range fields and second SW address field # $Revision: 1.3 $ 9/2/12 added option to print SN address if its state changed # $Revision: 1.4 $ 4/13/13 added display of decoded signal address and aspect import java import javax.swing aspectNam = " " # ************* Section to Listen to all sensors, printing a line when they change state. # Define routine to map status numbers to text def stateName(state) : if (state == ACTIVE) : return "ACTIVE" if (state == INACTIVE) : return "INACTIVE" if (state == INCONSISTENT) : return "INCONSISTENT" if (state == UNKNOWN) : return "UNKNOWN" return "(invalid)" # Define the sensor listener: Print some # information on the status change. class SensorListener(java.beans.PropertyChangeListener): def propertyChange(self, event): if (event.propertyName == "KnownState") : mesg = "Sensor "+event.source.systemName if (event.source.userName != None) : mesg += " ("+event.source.userName+")" mesg += " from "+stateName(event.oldValue) mesg += " to "+stateName(event.newValue) # print mesg if snChgCheckBox.isSelected() : scrollArea.setText(scrollArea.getText()+mesg+"\n") # You can also speak the message by un-commenting the next line #java.lang.Runtime.getRuntime().exec(["speak", mesg]) # For more info on the speak command, see http://espeak.sf.net/ return listener = SensorListener() # Define a Manager listener. When invoked, a new # item has been added, so go through the list of items removing the # old listener and adding a new one (works for both already registered # and new sensors) class ManagerListener(java.beans.PropertyChangeListener): def propertyChange(self, event): list = event.source.getSystemNameList() for i in range(list.size()) : event.source.getSensor(list.get(i)).removePropertyChangeListener(listener) event.source.getSensor(list.get(i)).addPropertyChangeListener(listener) # Attach the sensor manager listener sensors.addPropertyChangeListener(ManagerListener()) # For the sensors that exist, attach a sensor listener list = sensors.getSystemNameList() for i in range(list.size()) : sensors.getSensor(list.get(i)).addPropertyChangeListener(listener) # ******************** End section # have the text field enable the button when OK NOT used def whenAddressChanged(event) : if (address.text != "") : # address only changed if a value was entered swAddrCheckBox.setEnabled(True) else : swAddrCheckBox.setSelected(False) swAddrCheckBox.setEnabled(False) return # define what button does when clicked and attach that routine to the button def whenEnterButtonClicked(event) : # not used return def whenClearButtonClicked(event): # print "got to here" scrollArea.setText("") # clear the text area return def whenQuitButtonClicked(event): # not used 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): def windowClosing(self,event): # print"window closing" jmri.jmrix.loconet.LnTrafficController.instance().removeLocoNetListener(0xFF,lnListen) list = sensors.getSystemNameList() for i in range(list.size()) : # remove each of the sensor listeners that were added sensors.getSensor(list.get(i)).removePropertyChangeListener(listener) # print "remove" fr.dispose() # close the pane (window) def windowActivated(self,event): return def windowDeactivated(self,event): return def windowOpened(self,event): return def windowClosed(self,event): return #create a Llnmon to parse the message parseMsg = jmri.jmrix.loconet.locomon.Llnmon() #class to handle a listener event loconet messages class MsgListener(jmri.jmrix.loconet.LocoNetListener): def message(self, msg): global aspectNam # print "byte 0", java.lang.Integer.toHexString(msg.getElement(0)) # print "command 1 field ", java.lang.Integer(command.text) # Handle check boxes if swAddrCheckBox.isSelected() or fbAddrCheckBox.isSelected() or snAddrCheckBox.isSelected() or filterCheckBox.isSelected(): if filterCheckBox.isSelected(): if msg.getOpCode() == 176 or msg.getOpCode() == 177 or msg.getOpCode() == 178: displayMsg(msg) else: if swAddrCheckBox.isSelected() and msg.getOpCode() == 176: # msg.turnoutAddr() is an int and address.text is a string if len(address.text) > 0 or len(address2.text) > 0: if len(address.text) > 0: # if field let bank code stops code for some reason if msg.turnoutAddr() == java.lang.Integer.decode(address.text): displayMsg(msg) if len(address2.text) > 0: # if field let bank code stops code for some reason if msg.turnoutAddr() == java.lang.Integer.decode(address2.text): displayMsg(msg) else: displayMsg(msg) if fbAddrCheckBox.isSelected() and msg.getOpCode() == 177: # msg.turnoutAddr() is an int and address.text is a string if len(fbAddress.text) > 0 or len(fbAddress2.text) > 0: if len(fbAddress.text) > 0: # if field let bank code stops code for some reason if msg.turnoutAddr() == java.lang.Integer.decode(fbAddress.text): displayMsg(msg) if len(fbAddress2.text) > 0: # if field let bank code stops code for some reason if msg.turnoutAddr() == java.lang.Integer.decode(fbAddress2.text): displayMsg(msg) else: displayMsg(msg) if snAddrCheckBox.isSelected() and msg.getOpCode() == 178: if len(command.text) > 0 or len(command2.text) > 0 or len(rangeAdd1.text) > 0: if len(command.text) > 0: # if field let bank code stops code for some reason if msg.sensorAddr()+1 == java.lang.Integer.decode(command.text): displayMsg(msg) if len(command2.text) > 0: if msg.sensorAddr()+1 == java.lang.Integer.decode(command2.text): displayMsg(msg) if len(rangeAdd1.text) > 0 and len(rangeAdd2.text) > 0: if msg.sensorAddr()+1 >= java.lang.Integer.decode(rangeAdd1.text) and msg.sensorAddr()+1 <= java.lang.Integer.decode(rangeAdd2.text): displayMsg(msg) else: displayMsg(msg) else : # if no boxes are check just display the message # print "no check boxes" displayMsg(msg) # Extended accessory decoder packet format 10AAAAAA 0AAA0AA1 000XXXXX # Address AAA AAAAAA AA if msg.getOpCode() == 237 and msg.getElement(3) & 0xF0 == 0x30: # if msg.getOpCode() == 237: DHI = msg.getElement(4) raddrlo = msg.getElement(5) raddrhi = msg.getElement(6) if DHI & 0x02 == 0x02: raddrhi = raddrhi | 0x80 # set bit 7 if DHI & 0x01 == 0x01: raddrlo = raddrlo | 0x80 raddrlo = raddrlo << 2 # get hi bits of low address raddrlo = raddrlo & 0xFC # clear low 2 bits temp = raddrhi & 0x06 temp = temp >> 1 raddrlo = raddrlo | temp raddrhi = raddrhi >> 4 raddrhi = ~raddrhi # complement raddrhi = (raddrhi & 0x07) * 256 address = raddrlo + raddrhi + 1 aspect = msg.getElement(7) aspectName(aspect) scrollArea.setText(scrollArea.getText()+" Signal Head "+str(address)+" "+aspectNam+" "+str(aspect)+"\n") return # Add the next Loconet messsage to the scroll area test # If the raw box is checked add the HEX message value in front of message def displayMsg(msg): if rawCheckBox.isSelected(): scrollArea.setText(scrollArea.getText()+msg.toString()+" ") scrollArea.setText(scrollArea.getText()+parseMsg.displayMessage(msg)) # displayMessage adds a carriage return return def aspectName(num): global aspectNam if num == 0: aspectNam = "Red" return if num == 1: aspectNam = "Yellow" return if num == 2: aspectNam = "Green" return if num == 3: aspectNam = "Lunar" return if num == 4: aspectNam = "Flash Red" return if num == 5: aspectNam = "Flash Yellow" return if num == 6: aspectNam = "Flash Green" return if num == 7: aspectNam = "Flash Lunar" return if num == 8: aspectNam = "Dark" return aspectNam = "* ? *" return lnListen = MsgListener() jmri.jmrix.loconet.LnTrafficController.instance().addLocoNetListener(0xFF,lnListen) # start to initialise the GUI # create buttons and define action enterButton = javax.swing.JButton("Start the Run") enterButton.setEnabled(False) # button starts as grayed out (disabled) enterButton.actionPerformed = whenEnterButtonClicked clearButton = javax.swing.JButton("Clear") clearButton.actionPerformed = whenClearButtonClicked quitButton = javax.swing.JButton("Quit") quitButton.actionPerformed = whenQuitButtonClicked swAddrCheckBox = javax.swing.JCheckBox("Filter Sw address") swAddrCheckBox.setToolTipText("Display all switch messages or with the address") #swAddrCheckBox.setEnabled(False) # button starts as grayed out (disabled) snAddrCheckBox = javax.swing.JCheckBox("Filter Sn address") snAddrCheckBox.setToolTipText("Display all sensor messages or with the address") #snAddrCheckBox.setEnabled(False) # button starts as grayed out (disabled) fbAddrCheckBox = javax.swing.JCheckBox("Filter Fb address") fbAddrCheckBox.setToolTipText("Display all feedback messages or with the address") rawCheckBox = javax.swing.JCheckBox("Show raw data") filterCheckBox = javax.swing.JCheckBox("Filter SW, FB & SN") filterCheckBox.setToolTipText("Display switch, feedback or sensor messages") snChgCheckBox = javax.swing.JCheckBox("Show Sn Change") snChgCheckBox.setToolTipText("Display when a sensor state changes") address = javax.swing.JTextField(5) # sized to hold 5 characters, initially empty #address.actionPerformed = whenAddressChanged # if user hit return or enter #address.focusLost = whenAddressChanged # if user tabs away address2 = javax.swing.JTextField(5) # sized to hold 5 characters, initially empty fbAddress = javax.swing.JTextField(5) fbAddress2 = javax.swing.JTextField(5) # create the another text field similarly command = javax.swing.JTextField(5) # sized to hold 5 characters command.setToolTipText("Number from 1 to 2010") #command.actionPerformed = whenCommandChanged #command.focusLost = whenCommandChanged # create the another text field # before this text field works there must be a number in the command field command2 = javax.swing.JTextField(5) # sized to hold 5 characters command2.setToolTipText("Number from 1 to 2010") #command2.actionPerformed = whenCommandChanged #command2.focusLost = whenCommandChanged # create another text field for a range address rangeAdd1 = javax.swing.JTextField(5) # sized to hold 5 characters rangeAdd1.setToolTipText("Start address") # create another text field for a range address rangeAdd2 = javax.swing.JTextField(5) # sized to hold 5 characters rangeAdd2.setToolTipText("End address") # create a text area scrollArea = javax.swing.JTextArea(10, 45) # define a text area with it's size # scrollArea.setText("Put any init text here\n") srcollField = javax.swing.JScrollPane(scrollArea) # put text area in scroll field # create a panel to put the scroll area in # a borderlayout causes the scroll area to fill the space as the window is resized midPanel = javax.swing.JPanel() # midPanel.setBorder(javax.swing.BorderFactory.createMatteBorder(1,8,1,8, java.awt.Color.white)) midPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1,8,1,8)) midPanel.setLayout(java.awt.BorderLayout()) midPanel.add(srcollField) # 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 = WinListener() fr = javax.swing.JFrame("LocoNet Monitor") # argument is the frames title fr.contentPane.setLayout(javax.swing.BoxLayout(fr.contentPane, javax.swing.BoxLayout.Y_AXIS)) fr.addWindowListener(w) # put the text field on a line preceded by a label addressPanel = javax.swing.JPanel() addressPanel.setLayout(java.awt.FlowLayout(2)) # 2 is right align for FlowLayout addressPanel.add(javax.swing.JLabel("Sw Addresses")) addressPanel.add(address) addressPanel.add(javax.swing.JLabel("or")) addressPanel.add(address2) fbAddressPanel = javax.swing.JPanel() fbAddressPanel.setLayout(java.awt.FlowLayout(2)) # 2 is right align for FlowLayout fbAddressPanel.add(javax.swing.JLabel("Fb Addresses")) fbAddressPanel.add(fbAddress) fbAddressPanel.add(javax.swing.JLabel("or")) fbAddressPanel.add(fbAddress2) commandPanel = javax.swing.JPanel() commandPanel.setLayout(java.awt.FlowLayout(2)) # 2 is right align for FlowLayout commandPanel.add(javax.swing.JLabel("Sn Addresses")) commandPanel.add(command) commandPanel.add(javax.swing.JLabel("or")) commandPanel.add(command2) rangePanel = javax.swing.JPanel() rangePanel.setLayout(java.awt.FlowLayout(2)) # 2 is right align for FlowLayout rangePanel.add(javax.swing.JLabel("Sn Addr Range")) rangePanel.add(rangeAdd1) rangePanel.add(javax.swing.JLabel("to")) rangePanel.add(rangeAdd2) temppanel1 = javax.swing.JPanel() temppanel1.setLayout(javax.swing.BoxLayout(temppanel1, javax.swing.BoxLayout.PAGE_AXIS)) temppanel1.add(addressPanel) temppanel1.add(fbAddressPanel) temppanel1.add(commandPanel) temppanel1.add(rangePanel) ckBoxPanel = javax.swing.JPanel() ckBoxPanel.setLayout(javax.swing.BoxLayout(ckBoxPanel, javax.swing.BoxLayout.PAGE_AXIS)) ckBoxPanel.add(filterCheckBox) ckBoxPanel.add(swAddrCheckBox) ckBoxPanel.add(fbAddrCheckBox) ckBoxPanel.add(snAddrCheckBox) ckBoxPanel.add(rawCheckBox) ckBoxPanel.add(snChgCheckBox) butPanel = javax.swing.JPanel() butPanel.setLayout(javax.swing.BoxLayout(butPanel, javax.swing.BoxLayout.PAGE_AXIS)) butPanel.add(clearButton) buttonPanel = javax.swing.JPanel() buttonPanel.add(butPanel) blankPanel = javax.swing.JPanel() blankPanel.setLayout(java.awt.BorderLayout()) entryPanel = javax.swing.JPanel() entryPanel.setLayout(javax.swing.BoxLayout(entryPanel, javax.swing.BoxLayout.LINE_AXIS)) entryPanel.add(temppanel1) entryPanel.add(ckBoxPanel) entryPanel.add(buttonPanel) entryPanel.add(blankPanel) # create the top panel # it is a 1,1 GridLayout used to keep all controls stationary when the window is resized topPanel = javax.swing.JPanel() topPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1,8,8,8)) topPanel.setLayout(java.awt.GridLayout(1,1)) topPanel.add(entryPanel) # create a ppanel to give some space under the scrolling field bottomPanel = javax.swing.JPanel() # bottomPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1,8,1,8)) # Put contents in frame and display fr.contentPane.add(topPanel) fr.contentPane.add(midPanel) fr.contentPane.add(bottomPanel) fr.pack() fr.show()