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", "" + 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            return;
105        }
106    }
107
108    protected Element makeParameter(String name, String value) {
109        Element p = new Element("parameter");
110        p.setAttribute("name", name);
111        p.addContent(value);
112        return p;
113    }
114
115    @Override
116    protected void getInstance() {
117        adapter = new XBeeAdapter();
118    }
119
120    @Override
121    protected void getInstance(Object object) {
122        adapter = ((ConnectionConfig) object).getAdapter();
123    }
124
125    @Override
126    protected void unpackElement(Element shared, Element perNode) {
127        List<Element> l = shared.getChildren("node");
128        // Trigger initialization of this Node to reflect these parameters
129        XBeeConnectionMemo xcm = (XBeeConnectionMemo) adapter.getSystemConnectionMemo();
130        XBeeTrafficController xtc = (XBeeTrafficController) xcm.getTrafficController();
131        for (int i = 0; i < l.size(); i++) {
132            Element n = l.get(i);
133            byte GUID[] = jmri.util.StringUtil.bytesFromHexString(findParmValue(n, "GUID"));
134            XBee64BitAddress guid = new XBee64BitAddress(GUID);
135            byte addr[] = jmri.util.StringUtil.bytesFromHexString(findParmValue(n, "address"));
136            XBee16BitAddress address = new XBee16BitAddress(addr);
137            String Identifier = findParmValue(n, "name");
138            // create the RemoteXBeeDevice for the node.
139            RemoteXBeeDevice remoteDevice = new RemoteXBeeDevice(xtc.getXBee(),
140                    guid, address, Identifier);
141            // Check to see if the node is a duplicate, if it is, move
142            // to the next one.
143            // get a XBeeNode corresponding to this node address if one exists
144            XBeeNode curNode = (XBeeNode) xtc.getNodeFromXBeeDevice(remoteDevice);
145            if (curNode != null) {
146                log.info("Read duplicate node {} from file", remoteDevice);
147                continue;
148            }
149
150            try {
151                // and then add it to the network
152                xtc.getXBee().getNetwork().addRemoteDevice(remoteDevice);
153                // create node (they register themselves)
154                XBeeNode node = new XBeeNode(remoteDevice);
155
156                String polled = findParmValue(n, "polled");
157                node.setPoll(polled.equals("yes"));
158
159                xtc.registerNode(node);
160
161                // if there is a stream port controller stored for this
162                // node, we need to load that after the node starts running.
163                // otherwise, the IOStream associated with the node has not
164                // been configured.
165                String streamController = findParmValue(n, "StreamController");
166                String streamConfig = findParmValue(n, "StreamConfig");
167                AbstractStreamPortController connectedController = null;
168                jmri.jmrix.AbstractStreamConnectionConfig connectedConfig = null;
169
170                Element connect; 
171                try {
172                   connect = n.getChildren("connection").get(0); // there should only be one connection child.
173                } catch(IndexOutOfBoundsException ioobe){
174                   connect = null;
175                }
176
177                // configure the controller.
178                if (streamController != null) {
179                    try {
180                        @SuppressWarnings("unchecked") // Class.forName cast is unchecked at this point
181                        java.lang.Class<jmri.jmrix.AbstractStreamPortController> T = (Class<AbstractStreamPortController>) Class.forName(streamController);
182                        java.lang.reflect.Constructor<?> ctor = T.getConstructor(java.io.DataInputStream.class, java.io.DataOutputStream.class, String.class);
183                        connectedController = (jmri.jmrix.AbstractStreamPortController) ctor.newInstance(node.getIOStream().getInputStream(), node.getIOStream().getOutputStream(), "XBee Node " + node.getPreferedName());
184                    } catch (java.lang.ClassNotFoundException cnfe) {
185                        log.error("Unable to find class for stream controller : {}", streamController);
186                    } catch (java.lang.InstantiationException
187                            | java.lang.NoSuchMethodException
188                            | java.lang.IllegalAccessException
189                            | java.lang.reflect.InvocationTargetException ex) {
190                        log.error("Unable to construct Stream Port Controller for node.", ex);
191                    }
192                }
193                // if streamConfig is available, set up connectedConfig.
194                if (streamConfig != null && connectedController != null) {
195                    try {
196                        @SuppressWarnings("unchecked") // Class.forName cast is unchecked at this point
197                        java.lang.Class<jmri.jmrix.AbstractStreamConnectionConfig> T = (Class<AbstractStreamConnectionConfig>) Class.forName(streamConfig);
198                        java.lang.reflect.Constructor<?> ctor = T.getConstructor(jmri.jmrix.AbstractStreamPortController.class);
199                        connectedConfig = (jmri.jmrix.AbstractStreamConnectionConfig) ctor.newInstance(connectedController);
200                    } catch (java.lang.ClassNotFoundException cnfe) {
201                        log.error("Unable to find class for stream config: {}", streamConfig);
202                    } catch (java.lang.InstantiationException
203                            | java.lang.NoSuchMethodException
204                            | java.lang.IllegalAccessException
205                            | java.lang.reflect.InvocationTargetException ex) {
206                        log.error("Unable to construct Stream Port Configuration for node.", ex);
207                    }
208                }
209                // load information from the xml file.
210                if (connect != null && connectedConfig != null) {
211                    String className = connect.getAttributeValue("class");
212
213                    try {
214                        XmlAdapter adapter = (XmlAdapter) Class.forName(className).getDeclaredConstructor().newInstance();
215                        adapter.load(connect, connectedConfig);
216                    } catch (ClassNotFoundException
217                            | InstantiationException | NoSuchMethodException | java.lang.reflect.InvocationTargetException
218                            | IllegalAccessException ex) {
219                        log.error("Unable to create {} for {}", className, shared, ex);
220                    } catch (RuntimeException | jmri.configurexml.JmriConfigureXmlException ex) {
221                        log.error("Unable to load {} into {}", shared, className, ex);
222                    }
223                }
224
225                // after loading either config or controller, connect them.
226                if (connectedConfig != null) {
227                    node.connectPortController(connectedConfig);
228                } else if (connectedController != null) {
229                    // fallback for connections created with a script
230                    node.connectPortController(connectedController);
231                }
232                log.info("loaded {} onto node {}", node.getConnectionConfig(), node);
233                log.info("manuf {} userName {} ", node.getConnectionConfig().getManufacturer(), node.getConnectionConfig().name());
234            } catch (TimeoutException toe) {
235                log.error("Timeout adding node {} from configuration file.",
236                        remoteDevice);
237            } catch (XBeeException xbe) {
238                log.error("Exception adding node {} from configuration file.",
239                        remoteDevice);
240            }
241        }
242
243    }
244
245    @Override
246    protected void register() {
247        this.register(new ConnectionConfig(adapter));
248    }
249
250    // initialize logging
251    private final static Logger log = LoggerFactory.getLogger(ConnectionConfigXml.class);
252
253}