001package jmri.jmrix.ieee802154.xbee.configurexml;
002
003import com.digi.xbee.api.RemoteXBeeDevice;
004import com.digi.xbee.api.exceptions.TimeoutException;
005import com.digi.xbee.api.exceptions.XBeeException;
006import com.digi.xbee.api.models.XBee16BitAddress;
007import com.digi.xbee.api.models.XBee64BitAddress;
008import java.util.List;
009import jmri.configurexml.ConfigXmlManager;
010import jmri.configurexml.XmlAdapter;
011import jmri.jmrix.AbstractStreamPortController;
012import jmri.jmrix.AbstractStreamConnectionConfig;
013import jmri.jmrix.configurexml.AbstractSerialConnectionConfigXml;
014import jmri.jmrix.ieee802154.xbee.ConnectionConfig;
015import jmri.jmrix.ieee802154.xbee.XBeeAdapter;
016import jmri.jmrix.ieee802154.xbee.XBeeConnectionMemo;
017import jmri.jmrix.ieee802154.xbee.XBeeNode;
018import jmri.jmrix.ieee802154.xbee.XBeeTrafficController;
019import org.jdom2.Element;
020import org.slf4j.Logger;
021import org.slf4j.LoggerFactory;
022
023/**
024 * Handle XML persistance of layout connections by persisting the XBeeAdapter
025 * (and connections). Note this is named as the XML version of a
026 * ConnectionConfig object, but it's actually persisting the XBeeAdapter.
027 * <p>
028 * This class is invoked from jmrix.JmrixConfigPaneXml on write, as that class
029 * is the one actually registered. Reads are brought here directly via the class
030 * attribute in the XML.
031 *
032 * @author Bob Jacobsen Copyright: Copyright (c) 2003, 2006, 2007, 2008
033 */
034public class ConnectionConfigXml extends AbstractSerialConnectionConfigXml {
035
036    public ConnectionConfigXml() {
037        super();
038    }
039
040    /**
041     * Write out the SerialNode objects too
042     *
043     * @param e Element being extended
044     */
045    @Override
046    protected void extendElement(Element e) {
047        XBeeConnectionMemo xcm;
048        XBeeTrafficController xtc;
049        try {
050            xcm = (XBeeConnectionMemo) adapter.getSystemConnectionMemo();
051            xtc = (XBeeTrafficController) xcm.getTrafficController();
052        } catch (NullPointerException npe) {
053            // The adapter doesn't have a memo, so no nodes can be defined.
054            if (log.isDebugEnabled()) {
055                log.debug("No memo defined; no nodes to save.");
056            }
057            return;
058        }
059        try {
060            XBeeNode node = (XBeeNode) xtc.getNode(0);
061            int index = 1;
062            while (node != null) {
063                // add node as an element
064                Element n = new Element("node");
065                n.setAttribute("name", Integer.toString(node.getNodeAddress()));
066                e.addContent(n);
067                // add parameters to the node as needed
068                n.addContent(makeParameter("address",
069                        jmri.util.StringUtil.hexStringFromBytes(node.getUserAddress())));
070                n.addContent(makeParameter("PAN",
071                        jmri.util.StringUtil.hexStringFromBytes(node.getPANAddress())));
072                n.addContent(makeParameter("GUID",
073                        jmri.util.StringUtil.hexStringFromBytes(node.getGlobalAddress())));
074                n.addContent(makeParameter("name", node.getIdentifier()));
075                n.addContent(makeParameter("polled", node.getPoll() ? "yes" : "no"));
076
077                jmri.jmrix.AbstractStreamPortController pc = null;
078                if ((pc = node.getPortController()) != null) {
079                    n.addContent(makeParameter("StreamController",
080                            pc.getClass().getName()));
081                }
082
083                jmri.jmrix.AbstractStreamConnectionConfig cf = null;
084                if ((cf = node.getConnectionConfig()) != null) {
085                    n.addContent(makeParameter("StreamConfig",
086                            cf.getClass().getName()));
087                    String adapter = ConfigXmlManager.adapterName(cf);
088                    log.debug("forward to {}", adapter);
089                    try {
090                        XmlAdapter x = (XmlAdapter) Class.forName(adapter).getDeclaredConstructor().newInstance();
091                        n.addContent(x.store(cf));
092                    } catch (ClassNotFoundException | IllegalAccessException
093                            | InstantiationException | NoSuchMethodException | java.lang.reflect.InvocationTargetException ex) {
094                        log.error("Exception: ", ex);
095                    }
096                }
097
098                // look for the next node
099                node = (XBeeNode) xtc.getNode(index);
100                index++;
101            }
102        } catch (java.lang.NullPointerException npe2) {
103            // no nodes defined.
104        }
105    }
106
107    protected Element makeParameter(String name, String value) {
108        Element p = new Element("parameter");
109        p.setAttribute("name", name);
110        p.addContent(value);
111        return p;
112    }
113
114    @Override
115    protected void getInstance() {
116        adapter = new XBeeAdapter();
117    }
118
119    @Override
120    protected void getInstance(Object object) {
121        adapter = ((ConnectionConfig) object).getAdapter();
122    }
123
124    @Override
125    protected void unpackElement(Element shared, Element perNode) {
126        List<Element> l = shared.getChildren("node");
127        // Trigger initialization of this Node to reflect these parameters
128        XBeeConnectionMemo xcm = (XBeeConnectionMemo) adapter.getSystemConnectionMemo();
129        XBeeTrafficController xtc = (XBeeTrafficController) xcm.getTrafficController();
130        for (Element n : l) {
131            byte[] GUID = jmri.util.StringUtil.bytesFromHexString(findParmValue(n, "GUID"));
132            XBee64BitAddress guid = new XBee64BitAddress(GUID);
133            byte[] addr = jmri.util.StringUtil.bytesFromHexString(findParmValue(n, "address"));
134            XBee16BitAddress address = new XBee16BitAddress(addr);
135            String Identifier = findParmValue(n, "name");
136            // create the RemoteXBeeDevice for the node.
137            RemoteXBeeDevice remoteDevice = new RemoteXBeeDevice(xtc.getXBee(), guid, address, Identifier);
138            // Check to see if the node is a duplicate, if it is, move
139            // to the next one.
140            // get a XBeeNode corresponding to this node address if one exists
141            XBeeNode curNode = (XBeeNode) xtc.getNodeFromXBeeDevice(remoteDevice);
142            if (curNode != null) {
143                log.info("Read duplicate node {} from file", remoteDevice);
144                continue;
145            }
146
147            try {
148                // and then add it to the network
149                xtc.getXBee().getNetwork().addRemoteDevice(remoteDevice);
150                // create node (they register themselves)
151                XBeeNode node = new XBeeNode(remoteDevice);
152
153                String polled = findParmValue(n, "polled");
154                node.setPoll(polled.equals("yes"));
155
156                xtc.registerNode(node);
157
158                // if there is a stream port controller stored for this
159                // node, we need to load that after the node starts running.
160                // otherwise, the IOStream associated with the node has not
161                // been configured.
162                String streamController = findParmValue(n, "StreamController");
163                String streamConfig = findParmValue(n, "StreamConfig");
164                AbstractStreamPortController connectedController = null;
165                AbstractStreamConnectionConfig connectedConfig = null;
166
167                Element connect;
168                try {
169                    connect = n.getChildren("connection").get(0); // there should only be one connection child.
170                } catch (IndexOutOfBoundsException ioobe) {
171                    connect = null;
172                }
173
174                // configure the controller.
175                if (streamController != null) {
176                    try {
177                        @SuppressWarnings("unchecked") // Class.forName cast is unchecked at this point
178                        Class<AbstractStreamPortController> T = (Class<AbstractStreamPortController>) Class.forName(streamController);
179                        java.lang.reflect.Constructor<?> ctor = T.getConstructor(java.io.DataInputStream.class, java.io.DataOutputStream.class, String.class);
180                        connectedController = (AbstractStreamPortController) ctor.newInstance(node.getIOStream().getInputStream(), node.getIOStream().getOutputStream(), "XBee Node " + node.getPreferedName());
181                    } catch (ClassNotFoundException cnfe) {
182                        log.error("Unable to find class for stream controller : {}", streamController);
183                    } catch (InstantiationException | NoSuchMethodException | IllegalAccessException |
184                             java.lang.reflect.InvocationTargetException ex) {
185                        log.error("Unable to construct Stream Port Controller for node.", ex);
186                    }
187                }
188                // if streamConfig is available, set up connectedConfig.
189                if (streamConfig != null && connectedController != null) {
190                    try {
191                        @SuppressWarnings("unchecked") // Class.forName cast is unchecked at this point
192                        Class<AbstractStreamConnectionConfig> T = (Class<AbstractStreamConnectionConfig>) Class.forName(streamConfig);
193                        java.lang.reflect.Constructor<?> ctor = T.getConstructor(AbstractStreamPortController.class);
194                        connectedConfig = (AbstractStreamConnectionConfig) ctor.newInstance(connectedController);
195                    } catch (ClassNotFoundException cnfe) {
196                        log.error("Unable to find class for stream config: {}", streamConfig);
197                    } catch (InstantiationException | NoSuchMethodException | IllegalAccessException |
198                             java.lang.reflect.InvocationTargetException ex) {
199                        log.error("Unable to construct Stream Port Configuration for node.", ex);
200                    }
201                }
202                // load information from the xml file.
203                if (connect != null && connectedConfig != null) {
204                    String className = connect.getAttributeValue("class");
205
206                    try {
207                        XmlAdapter adapter = (XmlAdapter) Class.forName(className).getDeclaredConstructor().newInstance();
208                        adapter.load(connect, connectedConfig);
209                    } catch (ClassNotFoundException | InstantiationException | NoSuchMethodException |
210                             java.lang.reflect.InvocationTargetException | IllegalAccessException ex) {
211                        log.error("Unable to create {} for {}", className, shared, ex);
212                    } catch (RuntimeException | jmri.configurexml.JmriConfigureXmlException ex) {
213                        log.error("Unable to load {} into {}", shared, className, ex);
214                    }
215                }
216
217                // after loading either config or controller, connect them.
218                if (connectedConfig != null) {
219                    node.connectPortController(connectedConfig);
220                } else if (connectedController != null) {
221                    // fallback for connections created with a script
222                    node.connectPortController(connectedController);
223                }
224                if (node.getConnectionConfig() != null) {
225                    // connection config is optional. don't assume it loaded from the file.
226                    log.info("loaded {} onto node {}", node.getConnectionConfig(), node);
227                    log.info("manuf {} userName {} ", node.getConnectionConfig().getManufacturer(), node.getConnectionConfig().name());
228                }
229            } catch (TimeoutException toe) {
230                log.error("Timeout adding node {} from configuration file.", remoteDevice);
231            } catch (XBeeException xbe) {
232                log.error("Exception adding node {} from configuration file.", remoteDevice);
233            }
234        }
235
236    }
237
238    @Override
239    protected void register() {
240        this.register(new ConnectionConfig(adapter));
241    }
242
243    // initialize logging
244    private final static Logger log = LoggerFactory.getLogger(ConnectionConfigXml.class);
245
246}