jython/LnSendTool.py
# This script will send several of the most command loconet type messages
# These include switch, feedback and sensor type messages
# DCC signal packets can also be sent via loconet
#
# Messages are configurated using radio buttons and combo boxes
#
# Portions of this script are taken from LnPowerButton.py by Bob Jacobsen
# Author: Bill Robinson with help from Bob Jacobsen
#
# 5/02/06 - Bill Robinson
# This adds several buttons to the example
# Switch, feedback and sensor type LocoNet messages can be sent
# The original PM4 button and action are not used
#
# version 1.3 - 2/6/13 Changed DCC Signal packet to match JMRI definition
import jmri
import java
import java.awt
import java.awt.event
import javax.swing
typePacket = 0
# set the intended LocoNet connection by its index; when you have just 1 connection index = 0
connectionIndex = 0
def whenSendButtonClicked(event) :
# Based on user selection, prepare the arg for the specific LocoNet message
# Loconet message form - opcode,ARG1,ARG2,CHK - the checksum is not calculated here
lnAddress = int(lAddress.text) - 1 # get address from entry field and adjust for LocoNet
ARG1 = lnAddress - ((lnAddress / 128) * 128)
ARG2 = lnAddress / 128
ARG3 = ARG4 = ARG5 = ARG6 = ARG7 = ARG8 = ARG9 = 0
msgLength = 4 # number of bytes in the LocoNet message - includes checksum
if msgTypeBox.getSelectedItem() == "Switch" :
opcode = 176 # 0xB0
if msgActBox.getSelectedIndex() == 0 : # if close
ARG2 = ARG2 + 32
if msgOutBox.getSelectedIndex() == 0 : # if output on
ARG2 = ARG2 + 16
elif msgTypeBox.getSelectedItem() == "Feedback" :
opcode = 177 # 0xB1
ARG2 = ARG2 + 64 # set bit 6 in B1 or B2 type messages
if msgActBox.getSelectedIndex() == 0 :
ARG2 = ARG2 + 16 # set bit 4
if msgOutBox.getSelectedIndex() == 0 : # if aux
ARG2 = ARG2 + 32 # set bit 5
else :
opcode = 178 # 0xB2
# address of B2 is mapped to 4K space ie one more bit
remainder = lnAddress % 2
lnAddress = lnAddress / 2
ARG1 = lnAddress - ((lnAddress / 128) * 128)
ARG2 = lnAddress / 128
ARG2 = ARG2 + 64 # set bit 6 in B1 or B2 type messages
if remainder == 1 : # determine lsb based on remainder of divide
ARG2 = ARG2 + 32 # set bit 5
if msgActBox.getSelectedIndex() == 0 : # if Hi
ARG2 = ARG2 + 16 # set bit 4
sendLoconetMsg(msgLength,opcode,ARG1,ARG2,ARG3,ARG4,ARG5,ARG6,ARG7,ARG8,ARG9)
return
def whenSetButtonClicked(event) :
# Based on user selection, prepare the LocoNet interrogation message
msgTypeBox.setSelectedIndex(0)
msgOutBox.setSelectedIndex(1)
if interBox.getSelectedItem() == "1017c" :
lAddress.setText("1017") # put address in field
msgActBox.setSelectedIndex(0)
elif interBox.getSelectedItem() == "1017t" :
lAddress.setText("1017") # put address in field
msgActBox.setSelectedIndex(1)
elif interBox.getSelectedItem() == "1018c" :
lAddress.setText("1018") # put address in field
msgActBox.setSelectedIndex(0)
elif interBox.getSelectedItem() == "1018t" :
lAddress.setText("1018") # put address in field
msgActBox.setSelectedIndex(1)
elif interBox.getSelectedItem() == "1019c" :
lAddress.setText("1019") # put address in field
msgActBox.setSelectedIndex(0)
elif interBox.getSelectedItem() == "1019t" :
lAddress.setText("1019") # put address in field
msgActBox.setSelectedIndex(1)
elif interBox.getSelectedItem() == "1020c" :
lAddress.setText("1020") # put address in field
msgActBox.setSelectedIndex(0)
else :
interBox.getSelectedItem() == "1020t"
lAddress.setText("1020") # put address in field
msgActBox.setSelectedIndex(1)
return
def whenSendDccButtonClicked(event) :
# Based on user selection, prepare the arg for the specific LocoNet message
# OPC_IMM_PACKET 0xED ;SEND n-byte packet immediate LACK
# <0xED>,<0B>,<7F>,<REPS>,<DHI>,<IM1>,<IM2>,<IM3>,<IM4>,<IM5>,<CHK>
# <DHI>=<0,0,1,IM5.7-IM4.7,IM3.7,IM2.7,IM1.7>
# <REPS> D4,5,6=#IM bytes,D3=0(reserved); D2,1,0=repeat CNT
global typePacket
repeatCount = 1 # causes two DCC packets to be sent from command station
numOfBytes = 2 # number of bytes in the DCC packet - not including checksum
msgLength = 11 # number of bytes in the LocoNet message - includes checksum
opcode = 237 # 0XED
opcode2 = 11 # 0X0B
opcode3 = 127 # 0X7F
dhi = 32
im1 = im3 = im4 = im5 = 0
if typePacket == 0 :
im1 = 0
im2 = 0
elif typePacket == 1 : # Decoder Idle
im1 = 127
im2 = 0
dhi = 1 + 32
numOfBytes = 1
elif typePacket == 2 : # Broadcast Stop
im1 = 0
im2 = 96
else : # Signal packet - 10AAAAAA 0 0AAA0aa1 0 000XXXXX 0 EEEEEEEE
# a - two low bit of the address
# A - address, X - aspect number
addr = int(sgAddress.text) - 1 # change text to a number and subtrac one
# print "raw address", addr
toLsb = addr & 3 # extract bits 1,0 for second byte 0xxx0AA1
addr = addr / 4 # shift down by two
# print "mod address", addr
dhi = 1 # bit 7 of im1 is a one (10AAAAAA)
im1 = addr & 0x3F # extract six bits 7-2 for first byte 10AAAAAA
im2 = addr / 64 # shift down by 6 bits
# print "im1, im2", im1, im2
im2 = 7 - im2 # invert the three bits by subtracting from seven
# print "im2 inverted", im2
im2 = im2 * 16 # shift up by 5 bits
im2 = im2 + (toLsb * 2) + 1
# print "im2 final", im2
im3 = int(aspect.text) & 0x7F
if (int(aspect.text) & 0X80) == 0x80:
dhi = dhi + 4
numOfBytes = 3
reps = repeatCount + (numOfBytes * 16)
sendLoconetMsg(msgLength,opcode,opcode2,opcode3,reps,dhi,im1,im2,im3,im4,im5)
return
def sendLoconetMsg(msgLength,opcode,ARG1,ARG2,ARG3,ARG4,ARG5,ARG6,ARG7,ARG8,ARG9) :
# format and send the specific LocoNet message
# send up to 11 bytes in the message - includes checksum
packet = jmri.jmrix.loconet.LocoNetMessage(msgLength)
if msgLength == 4 :
packet.setElement(0, opcode)
packet.setElement(1, ARG1)
packet.setElement(2, ARG2)
else :
packet.setElement(0, opcode)
packet.setElement(1, ARG1)
packet.setElement(2, ARG2)
packet.setElement(3, ARG3)
packet.setElement(4, ARG4)
packet.setElement(5, ARG5)
packet.setElement(6, ARG6)
packet.setElement(7, ARG7)
packet.setElement(8, ARG8)
packet.setElement(9, ARG9)
jmri.InstanceManager.getList(jmri.jmrix.loconet.LocoNetSystemConnectionMemo).get(connectionIndex).getLnTrafficController().sendLocoNetMessage(packet)
print "Packet", packet # print packet to Script Output window
prevMsg.setText(str(packet)) # put packet in hex in field
return
# Actions for radio buttons
def whenSwButtonClicked(event) :
global typePacket
typePacket = 0
signalButtonFalse()
return
def whenFbButtonClicked(event) :
global typePacket
typePacket = 1
signalButtonFalse()
return
def whenSnButtonClicked(event) :
global typePacket
typePacket = 2
signalButtonFalse()
return
def whenSgButtonClicked(event) :
global typePacket
typePacket = 3
aspect.setEnabled(True)
sgAddress.setEnabled(True)
sgAddress.setToolTipText("Range 1-2040")
aspect.setToolTipText("Range 0-31")
return
def signalButtonFalse() :
aspect.setEnabled(False)
sgAddress.setEnabled(False)
sgAddress.setToolTipText("Disabled because not in this mode")
aspect.setToolTipText("Disabled because not in this mode")
return
# Class to handle a listener event from JComboBox
class MsgTypeListener(java.awt.event.ItemListener):
def itemStateChanged(self, event):
if (event.getItem() == "Switch") :
# print msgTypeBox.getSelectedIndex(), "Selected index"
if (event.getStateChange() == java.awt.event.ItemEvent.SELECTED) :
# Switch selected
print event.getItem(), "Selected"
msgActBox.setEnabled(True)
msgActBox.setToolTipText(None)
msgOutBox.removeItemAt(0)
msgOutBox.insertItemAt("On",0)
msgOutBox.removeItemAt(1)
msgOutBox.insertItemAt("Off",1)
outputLabel.setText(" Output")
else :
# Switch deselected
print event.getItem(), "Deselected"
elif (event.getItem() == "Feedback") :
if (event.getStateChange() == java.awt.event.ItemEvent.SELECTED) :
print event.getItem(), "Selected"
msgOutBox.removeItemAt(0)
msgOutBox.insertItemAt("Sw",0)
msgOutBox.removeItemAt(1)
msgOutBox.insertItemAt("Aux",1)
outputLabel.setText(" Input Type")
else :
print event.getItem(), "Deselected"
elif (event.getItem() == "Sensor") :
if (event.getStateChange() == java.awt.event.ItemEvent.SELECTED) :
print event.getItem(), "Selected"
msgActBox.removeItemAt(0)
msgActBox.insertItemAt("Hi",0)
msgActBox.removeItemAt(1)
msgActBox.insertItemAt("Lo",1)
msgOutBox.setEnabled(False)
msgOutBox.setToolTipText("Disabled because not in this mode")
else :
print event.getItem(), "Deselected"
msgActBox.removeItemAt(0)
msgActBox.insertItemAt("Close",0)
msgActBox.removeItemAt(1)
msgActBox.insertItemAt("Throw",1)
msgOutBox.setEnabled(True)
msgOutBox.setToolTipText(None)
else :
print "Error, unexpected item:", event.getItem()
return
# Start to initialize the GUI
# Create buttons and define action
sendButton = javax.swing.JButton("Send")
sendButton.actionPerformed = whenSendButtonClicked
setButton = javax.swing.JButton("Set")
setButton.actionPerformed = whenSetButtonClicked
setButton.setToolTipText("Set up the interrogation message")
sendDccButton = javax.swing.JButton("Send")
sendDccButton.actionPerformed = whenSendDccButtonClicked
sendDccButton.setAlignmentX(java.awt.Component.RIGHT_ALIGNMENT)
msgTypeBox = javax.swing.JComboBox()
msgTypeBox.addItem("Switch")
msgTypeBox.addItem("Feedback")
msgTypeBox.addItem("Sensor")
msgTypeBox.itemListener = MsgTypeListener()
msgActBox = javax.swing.JComboBox()
msgActBox.addItem("Close")
msgActBox.addItem("Throw")
msgOutBox = javax.swing.JComboBox()
msgOutBox.addItem("On")
msgOutBox.addItem("Off")
interBox = javax.swing.JComboBox()
interBox.addItem("1017c")
interBox.addItem("1017t")
interBox.addItem("1018c")
interBox.addItem("1018t")
interBox.addItem("1019c")
interBox.addItem("1019t")
interBox.addItem("1020c")
interBox.addItem("1020t")
interBox.setToolTipText("Select the interrogation command")
swButton = javax.swing.JRadioButton("Decoder Reset")
fbButton = javax.swing.JRadioButton("Decoder Idle")
snButton = javax.swing.JRadioButton("Broadcast Stop")
sgButton = javax.swing.JRadioButton("Signal")
swButton.actionPerformed = whenSwButtonClicked
fbButton.actionPerformed = whenFbButtonClicked
snButton.actionPerformed = whenSnButtonClicked
sgButton.actionPerformed = whenSgButtonClicked
sgButton.setToolTipText("Extended Accessory Decoder Packet")
swButton.setSelected(True) # initially set the group selection
# create fields
lAddress = javax.swing.JTextField(4) # sized to hold 4 characters
lAddress.setText("1") # initialize field
sgAddress = javax.swing.JTextField(4) # sized to hold 4 characters
sgAddress.setText("1") # initialize field
sgAddress.setEnabled(False) # initially disabled until signal selected
sgAddress.setToolTipText("Disabled because not in this mode")
aspect = javax.swing.JTextField(4) # sized to hold 4 characters
aspect.setText("0") # initialize field
aspect.setEnabled(False) # initially disabled until signal selected
aspect.setToolTipText("Disabled because not in this mode")
prevMsg = javax.swing.JTextField(22) # sized to hold 22 characters, initially empty
outputLabel = javax.swing.JLabel(" Output") # insert spaces to help with display alignment
# create a frame to display the buttons and panels
f = javax.swing.JFrame("LocoNet Send Tool")
f.contentPane.setLayout(javax.swing.BoxLayout(f.contentPane, javax.swing.BoxLayout.Y_AXIS))
group1 = javax.swing.ButtonGroup() # create a radio button group
group1.add(swButton)
group1.add(fbButton)
group1.add(snButton)
group1.add(sgButton)
radioPanel1 = javax.swing.JPanel() # put the radio buttons in a panel
radioPanel1.setLayout(javax.swing.BoxLayout(radioPanel1, javax.swing.BoxLayout.Y_AXIS))
radioPanel1.add(swButton)
radioPanel1.add(fbButton)
radioPanel1.add(snButton)
radioPanel1.add(sgButton)
panela = javax.swing.JPanel() # panel to hold interrogate and set button
panela.add(interBox)
panela.add(setButton)
# put a border around the interrogate selection box and set button
panela.setBorder(javax.swing.BorderFactory.createMatteBorder(1,1,1,1, java.awt.Color.gray))
panelb = javax.swing.JPanel() # panel to hold send button
panelb.add(sendButton)
panelc = javax.swing.JPanel() # panel to create a space
panel = javax.swing.JPanel() # panel to hold the following panels
panel.add(panela) # add the interrogate and set button panel
panel.add(panelc) # add a panel to put a space between the other panels
panel.add(panelb) # add the send button panel
panel0 = javax.swing.JPanel() # use panel to display items horizontally
panel0.add(javax.swing.JLabel("Address")) # put label discription before combobox
panel0.add(lAddress)
panel1 = javax.swing.JPanel()
panel1.add(javax.swing.JLabel("Type of message"))
panel1.add(msgTypeBox)
panel2 = javax.swing.JPanel()
panel2.add(javax.swing.JLabel(" Type of action"))
panel2.add(msgActBox)
panel3 = javax.swing.JPanel()
panel3.add(outputLabel)
panel3.add(msgOutBox)
panel4 = javax.swing.JPanel()
panel4.add(javax.swing.JLabel(" Signal Address"))
panel4.add(sgAddress)
panel5 = javax.swing.JPanel()
panel5.add(javax.swing.JLabel("Signal Aspect number"))
panel5.add(aspect)
panel6 = javax.swing.JPanel()
panel6.add(javax.swing.JLabel("Last Message sent"))
panel6.add(prevMsg)
panel7 = javax.swing.JPanel()
panel7.add(javax.swing.JLabel("------ DCC Packet ------"))
panel8 = javax.swing.JPanel()
panel8.add(sendDccButton)
f.contentPane.add(panel0) # put the buttons and panels in the frame
f.contentPane.add(panel1)
f.contentPane.add(panel2)
f.contentPane.add(panel3)
f.contentPane.add(panel)
f.contentPane.add(javax.swing.JSeparator(javax.swing. JSeparator.HORIZONTAL))
f.contentPane.add(panel7)
f.contentPane.add(radioPanel1)
f.contentPane.add(panel4)
f.contentPane.add(panel5)
f.contentPane.add(panel8)
f.contentPane.add(panel6)
f.pack()
f.show()