001package jmri.jmrix;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004import org.slf4j.Logger;
005import org.slf4j.LoggerFactory;
006
007/**
008 * Abstract Traffic Controller base class for those implementations that track a
009 * set of nodes.
010 * <p>
011 * The nodes are descendents of {@link jmri.jmrix.AbstractNode}. Provides node
012 * management services, but no additional protocol.
013 *
014 * @author Bob Jacobsen Copyright (C) 2008
015 */
016public abstract class AbstractMRNodeTrafficController extends AbstractMRTrafficController {
017
018    /**
019     * Create a new unnamed MRNodeTrafficController instance.
020     */
021    public AbstractMRNodeTrafficController() {
022    }
023
024    /**
025     * Initialize based on number of first and last nodes.
026     * @param minNode lowest node number, usually 0
027     * @param maxNode highest node number
028     */
029    protected void init(int minNode, int maxNode) {
030        this.minNode = minNode;
031        this.maxNode = maxNode;
032
033        nodeArray = new AbstractNode[this.maxNode + 1]; // numbering from 0
034        mustInit = new boolean[this.maxNode + 1];
035
036        // initialize content
037        for (int i = 0; i <= this.maxNode; i++) {
038            mustInit[i] = true;
039        }
040    }
041
042    protected int minNode = -1;
043    protected int maxNode = -1;
044
045    /**
046     * Total number of SerialNodes registered with this TrafficController.
047     * Incremented as Serial Nodes are created and registered.
048     * Corresponds to the next available address in {@link #nodeArray}.
049     */
050    protected volatile int numNodes = 0;
051    protected AbstractNode[] nodeArray;
052    private boolean[] mustInit;
053
054    /**
055     * Does a given node need to have initialization data sent?
056     *
057     * @param i the node address (number)
058     * @return true if initialization data is required
059     */
060    protected boolean getMustInit(int i) {
061        return mustInit[i];
062    }
063
064    /**
065     * Mark whether a given node needs to have initialization data sent.
066     *
067     * @param i the node index
068     * @param v true if set to require sending initialization data
069     */
070    protected void setMustInit(int i, boolean v) {
071        mustInit[i] = v;
072    }
073
074    protected void setMustInit(AbstractNode node, boolean v) {
075        for (int i = 0; i < numNodes; i++) {
076            if (nodeArray[i] == node) {
077                // found node - set up for initialization
078                mustInit[i] = v;
079                return;
080            }
081        }
082    }
083
084    /**
085     * Get the total number of currently registered nodes.
086     *
087     * @return the number of registerd nodes on this connection
088     */
089    public int getNumNodes() {
090        return numNodes;
091    }
092
093    /**
094     * Register a Serial node on this TrafficController.
095     *
096     * @param node the node object to register
097     */
098    @SuppressFBWarnings(value = "VO_VOLATILE_INCREMENT", justification = "Method itself is synchronized to protect numNodes")
099    public void registerNode(AbstractNode node) {
100        synchronized (this) {
101            // no validity checking because at this point the node may not be fully defined
102            nodeArray[numNodes] = node;
103            mustInit[numNodes] = true;
104            numNodes++;
105        }
106    }
107
108    /**
109     * Get the Serial node for a given index as registered with this
110     * TrafficController.
111     *
112     * @param index the index number of the node. To cycle through all nodes,
113     *          begin with index=0, and increment your index at each call.
114     * @return the node at index, 'null' when index exceeds the number of defined nodes
115     */
116    public synchronized AbstractNode getNode(int index) {
117        if (index >= numNodes) {
118            return null;
119        }
120        return nodeArray[index];
121    }
122
123    /**
124     * Identify a SerialNode from its node address.
125     *
126     * @param addr the node address, numbered from 0
127     * @return the node at node address, 'null' if a SerialNode with the
128     * specified address was not found
129     */
130    synchronized public AbstractNode getNodeFromAddress(int addr) {
131        for (int i = 0; i < numNodes; i++) {
132            if (getNode(i).getNodeAddress() == addr) {
133                return (getNode(i));
134            }
135        }
136        return (null);
137    }
138
139    /**
140     * Working variable for keeping track of the active node, if any.
141     */
142    protected int curSerialNodeIndex = 0;
143
144    /**
145     * Delete a SerialNode by node address.
146     *
147     * @param nodeAddress address number for the node to be deleted
148     */
149    @SuppressFBWarnings(value = "VO_VOLATILE_INCREMENT", justification = "Method itself is synchronized to protect numNodes")
150    public synchronized void deleteNode(int nodeAddress) {
151        // find the serial node
152        int index = 0;
153        for (int i = 0; i < numNodes; i++) {
154            if (nodeArray[i].getNodeAddress() == nodeAddress) {
155                index = i;
156            }
157        }
158        if (index == curSerialNodeIndex) {
159            log.warn("Deleting the serial node active in the polling loop");
160            // just a warning, unlikely event and probably OK in any case.
161        }
162        // delete the node from the node list
163        numNodes--;
164        if (index < numNodes) {
165            // did not delete the last node, shift
166            for (int j = index; j < numNodes; j++) {
167                nodeArray[j] = nodeArray[j + 1];
168            }
169        }
170        nodeArray[numNodes] = null;
171    }
172
173    private final static Logger log = LoggerFactory.getLogger(AbstractMRNodeTrafficController.class);
174
175}