Class SlotManager

All Implemented Interfaces:
EventListener, PropertyChangeFirer, PropertyChangeProvider, CommandStation, Disposable, LocoNetListener, Programmer
Direct Known Subclasses:
UhlenbrockSlotManager

Controls a collection of slots, acting as the counter-part of a LocoNet command station.

A SlotListener can register to hear changes. By registering here, the SlotListener is saying that it wants to be notified of a change in any slot. Alternately, the SlotListener can register with some specific slot, done via the LocoNetSlot object itself.

Strictly speaking, functions 9 through 28 are not in the actual slot, but it's convenient to imagine there's an "extended slot" and keep track of them here. This is a partial implementation, though, because setting is still done directly in LocoNetThrottle. In particular, if this slot has not been read from the command station, the first message directly setting F9 through F28 will not have a place to store information. Instead, it will trigger a slot read, so the following messages will be properly handled.

Some of the message formats used in this class are Copyright Digitrax, Inc. and used with permission as part of the JMRI project. That permission does not extend to uses in other software products. If you wish to use this code, algorithm or these message formats outside of JMRI, please contact Digitrax Inc for separate permission.

This Programmer implementation is single-user only. It's not clear whether the command stations can have multiple programming requests outstanding (e.g. service mode and ops mode, or two ops mode) at the same time, but this code definitely can't.

  • Field Details

  • Constructor Details

    • SlotManager

      Constructor for a SlotManager on a given TrafficController.
      Parameters:
      tc - Traffic Controller to be used by SlotManager for communication with LocoNet
  • Method Details

    • loadSlots

      protected void loadSlots(boolean initialize)
      Initialize the slots array.
      Parameters:
      initialize - if true a new slot is created else it is just updated with type and protocol
    • sendPacket

      public boolean sendPacket(byte[] packet, int sendCount)
      Send a DCC packet to the rails. This implements the CommandStation interface. This mechanism can pass any valid NMRA packet of up to 6 data bytes (including the error-check byte). When available, these messages are forwarded to LocoNet using a "throttledTransmitter". This decreases the speed with which these messages are sent, resulting in lower throughput, but fewer rejections by the command station on account of "buffer-overflow".
      Specified by:
      sendPacket in interface CommandStation
      Parameters:
      packet - the data bytes of the raw NMRA packet to be sent. The "error check" byte must be included, even though the LocoNet message will not include that byte; the command station will re-create the error byte from the bytes encoded in the LocoNet message. LocoNet is unable to propagate packets longer than 6 bytes (including the error-check byte).
      sendCount - the total number of times the packet is to be sent on the DCC track signal (not LocoNet!). Valid range is between 1 and 8. sendCount will be forced to this range if it is outside of this range.
      Returns:
      true if the operation succeeds, false otherwise.
    • setLoconet2Supported

      public void setLoconet2Supported(int value)
      Parameters:
      value - the loconet protocol supported
    • getSlot248CommandStationType

      Get the Command Station type reported in slot 248 message
      Returns:
      model
    • getSlot250CSSlots

      public int getSlot250CSSlots()
      Get the total number of slots reported in the slot250 message;
      Returns:
      number of slots
    • getLoconetProtocol

      public int getLoconetProtocol()
      Returns:
      the loconet protocol supported
    • slot

      public LocoNetSlot slot(int i)
      Access the information in a specific slot. Note that this is a mutable access, so that the information in the LocoNetSlot object can be changed.
      Parameters:
      i - Specific slot, counted starting from zero.
      Returns:
      The Slot object
    • getNumSlots

      public int getNumSlots()
    • slotFromLocoAddress

      public void slotFromLocoAddress(int i, SlotListener l)
      Obtain a slot for a particular loco address.

      This requires access to the command station, even if the locomotive address appears in the current contents of the slot array, to ensure that our local image is up-to-date.

      This method sends an info request. When the echo of this is returned from the LocoNet, the next slot-read is recognized as the response.

      The object that's looking for this information must provide a SlotListener to notify when the slot ID becomes available.

      The SlotListener is not subscribed for slot notifications; it can do that later if it wants. We don't currently think that's a race condition.

      Parameters:
      i - Specific slot, counted starting from zero.
      l - The SlotListener to notify of the answer.
    • addSlotListener

      public void addSlotListener(SlotListener l)
      Add a slot listener, if it is not already registered

      The slot listener will be invoked every time a slot changes state.

      Parameters:
      l - Slot Listener to be added
    • removeSlotListener

      Add a slot listener, if it is registered.

      The slot listener will be removed from the list of listeners which are invoked whenever a slot changes state.

      Parameters:
      l - Slot Listener to be removed
    • notify

      protected void notify(LocoNetSlot s)
      Trigger the notification of all SlotListeners.
      Parameters:
      s - The changed slot to notify.
    • message

      public void message(LocoNetMessage m)
      Listen to the LocoNet. This is just a steering routine, which invokes others for the various processing steps.
      Specified by:
      message in interface LocoNetListener
      Parameters:
      m - incoming message
    • checkSpecialSlots

      void checkSpecialSlots(LocoNetMessage m, int slot)
    • checkLoconetProtocol

    • getDirectFunctionAddress

      Checks a LocoNet message to see if it encodes a DCC "direct function" packet.
      Parameters:
      m - a LocoNet Message
      Returns:
      the loco address if the LocoNet message encodes a "direct function" packet, else returns -1
    • getDirectDccPacket

      Extracts a DCC "direct packet" from a LocoNet message, if possible.

      if this is a direct DCC packet, return as one long else return -1. Packet does not include address bytes.

      Parameters:
      m - a LocoNet message to be inspected
      Returns:
      an integer containing the bytes of the DCC packet, except the address bytes.
    • isExtFunctionMessage

      Determines if a LocoNet message encodes a direct request to control DCC functions F9 thru F28
      Parameters:
      m - the LocoNet message to be evaluated
      Returns:
      true if the message is an external DCC packet request for F9-F28, else false.
    • findSlotFromMessage

      Extracts the LocoNet slot number from a LocoNet message, if possible.

      Find the slot number that a message references

      This routine only looks for explicit slot references; it does not, for example, identify a loco address in the message and then work thru the slots to find a slot which references that loco address.

      Parameters:
      m - LocoNet Message to be inspected
      Returns:
      an integer representing the slot number encoded in the LocoNet message, or -1 if the message does not contain a slot reference
    • checkLackByte1

      protected boolean checkLackByte1(int byte1)
      Check CV programming LONG_ACK message byte 1

      The following methods are for parsing LACK as response to CV programming. It is divided into numerous small methods so that each bit can be overridden for special parsing for individual command station types.

      Parameters:
      byte1 - from the LocoNet message
      Returns:
      true if byte1 encodes a response to a OPC_SL_WRITE or an Expanded Slot Write
    • checkLackTaskAccepted

      protected boolean checkLackTaskAccepted(int byte2)
      Checks the status byte of an OPC_LONG_ACK when performing CV programming operations.
      Parameters:
      byte2 - status byte
      Returns:
      True if status byte indicates acceptance of the command, else false.
    • checkLackProgrammerBusy

      protected boolean checkLackProgrammerBusy(int byte2)
      Checks the OPC_LONG_ACK status byte response to a programming operation.
      Parameters:
      byte2 - from the OPC_LONG_ACK message
      Returns:
      true if the programmer returned "busy" else false
    • checkLackAcceptedBlind

      protected boolean checkLackAcceptedBlind(int byte2)
      Checks the OPC_LONG_ACK status byte response to a programming operation to see if the programmer accepted the operation "blindly".
      Parameters:
      byte2 - from the OPC_LONG_ACK message
      Returns:
      true if the programmer indicated a "blind operation", else false
    • okToIgnoreLack

      protected boolean okToIgnoreLack(int byte2)
      Some LACKs with specific OPC_LONG_ACK status byte values can just be ignored.
      Parameters:
      byte2 - from the OPC_LONG_ACK message
      Returns:
      true if this form of LACK can be ignored without a warning message
    • setAcceptAnyLACK

      public final void setAcceptAnyLACK()
      Indicate that the command station LONG_ACK response details can be ignored for this operation. Typically this is used when accessing Loconet-attached boards.
    • handleLongAck

      protected void handleLongAck(LocoNetMessage m)
      Handles OPC_LONG_ACK replies to programming slot operations. LACK 0x6D00 which requests a retransmission is handled separately in the message(..) method.
      Parameters:
      m - LocoNet message being analyzed
    • notifyProgListenerEndAfterDelay

      Internal method to notify ProgListener after a short delay that the operation is complete. The delay ensures that the target device has completed the operation prior to the notification.
    • forwardMessageToSlot

      public void forwardMessageToSlot(LocoNetMessage m, int i)
      Forward Slot-related LocoNet message to the slot.
      Parameters:
      m - a LocoNet message targeted at a slot
      i - the slot number to which the LocoNet message is targeted.
    • respondToAddrRequest

      protected void respondToAddrRequest(LocoNetMessage m, int i)
      A sort of slot listener which handles loco address requests
      Parameters:
      m - a LocoNet message
      i - the slot to which it is directed
    • getMoreDetailsForSlot

      protected void getMoreDetailsForSlot(LocoNetMessage m, int i)
      If it is a slot being sent COMMON, after a delay, get the new status of the slot If it is a true slot move, not dispatch or null after a delay, get the new status of the from slot, which varies by CS. the to slot should come in the reply.
      Parameters:
      m - a LocoNet message
      i - the slot to which it is directed
    • sendReadSlotDelayed

      protected void sendReadSlotDelayed(int slotNo, long delay)
      Schedule a delayed slot read.
      Parameters:
      slotNo - - the slot.
      delay - - delay in msecs.
    • programmerOpMessage

      protected void programmerOpMessage(LocoNetMessage m, int i)
      Handle LocoNet messages related to CV programming operations
      Parameters:
      m - a LocoNet message
      i - the slot toward which the message is destined
    • getSupportedModes

      Return a list of ProgrammingModes supported by this interface Types implemented here.
      Specified by:
      getSupportedModes in interface Programmer
      Specified by:
      getSupportedModes in class AbstractProgrammer
      Returns:
      a List of ProgrammingMode objects containing the supported programming modes.
    • getCanRead

      public boolean getCanRead()
      Determine whether this Programmer implementation is capable of reading decoder contents. This is entirely determined by the attached command station, not the code here, so it refers to the mCanRead member variable which is recording the known state of that.
      Specified by:
      getCanRead in interface Programmer
      Overrides:
      getCanRead in class AbstractProgrammer
      Returns:
      True if reads are possible
    • getWriteConfirmMode

      Return the write confirm mode implemented by the command station.

      Service mode always checks for DecoderReply. (The DCS240 also seems to do ReadAfterWrite, but that's not fully understood yet)

      Specified by:
      getWriteConfirmMode in interface Programmer
      Overrides:
      getWriteConfirmMode in class AbstractProgrammer
      Parameters:
      addr - This implementation ignores this parameter
      Returns:
      the supported WriteConfirmMode
    • setCommandStationType

      Set the command station type to one of the known types in the LnCommandStationType enum.
      Parameters:
      value - contains the command station type
    • setThrottledTransmitter

      public void setThrottledTransmitter(LocoNetThrottledTransmitter value, boolean m)
      Provide a ThrottledTransmitter for sending immediate packets.
      Parameters:
      value - contains a LocoNetThrottledTransmitter object
      m - contains a boolean value indicating mTurnoutNoRetry
    • getCommandStationType

      Get the command station type.
      Returns:
      an LnCommandStationType object
    • timeout

      protected void timeout()
      Internal routine to handle a timeout.
      Specified by:
      timeout in class AbstractProgrammer
    • writeCVOpsMode

      public void writeCVOpsMode(String CVname, int val, ProgListener p, int addr, boolean longAddr) throws ProgrammerException
      Write a CV via Ops Mode programming.
      Parameters:
      CVname - CV number
      val - value to write to the CV
      p - programmer
      addr - address of decoder
      longAddr - true if the address is a long address
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • writeCV

      public void writeCV(String cvNum, int val, ProgListener p) throws ProgrammerException
      Write a CV via the Service Mode programmer.
      Specified by:
      writeCV in interface Programmer
      Specified by:
      writeCV in class AbstractProgrammer
      Parameters:
      cvNum - CV id as String
      val - value to write to the CV
      p - programmer
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • doWrite

      public void doWrite(int CV, int val, ProgListener p, int pcmd) throws ProgrammerException
      Perform a write a CV via the Service Mode programmer.
      Parameters:
      CV - CV number
      val - value to write to the CV
      p - programmer
      pcmd - programming command
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • confirmCVOpsMode

      public void confirmCVOpsMode(String CVname, int val, ProgListener p, int addr, boolean longAddr) throws ProgrammerException
      Confirm a CV via the OpsMode programmer.
      Parameters:
      CVname - a String containing the CV name
      val - expected value
      p - programmer
      addr - address of loco to write to
      longAddr - true if addr is a long address
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • confirmCV

      public void confirmCV(String CVname, int val, ProgListener p) throws ProgrammerException
      Confirm a CV via the Service Mode programmer.
      Specified by:
      confirmCV in interface Programmer
      Specified by:
      confirmCV in class AbstractProgrammer
      Parameters:
      CVname - a String containing the CV name
      val - expected value
      p - programmer
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • doConfirm

      public void doConfirm(int CV, int val, ProgListener p, int pcmd) throws ProgrammerException
      Perform a confirm operation of a CV via the Service Mode programmer.
      Parameters:
      CV - the CV number
      val - expected value
      p - programmer
      pcmd - programming command
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • readCV

      public void readCV(String cvNum, ProgListener p) throws ProgrammerException
      Description copied from class: AbstractProgrammer
      Perform a CV read in the system-specific manner, and using the specified programming mode.

      Handles a general address space through a String address. Each programmer defines the acceptable formats.

      Note that this returns before the write is complete; you have to provide a ProgListener to hear about completion. For simplicity, expect the return to be on the GUI thread.

      Exceptions will only be thrown at the start, not during the actual programming sequence. A typical exception would be due to an invalid mode (though that should be prevented earlier)

      Specified by:
      readCV in interface Programmer
      Specified by:
      readCV in class AbstractProgrammer
      Parameters:
      cvNum - the CV to read
      p - the listener that will be notified of the read
      Throws:
      ProgrammerException - if unable to communicate
    • readCV

      public void readCV(String cvNum, ProgListener p, int startVal) throws ProgrammerException
      Read a CV via the OpsMode programmer.
      Specified by:
      readCV in interface Programmer
      Parameters:
      cvNum - a String containing the CV number
      p - programmer
      startVal - initial "guess" for value of CV, can improve speed if used
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • readCVOpsMode

      public void readCVOpsMode(String CVname, ProgListener p, int addr, boolean longAddr) throws ProgrammerException
      Invoked by LnOpsModeProgrammer to start an ops-mode read operation.
      Parameters:
      CVname - Which CV to read
      p - Who to notify on complete
      addr - Address of the locomotive
      longAddr - true if a long address, false if short address
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • doRead

      void doRead(int CV, ProgListener p, int progByte, int startVal) throws ProgrammerException
      Perform a CV Read.
      Parameters:
      CV - the CV number
      p - programmer
      progByte - programming command
      startVal - initial "guess" for value of CV, can improve speed if used
      Throws:
      ProgrammerException - if an unsupported programming mode is exercised
    • useProgrammer

      protected void useProgrammer(ProgListener p) throws ProgrammerException
      Throws:
      ProgrammerException
    • progTaskStart

      protected LocoNetMessage progTaskStart(int pcmd, int val, int cvnum, boolean write)
      Internal method to create the LocoNetMessage for programmer task start.
      Parameters:
      pcmd - programmer command
      val - value to be used
      cvnum - CV number
      write - true if write, else false
      Returns:
      a LocoNet message containing a programming task start operation
    • notifyProgListenerEnd

      protected void notifyProgListenerEnd(int value, int status)
      Internal method to notify of the final result.
      Parameters:
      value - The cv value to be returned
      status - The error code, if any
    • notifyProgListenerLack

      protected void notifyProgListenerLack(int status)
      Internal method to notify of the LACK result. This is a separate routine from nPLRead in case we need to handle something later.
      Parameters:
      status - The error code, if any
    • sendProgrammingReply

      protected void sendProgrammingReply(ProgListener p, int value, int status)
      Internal routine to forward a programming reply. This is delayed to prevent overruns of the command station.
      Parameters:
      p - a ProgListener object
      value - the value to return
      status - The error code, if any
    • stopEndOfProgrammingTimer

      protected void stopEndOfProgrammingTimer()
      Internal routine to stop end-of-programming timer, as another programming operation has happened.
    • restartEndOfProgrammingTimer

      protected void restartEndOfProgrammingTimer()
      Internal routine to handle timer restart if needed to restore power. This is only needed in service mode.
    • doEndOfProgramming

      protected void doEndOfProgramming()
      Internal routine to handle a programming timeout by turning power off.
    • update

      public void update(List<SlotMapEntry> inputSlotMap, int interval)
      Start the process of checking each slot for contents.

      This is not invoked by this class, but can be invoked from elsewhere to start the process of scanning all slots to update their contents. If an instance is already running then the request is ignored

      Parameters:
      inputSlotMap - array of from to pairs
      interval - ms between slt rds
    • update

      public void update()
    • sendReadSlot

      public void sendReadSlot(int slot)
      Send a message requesting the data from a particular slot.
      Parameters:
      slot - Slot number
    • readNextSlot

      protected void readNextSlot(int toSlot, int interval)
      Continue the sequence of reading all slots.
      Parameters:
      toSlot - index of the next slot to read
      interval - wait time before operation, milliseconds
    • getInUseCount

      public int getInUseCount()
      Provide a snapshot of the slots in use.

      Note that the count of "in-use" slots may be somewhat misleading, as slots in the "common" state can be controlled and are occupying a slot in a meaningful way.

      Returns:
      the count of in-use LocoNet slots
    • setSystemConnectionMemo

      Set the system connection memo.
      Parameters:
      memo - a LocoNetSystemConnectionMemo
    • getUserName

      public String getUserName()
      Get the "user name" for the slot manager connection, from the memo.
      Specified by:
      getUserName in interface CommandStation
      Returns:
      the connection's user name or "LocoNet" if the memo does not exist
    • getSystemPrefix

      Return the memo "system prefix".
      Specified by:
      getSystemPrefix in interface CommandStation
      Returns:
      the system prefix or "L" if the memo does not exist
    • setTranspondingAvailable

      public void setTranspondingAvailable(boolean val)
    • getTranspondingAvailable

      public boolean getTranspondingAvailable()
    • setLoconetProtocolAutoDetect

      public void setLoconetProtocolAutoDetect(boolean val)
      Parameters:
      val - If false then we only use protocol one.
    • getSystemConnectionMemo

      Get the memo.
      Returns:
      the memo
    • dispose

      public void dispose()
      Dispose of this by stopped it's ongoing actions
      Specified by:
      dispose in interface Disposable
      Specified by:
      dispose in interface Programmer