Files
JIMRI/jython/SensorFromBxp88ShortCircuit.py
T
2026-06-17 14:00:51 +02:00

259 lines
12 KiB
Python

#
# A Digitrax BXP88 Short-Circuit-to-Sensor "follower"
#
# This script provides a set of JMRI sensors where each sensor's state "follows"
# one of the Digitrax BXP88 device's detection sections reported "Short Circuit state",
# as reported by LocoNet messaging.
#
# This script can be configured to monitor one BXP88 device or all devices. Within
# any BXP88 device, this script separately monitors all 8 of the device's detection
# sections. Each separate BXP88 detection section is monitored via a separate JMRI
# "internal" sensor.
#
# Sensor naming is of the form "ISPM88ShortX-Y". This is interpreted as:
# "I" for JMRI's "Internal" object grouping
# "S" for "Sensor" (in the "Internal" object grouping)
# "PM" for "Power Management" effects
# "88" for a source from a "BXP88" device
# "Short" to denote the sensor as reporting "Short-circuit detection" state
# "X" represents the BoardID number reported in the LocoNet message from the BXP88
# device
# "-" is a character to separate the device IDfrom the "Detection Section" number
# "Y" represents the "Detection Section" number
#
# Some Examples"
# ISPM88Short1-1 the JMRI internal sensor for BXP88 (board ID 1) Detection Section 1
# ISPM88Short1-2 the JMRI internal sensor for BXP88 (board ID 1) Detection Section 2
# ISPM88Short1-3 the JMRI internal sensor for BXP88 (board ID 1) Detection Section 3
# ...
# ISPM88Short1-8 the JMRI internal sensor for BXP88 (board ID 1) Detection Section 8
#
# ISPM88Short2-1 the JMRI internal sensor for BXP88 (board ID 2) Detection Section 1
# ISPM88Short2-2 the JMRI internal sensor for BXP88 (board ID 2) Detection Section 2
# ISPM88Short2-3 the JMRI internal sensor for BXP88 (board ID 2) Detection Section 3
# ...
# ISPM88Short2-8 the JMRI internal sensor for BXP88 (board ID 2) Detection Section 8
# ...
# ISPM88Shout127-1 the JMRI internal sensor for BXP88 (board ID 2) Detection Section 8
#
# As written, this script supports either
# - monitoring a single BXP88 device, specified by BoardId number; _all
# eight "Detection sections"_, via separate sensors
# - monitoring of _all_ BXP88 devices and each 8 Detection sensors
# of each BXP88, via separate sensors (8 sensors per BXP88).
#
# Configuring the script
# ----------------------
# - See comments below for information on configuring the behavior of this
# script:
# -- the "interestingBoardIdNumber" determines what BXP88 device BoardID
# value to watch for, or, alternately, configures the script to monitor
# for LocoNet messages from _all_ BXP88 BoardID values, and causes this
# script to follow each unique BXP88 BoardID number using a unique Sensor.
# -- DebuggingMessages determines whether debugging messages will be sent to
# the JMRI "Script Output" window.
#
# Script details
# --------------
# This script has three main functional parts (in order of appearance).
#
# The first part provides some "import" statements and declares some variables
# that influence script behavior. Users may wish to modify some variable values
# in this section.
#
# The second part is where the bulk of the work is done, because it declares and
# defines a "Bxp88ShortCircuitStateListener" class which implements a JMRI
# "LocoNetListener" which has BXP88-specific features. The class's "message()"
# method is triggered upon JMRI receipt of a LocoNet message. It "parses" the
# received LocoNet message to determine if it is a valid "Short-circuit" status
# message from a BXP88 device. If it is, the "BoardID" value is extracted and
# checked to determine if it is a BXP88 for which the message should be reported
# via a JMRI Sensor object(s). This determination is made in the
# isInterestingBoardId()method in Section 2, described below.
#
# For a message that is interesting to the script, the BoardId number from the
# message is used to create eight variables representing the JMRI Sensor name(s)
# associated with the eight detection sections which report short circuit status. Once
# created (if necessary), those variables are updated with the current short circuit
# status from the message.
#
# The isInterestingBoardId() method uses the user-customization variables of
# Section 1, along with the LocoNet BXP88 Short-circuit status message's extracted
# boardID value, to determine whether or not to update a sensor. It returns True
# if the message's BoardID value refers to a BXP88 which should be tracked via a
# JMRI sensor(s), or it returns False to indicate that the message should be ignored.
#
# The third part of the script creates an instance of the LocoNet Listener class
# and "connects" that instance to the LocoNet connection.
#
# Notes and Limitations
# ---------------------
#
# Note the following limitations:
# - So far as the script author knows, there is _no_ way to query the short-circuit
# state of a BXP88 device. As such, until a BXP88 device's detection section
# changes its short-circuit state, there is _no_ way to tell what the device's
# detection section's current state is.
#
# - This script creates, if necessary, the Internal Sensor used to "follow" the
# BXP88 device's detection section's short-circuit state. Before a BXP88 reports
# its short-circuit state, JMRI will _not_ have a corresponding Sensor object,
# unless you have opened a JMRI "panel" XML file which was saved when the
# corresponding sensor was known to JMRI.
#
# - If you open a saved JMRI Panel XML file and that file had one or more JMRI
# Sensor objects created by this script, such as if you "save" a panel XML
# file, JMRI will create the associated Sensors but will leave those sensors
# in the "unknown" state. Once a BXP88 sends a "Short-circuit" event
# LocoNet message, the associated sensor(s) will be updated to "Inactive" or
# "Active", as appropriate.
#
# - This script provides little if any "error-checking" of the configuration
# variables. Specifying an "out-of-range" connection index will result in
# an exception reported in the JMRI Console log as well as failure of the
# script to perform. Specifying a BoardID value of 0 will not result in
# capture of _any_ BXP88 short-circuit data.
# Various other configuration boo-boos may result in exceptions in the log
# and/or failure of this script to perform.
#
# Script version 1.0 created 25Nov2024 by Bob M.
# Part 1:
import jmri
import java
# Initialize the variable "connectionIndex" to specify which of potentially
# several LocoNet connections this script watches. This is specified by the
# LocoNet connection's "index":
# - When you have just a single connection, the only usable index is 1.
# - When you have more than one LocoNet connection defined, the index is the
# count, from the left, of the "tabs" for LocoNet connections as seen
# in the "Connections" page of the JMRI "Preferences".
connectionIndex = 1 # this is appropriate for a JMRI install with only a single
# LocoNet connection, or when monitoring the _first_ of
# available (and active) LocoNet connections.
reportAllBxp88s = False # assume that only a specific BXP88 Board ID number
# will be reported. This value may be overridden below.
# Initialize the variable "interestingBoardIdNumber" to reflect the
# BoardId of the BXP88 you care about. If you want to create a sensor
# for _each_ BXP88 BoardId that reports autoreversing state, set
# interestingBoardIdNumber to a negative value. Uncomment (and modify, # as
# needed) one of the examples shown below.
#
# Example: follow only Autoreversing messages from BXP88 BoardID 1
#interestingBoardIdNumber = 1
#
# Example: follow only Autoreversing messages from BXP88 BoardID 12
interestingBoardIdNumber = 12
# Example: follow every BXP88 Short-Circuit message, regardless of BoardID, by
# overriding the previous value of reportAllBxp88s. Note that the value of the
# interestingBoardIdNumber variable has no effect which reportAllBxp88s is True.:
reportAllBxp88s = True
# Print debugging messages to the "script output" window?
# - True enables the debugging messages.
# - False disables the debugging messages.
DebuggingMessages = True
# Part 2:
# Define a LocoNet "BXP88 Short-Circuit Event" listener class
class Bxp88ShortCircuitStateListener(jmri.jmrix.loconet.LocoNetListener) :
def isInterestingBoardId(self, boardId) :
# This method is used to determine whether a particular BXP88's
# short-circuit messages will be used to update appropriately-named
# JMRI sensors.
#
# Returns True to update the sensor based on the LocoNet message contents.
# Returns False to ignore the BXP88's LocoNet Message.
#
# The user may modify this method to suit his/her personal "selection"
# criteria.
if (reportAllBxp88s == True) :
return True
if (boardId == interestingBoardIdNumber) :
return True
return False
def message(self, msg) :
# This method (if registered as a LocoNet listener!) parses all incoming
# LocoNet messages and deals with BXP88 autoreversing messages.
# Is this message an autoreversing message from a BPX88 device?
if ((msg.getNumDataElements() == 6) and (msg.getElement(0) == 0xD0) \
and ((msg.getElement(1) & 0x7E) == 0x62) and ((msg.getElement(3) & 0xF0) == 0x20) \
and ((msg.getElement(4) & 0xF0) == 0x20)) :
# Yes, the message is for a BXP88 autoreversing message!
boardId = 1 + (msg.getElement(2) & 0x7F) + ((msg.getElement(1) & 0x1) << 7)
# Determine whether to update a sensor for the reported
# BoardId number seen in this LocoNet message
if (self.isInterestingBoardId(boardId) == False) :
# Not interested in this particular board number
if (DebuggingMessages == True) :
print ("Ignoring message apparantly from BXP88 with boardID #"),
print (boardId),
print (".")
return
for section in range (0,8):
# Specify the sensor to be updated (create it if it isn't
# present!)
s = sensors.provideSensor("ISPM88Short"+str(boardId)+"-"+str(section+1))
# Update the sensor based on data from the LocoNet message
if (section < 4) :
data = (msg.getElement(4) >> section) & 0x01
else:
data = (msg.getElement(3) >> (section - 4) ) & 0x01
if (data == 0) :
# Not Shorted!
s.state = INACTIVE
if (DebuggingMessages == True) :
print ("Sensor"),
print ( s.getSystemName()),
print ("is set to Inactive (i.e. 'live' track) for"),
print ("short-circuit state of section "),
print (str(section+1)),
print ("of BXP88 with boardID #"),
print (boardId),
print (".")
else :
# Shorted!
s.state = ACTIVE
if (DebuggingMessages == True) :
print ("Sensor"),
print ( s.getSystemName()),
print ("is set to Active (i.e. 'shorted' track) for"),
print ("short-circuit state of section "),
print (str(section+1)),
print ("of BXP88 with boardID #"),
print (boardId),
print (".")
# Nothing more to do, so ...
return
# Part 3:
# Create an instance of the listener class
ln = Bxp88ShortCircuitStateListener()
# Register the Listener
jmri.InstanceManager.getList(jmri.jmrix.loconet.LocoNetSystemConnectionMemo).get(connectionIndex - 1).getLnTrafficController().addLocoNetListener(0xFF,ln)
if (DebuggingMessages == True) :
print "Registered the BXP88 Short-Circuit Message listener"
# end of script