001package jmri.jmrix.ieee802154;
002
003import jmri.jmrix.AbstractMRListener;
004import jmri.jmrix.AbstractMRMessage;
005import jmri.jmrix.AbstractMRNodeTrafficController;
006import jmri.jmrix.AbstractMRReply;
007import jmri.jmrix.AbstractNode;
008import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012/**
013 * Converts Stream-based I/O to/from messages. The "IEEE802154Interface" side
014 * sends/receives message objects.
015 * <p>
016 * The connection to a IEEE802154PortController is via a pair of *Streams, which
017 * then carry sequences of characters for transmission. Note that this
018 * processing is handled in an independent thread.
019 * <p>
020 * This maintains a list of nodes, but doesn't currently do anything with it.
021 * This implementation is complete and can be instantiated, but is not
022 * functional. It will be created e.g. when a default object is needed for
023 * configuring nodes, etc, during the initial configuration. A subclass must be
024 * instantiated to actually communicate with an adapter.
025 *
026 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2005, 2006, 2008 Converted to multiple connection
027 * @author kcameron Copyright (C) 2011
028 * @author Paul Bender Copyright (C) 2013
029 */
030abstract public class IEEE802154TrafficController extends AbstractMRNodeTrafficController implements IEEE802154Interface {
031
032    /**
033     * Create a new IEEE802154TrafficController instance.
034     */
035    public IEEE802154TrafficController() {
036        super();
037        logDebug = log.isDebugEnabled();
038
039        init(1, 100);
040
041        // not polled at all, so allow unexpected messages, and
042        // use poll delay just to spread out startup
043        setAllowUnexpectedReply(true);
044        mWaitBeforePoll = 1000;  // can take a long time to send
045    }
046
047    /**
048     * Get a message of a specific length for filling in.
049     * <p>
050     * This is a default, null implementation, which must be overridden in an
051     * adapter-specific subclass.
052     *
053     * @param length length for new message
054     * @return null since this method should be over-ridden
055     */
056    public IEEE802154Message getIEEE802154Message(int length) {
057        return null;
058    }
059
060    // have several debug statements in tight loops, e.g. every character;
061    // only want to check once
062    protected boolean logDebug = false;
063
064    // The methods to implement the IEEE802154Interface
065
066    @Override
067    public synchronized void addIEEE802154Listener(IEEE802154Listener l) {
068        this.addListener(l);
069    }
070
071    @Override
072    public synchronized void removeIEEE802154Listener(IEEE802154Listener l) {
073        this.removeListener(l);
074    }
075
076    @Override
077    protected int enterProgModeDelayTime() {
078        // we should to wait at least a second after enabling the programming track
079        return 1000;
080    }
081
082    /**
083     * Forward a IEEE802154Message to all registered IEEE802154Interface
084     * listeners.
085     */
086    @Override
087    protected void forwardMessage(AbstractMRListener client, AbstractMRMessage m) {
088        ((IEEE802154Listener) client).message((IEEE802154Message) m);
089    }
090
091    /**
092     * Forward a reply to all registered IEEE802154Interface listeners.
093     */
094    @Override
095    protected void forwardReply(AbstractMRListener client, AbstractMRReply r) {
096        ((IEEE802154Listener) client).reply((IEEE802154Reply) r);
097    }
098
099    /**
100     * Eventually, do initialization if needed
101     */
102    @Override
103    protected AbstractMRMessage pollMessage() {
104        return null;
105    }
106
107    @Override
108    protected AbstractMRListener pollReplyHandler() {
109        return null;
110    }
111
112    /**
113     * Forward a preformatted message to the actual interface.
114     */
115    @Override
116    public void sendIEEE802154Message(IEEE802154Message m, IEEE802154Listener reply) {
117        sendMessage(m, reply);
118    }
119
120    @Override
121    protected void forwardToPort(AbstractMRMessage m, AbstractMRListener reply) {
122        if (logDebug) {
123            log.debug("forward {}", m);
124        }
125        super.forwardToPort(m, reply);
126    }
127
128    @Override
129    protected AbstractMRMessage enterProgMode() {
130        return null;
131    }
132
133    @Override
134    protected AbstractMRMessage enterNormalMode() {
135        return null;
136    }
137
138    public void setAdapterMemo(IEEE802154SystemConnectionMemo adaptermemo) {
139        memo = adaptermemo;
140    }
141
142    public IEEE802154SystemConnectionMemo getAdapterMemo() {
143        return memo;
144    }
145
146    private IEEE802154SystemConnectionMemo memo = null;
147
148    /**
149     * IEEE 802.15.4 messages start with a 0x7E delimiter byte.
150     */
151    @Override
152    protected void waitForStartOfReply(java.io.DataInputStream istream) throws java.io.IOException {
153        // loop looking for the start character
154        while (readByteProtected(istream) != 0x7E) {
155        }
156    }
157
158    /**
159     * <p>
160     * The length is the first byte of the message payload, The end of the
161     * message occurs when the length of the message is equal to the payload
162     * length.
163     * <p>
164     * NOTE: This function does not work with XBee nodes, which provide a
165     * modified version of the packets sent via the radio.
166     */
167    @Override
168    protected boolean endOfMessage(AbstractMRReply msg) {
169        return ((msg.getElement(0) + 2) == msg.getNumDataElements());
170    }
171
172    /**
173     * Add trailer to the outgoing byte stream. This version adds the checksum
174     * to the end of the message.
175     *
176     * @param msg    The output byte stream
177     * @param offset the first byte not yet used
178     */
179    @Override
180    protected void addTrailerToOutput(byte[] msg, int offset, jmri.jmrix.AbstractMRMessage m) {
181        if (m.getNumDataElements() == 0) {
182            return;
183        }
184        ((IEEE802154Message) m).setParity();
185    }
186
187    /**
188     * Add header to the outgoing byte stream.
189     *
190     * @param msg The output byte stream
191     * @return next location in the stream to fill
192     */
193    //protected int addHeaderToOutput(byte[] msg, AbstractMRMessage m) {
194    //    m.setElement(0)=0x7E;
195    //    return 1;
196    //}
197
198    /**
199     * <p>
200     * This is a default, null implementation, which must be overridden in an
201     * adapter-specific subclass.
202     */
203    //protected AbstractMRReply newReply() {return new IEEE802154Reply();}
204
205    /**
206     * Build a new IEEE802154 Node.
207     * Must be implemented by derived classes
208     * @return new IEEE802154Node.
209     */
210    abstract public IEEE802154Node newNode();
211
212    /**
213     * Identify a SerialNode from its node address.
214     *
215     * @param addr hex string for address, numbered from 0
216     * @return serial node id, or 'niull' if a SerialNode with the specified
217     * address was not found
218     */
219    synchronized public AbstractNode getNodeFromAddress(String addr) {
220        log.debug("String getNodeFromAddress called with {}", addr);
221        byte ba[] = jmri.util.StringUtil.bytesFromHexString(addr);
222        return getNodeFromAddress(ba);
223    }
224
225    /**
226     * Identify a SerialNode from its node address Note: 'addr'
227     * is the node address.
228     *
229     * @param ia int array of node address, numbered from 0
230     * @return serial address, or 'null' if a SerialNode with the specified
231     * address was not found
232     */
233    synchronized public AbstractNode getNodeFromAddress(int ia[]) {
234        if(logDebug) {
235           String s="";
236           for( int i=0;i<ia.length;i++) {
237               s=jmri.util.StringUtil.appendTwoHexFromInt(ia[i],s);
238           }
239           log.debug("int array getNodeFromAddress called with {}", s);
240        }
241        byte ba[] = new byte[ia.length];
242        for (int i = 0; i < ia.length; i++) {
243            ba[i] = (byte) (ia[i] & 0xff);
244        }
245        return getNodeFromAddress(ba);
246    }
247
248    /**
249     * Identify a SerialNode from its node address.
250     *
251     * @param ba array of bytes in hex address
252     * @return serial node id, or 'null' if a SerialNode with the specified
253     * address was not found
254     */
255    synchronized public AbstractNode getNodeFromAddress(byte ba[]) {
256        log.debug("byte array getNodeFromAddress called with {}",
257                jmri.util.StringUtil.hexStringFromBytes(ba));
258        for (int i = 0; i < numNodes; i++) {
259            byte bsa[] = ((IEEE802154Node) getNode(i)).getUserAddress();
260            byte bga[] = ((IEEE802154Node) getNode(i)).getGlobalAddress();
261            if (bsa.length == ba.length) {
262                int j = 0;
263                for (; j < bsa.length; j++) {
264                    if (bsa[j] != ba[j]) {
265                        break;
266                    }
267                }
268                if (j == bsa.length) {
269                    return (getNode(i));
270                }
271            } else if (bga.length == ba.length) {
272                int j = 0;
273                for (; j < bga.length; j++) {
274                    if (bga[j] != ba[j]) {
275                        break;
276                    }
277                }
278                if (j == bga.length) {
279                    return (getNode(i));
280                }
281            }
282        }
283        return (null);
284    }
285
286    /**
287     * Delete a node by the string representation of its address.
288     *
289     * @param nodeAddress text of hex node address
290     */
291    @SuppressFBWarnings(value="VO_VOLATILE_INCREMENT", justification="synchronized method provides locking")
292    public synchronized void deleteNode(String nodeAddress) {
293        // find the serial node
294        int index = 0;
295        for (int i = 0; i < numNodes; i++) {
296            if (nodeArray[i] == getNodeFromAddress(nodeAddress)) {
297                index = i;
298            }
299        }
300        if (index == curSerialNodeIndex) {
301            log.warn("Deleting the serial node active in the polling loop");
302        }
303        // Delete the node from the node list
304        numNodes--;
305        if (index < numNodes) {
306            // did not delete the last node, shift
307            for (int j = index; j < numNodes; j++) {
308                nodeArray[j] = nodeArray[j + 1];
309            }
310        }
311        nodeArray[numNodes] = null;
312    }
313
314    private final static Logger log = LoggerFactory.getLogger(IEEE802154TrafficController.class);
315
316}