Class SlotManager

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

    public class SlotManager
    extends AbstractProgrammer
    implements LocoNetListener, CommandStation
    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.

    • Constructor Detail

      • SlotManager

        public SlotManager​(LnTrafficController tc)
        Constructor for a SlotManager on a given TrafficController.
        Parameters:
        tc - Traffic Controller to be used by SlotManager for communication with LocoNet
    • Method Detail

      • 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

        public java.lang.String 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
      • 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

        public void removeSlotListener​(SlotListener l)
        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
      • getDirectFunctionAddress

        int getDirectFunctionAddress​(LocoNetMessage m)
        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

        int getDirectDccPacket​(LocoNetMessage m)
        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

        boolean isExtFunctionMessage​(LocoNetMessage m)
        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

        public int findSlotFromMessage​(LocoNetMessage m)
        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.
        Parameters:
        m - LocoNet message being analyzed
      • notifyProgListenerEndAfterDelay

        protected void 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
      • 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
      • 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
      • writeCVOpsMode

        public void writeCVOpsMode​(java.lang.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
      • 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​(java.lang.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
      • 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​(java.lang.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​(java.lang.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​(java.lang.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
      • 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​(java.util.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
      • getUserName

        public java.lang.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
      • setLoconetProtocolAutoDetect

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