001package jmri.jmrix.loconet;
002
003import java.util.ArrayList;
004import java.util.Vector;
005import javax.annotation.Nonnull;
006
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010/**
011 * Abstract base class for implementations of LocoNetInterface.
012 * <p>
013 * This provides just the basic interface and some statistics support.
014 *
015 * @author Bob Jacobsen Copyright (C) 2001
016 */
017public abstract class LnTrafficController implements LocoNetInterface {
018
019    /**
020     * Reference to the system connection memo.
021     */
022    LocoNetSystemConnectionMemo memo = null;
023
024    /**
025     * Constructor without reference to a LocoNetSystemConnectionMemo.
026     */
027    public LnTrafficController() {
028        super();
029    }
030
031    /**
032     * Constructor. Gets a reference to the LocoNetSystemConnectionMemo.
033     *
034     * @param memo connection's memo
035     */
036    public LnTrafficController(LocoNetSystemConnectionMemo memo) {
037        super();
038        this.memo = memo;
039    }
040
041    /**
042     * {@inheritDoc}
043     */
044    @Override
045    public void setSystemConnectionMemo(LocoNetSystemConnectionMemo m) {
046        log.debug("LnTrafficController set memo to {}", m.getUserName());
047        memo = m;
048    }
049
050    /**
051     * {@inheritDoc}
052     */
053    @Override
054    public LocoNetSystemConnectionMemo getSystemConnectionMemo() {
055        log.debug("getSystemConnectionMemo {} called in LnTC", memo.getUserName());
056        return memo;
057    }
058
059    /**
060     * {@inheritDoc}
061     */
062    @Override
063    abstract public boolean status();
064
065    /**
066     * Forward a preformatted LocoNetMessage to the actual interface.
067     * <p>
068     * Implementations should update the transmit count statistic.
069     *
070     * @param m message to send; will be updated with CRC
071     */
072    @Override
073    abstract public void sendLocoNetMessage(LocoNetMessage m);
074
075    // The methods to implement adding and removing listeners
076
077    // relies on Vector being a synchronized class
078    protected Vector<LocoNetListener> listeners = new Vector<LocoNetListener>();
079
080    @Override
081    public synchronized void addLocoNetListener(int mask, @Nonnull LocoNetListener l) {
082        java.util.Objects.requireNonNull(l);
083        if (!listeners.contains(l)) {
084            listeners.addElement(l);
085        }
086    }
087
088    @Override
089    public synchronized void removeLocoNetListener(int mask, @Nonnull LocoNetListener l) {
090        java.util.Objects.requireNonNull(l);
091        if (listeners.contains(l)) {
092            listeners.removeElement(l);
093        }
094    }
095
096    /**
097     * Forward a LocoNetMessage to all registered listeners.
098     * <p>
099     * Needs to have public access, as
100     * {@link jmri.jmrix.loconet.loconetovertcp.LnOverTcpPacketizer} and
101     * {@link jmri.jmrix.loconet.Intellibox.IBLnPacketizer} invoke it, but don't
102     * inherit from it.
103     *
104     * @param m message to forward. Listeners should not modify it!
105     */
106    public void notify(LocoNetMessage m) {
107        // record statistics
108        receivedMsgCount++;
109        receivedByteCount += m.getNumDataElements();
110
111        // make a copy of the listener vector for notifications; synchronized not needed once copied
112        ArrayList<LocoNetListener> v;
113        synchronized (this) {
114            v = new ArrayList<LocoNetListener>(listeners);
115        }
116
117        // forward to all listeners
118        log.debug("notify of incoming LocoNet packet: {}", m);
119        for (LocoNetListener client : v) {
120            log.trace("  notify {} of incoming LocoNet packet: {}", client, m);
121            client.message(m);
122        }
123    }
124
125    /**
126     * Is there a backlog of information for the outbound link? This includes
127     * both in the program (e.g. the outbound queue) and in the Command Station
128     * interface (e.g. flow control from the port).
129     *
130     * @return true if busy, false if nothing waiting to send
131     */
132    abstract public boolean isXmtBusy();
133
134    /**
135     * Reset statistics (received message count, transmitted message count,
136     * received byte count).
137     */
138    public void resetStatistics() {
139        receivedMsgCount = 0;
140        transmittedMsgCount = 0;
141        receivedByteCount = 0;
142    }
143
144    /**
145     * Clean up any resources, particularly threads.
146     * <p>
147     * The object can't be used after this.
148     */
149    public void dispose() {}
150
151    /**
152     * Monitor the number of LocoNet messages received across the interface.
153     * This includes the messages this client has sent.
154     *
155     * @return the number of messages received
156     */
157    public int getReceivedMsgCount() {
158        return receivedMsgCount;
159    }
160    protected int receivedMsgCount = 0;
161
162    /**
163     * Monitor the number of bytes in LocoNet messages received across the
164     * interface. This includes the bytes in messages this client has sent.
165     *
166     * @return the number of bytes received
167     */
168    public int getReceivedByteCount() {
169        return receivedByteCount;
170    }
171    protected int receivedByteCount = 0;
172
173    /**
174     * Monitor the number of LocoNet messages transmitted across the interface.
175     *
176     * @return the number of messages transmitted
177     */
178    public int getTransmittedMsgCount() {
179        return transmittedMsgCount;
180    }
181    protected int transmittedMsgCount = 0;
182
183    private final static Logger log = LoggerFactory.getLogger(LnTrafficController.class);
184
185}