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