001package jmri.managers.configurexml;
002
003import java.util.List;
004import java.util.SortedSet;
005import jmri.InstanceManager;
006import jmri.Sensor;
007import jmri.SensorManager;
008import jmri.configurexml.JmriConfigureXmlException;
009import org.jdom2.Element;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013/**
014 * Provides the abstract base and store functionality for configuring
015 * SensorManagers, working with AbstractSensorManagers.
016 * <p>
017 * Typically, a subclass will just implement the load(Element sensors) class,
018 * relying on implementation here to load the individual sensors. Note that
019 * these are stored explicitly, so the resolution mechanism doesn't need to see
020 * *Xml classes for each specific Sensor or AbstractSensor subclass at store
021 * time.
022 *
023 * @author Bob Jacobsen Copyright: Copyright (c) 2002, 2008
024 */
025public abstract class AbstractSensorManagerConfigXML extends AbstractNamedBeanManagerConfigXML {
026
027    public AbstractSensorManagerConfigXML() {
028    }
029
030    /**
031     * Default implementation for storing the contents of a SensorManager.
032     *
033     * @param o Object to store, of type SensorManager
034     * @return Element containing the complete info
035     */
036    @Override
037    public Element store(Object o) {
038        Element sensors = new Element("sensors");
039        return store(o, sensors);
040    }
041
042    public Element store(Object o, Element sensors) {
043        setStoreElementClass(sensors);
044        SensorManager sm = (SensorManager) o;
045        if (sm.getDefaultSensorDebounceGoingActive() > 0 || sm.getDefaultSensorDebounceGoingInActive() > 0) {
046            Element elem = new Element("globalDebounceTimers");
047            elem.addContent(new Element("goingActive").addContent(String.valueOf(sm.getDefaultSensorDebounceGoingActive())));
048            elem.addContent(new Element("goingInActive").addContent(String.valueOf(sm.getDefaultSensorDebounceGoingInActive())));
049            sensors.addContent(elem);
050        }
051        SortedSet<Sensor> sensorList = sm.getNamedBeanSet();
052        // don't return an element if there are no sensors to include
053        if (sensorList.isEmpty()) {
054            return null;
055        }
056        // store the sensors
057        for (Sensor s : sensorList) {
058            String sName = s.getSystemName();
059            log.debug("system name is {}", sName);
060            String inverted = (s.getInverted() ? "true" : "false");
061
062            Element elem = new Element("sensor").setAttribute("inverted", inverted);
063            elem.addContent(new Element("systemName").addContent(sName));
064
065            // store common part
066            storeCommon(s, elem);
067
068            log.debug("store Sensor {}", sName);
069            if (s.getUseDefaultTimerSettings()) {
070                elem.addContent(new Element("useGlobalDebounceTimer").addContent("yes"));
071            } else {
072                if (s.getSensorDebounceGoingActiveTimer() > 0 || s.getSensorDebounceGoingInActiveTimer() > 0) {
073                    Element timer = new Element("debounceTimers");
074                    timer.addContent(new Element("goingActive").addContent(String.valueOf(s.getSensorDebounceGoingActiveTimer())));
075                    timer.addContent(new Element("goingInActive").addContent(String.valueOf(s.getSensorDebounceGoingInActiveTimer())));
076                    elem.addContent(timer);
077                }
078            }
079            if (sm.isPullResistanceConfigurable()) {
080                // store the sensor's value for pull resistance.
081                elem.addContent(new Element("pullResistance").addContent(s.getPullResistance().getShortName()));
082            }
083
084            sensors.addContent(elem);
085        }
086        return sensors;
087    }
088
089    /**
090     * Subclass provides implementation to create the correct top element,
091     * including the type information. Default implementation is to use the
092     * local class here.
093     *
094     * @param sensors The top-level element being created
095     */
096    abstract public void setStoreElementClass(Element sensors);
097
098    /**
099     * Create a SensorManager object of the correct class, then register and
100     * fill it.
101     *
102     * @param sharedSensors  Shared top level Element to unpack.
103     * @param perNodeSensors Per-node top level Element to unpack.
104     * @return true if successful
105     * @throws jmri.configurexml.JmriConfigureXmlException if error during load
106     */
107    @Override
108    abstract public boolean load(Element sharedSensors, Element perNodeSensors) throws JmriConfigureXmlException;
109
110    /**
111     * Utility method to load the individual Sensor objects. If there's no
112     * additional info needed for a specific sensor type, invoke this with the
113     * parent of the set of Sensor elements.
114     *
115     * @param sensors Element containing the Sensor elements to load.
116     * @return true if succeeded.
117     * @throws JmriConfigureXmlException on error.
118     */
119    public boolean loadSensors(Element sensors) throws jmri.configurexml.JmriConfigureXmlException {
120        boolean result = true;
121        List<Element> sensorList = sensors.getChildren("sensor");
122        log.debug("Found {} sensors", sensorList.size());
123        SensorManager tm = InstanceManager.sensorManagerInstance();
124        tm.setPropertyChangesSilenced("beans", true);
125
126        if (sensors.getChild("globalDebounceTimers") != null) {
127            Element timer = sensors.getChild("globalDebounceTimers");
128            try {
129                if (timer.getChild("goingActive") != null) {
130                    String active = timer.getChild("goingActive").getText();
131                    long goingActive = Long.parseLong(active);
132                    tm.setDefaultSensorDebounceGoingActive(goingActive);
133                }
134            } catch (NumberFormatException ex) {
135                log.error("Could not set DefaultSensor Debounce GoingActive : {}", ex.getMessage() );
136            }
137
138            try {
139                if (timer.getChild("goingInActive") != null) {
140                    String inActive = timer.getChild("goingInActive").getText();
141                    long goingInActive = Long.parseLong(inActive);
142                    tm.setDefaultSensorDebounceGoingInActive(goingInActive);
143                }
144            } catch (NumberFormatException ex) {
145                log.error("Could not set DefaultSensor Debounce GoingInActive : {}", ex.getMessage() );
146            }
147        }
148
149        for (Element sen : sensorList) {
150            String sysName = getSystemName(sen);
151            if (sysName == null) {
152                handleException("Unexpected missing system name while loading sensors",
153                        null, null, null, null);
154                result = false;
155                break;
156            }
157            boolean inverted = false;
158
159            String userName = getUserName(sen);
160
161            checkNameNormalization(sysName, userName, tm);
162
163            if (sen.getAttribute("inverted") != null) {
164                if (sen.getAttribute("inverted").getValue().equals("true")) {
165                    inverted = true;
166                }
167            }
168
169            log.debug("create sensor: ({})", sysName);
170
171            Sensor s;
172
173            try {
174                s = tm.newSensor(sysName, userName);
175            } catch (IllegalArgumentException e) {
176                handleException("Could not create sensor", null, sysName, userName, null);
177                result = false;
178                continue;
179            }
180
181            // load common parts
182            loadCommon(s, sen);
183
184            if (sen.getChild("debounceTimers") != null) {
185                Element timer = sen.getChild("debounceTimers");
186                try {
187                    if (timer.getChild("goingActive") != null) {
188                        String active = timer.getChild("goingActive").getText();
189                        s.setSensorDebounceGoingActiveTimer(Long.parseLong(active));
190                    }
191                } catch (NumberFormatException ex) {
192                    log.error("Could not set Sensor {} Debounce GoingActive : {}", s, ex.getMessage() );
193                }
194
195                try {
196                    if (timer.getChild("goingInActive") != null) {
197                        String inActive = timer.getChild("goingInActive").getText();
198                        s.setSensorDebounceGoingInActiveTimer(Long.parseLong(inActive));
199                    }
200                } catch (NumberFormatException ex) {
201                    log.error("Could not set Sensor {} Debounce GoingInActive : {}", s, ex.getMessage() );
202                }
203            }
204
205            if (sen.getChild("useGlobalDebounceTimer") != null) {
206                if (sen.getChild("useGlobalDebounceTimer").getText().equals("yes")) {
207                    s.setUseDefaultTimerSettings(true);
208                }
209            }
210            s.setInverted(inverted);
211
212            if (sen.getChild("pullResistance") != null) {
213                String pull = sen.getChild("pullResistance")
214                        .getText();
215                log.debug("setting pull to {} for sensor {}", pull, s);
216                s.setPullResistance(jmri.Sensor.PullResistance.getByShortName(pull));
217            }
218        }
219        tm.setPropertyChangesSilenced("beans", false);
220        return result;
221    }
222
223    @Override
224    public int loadOrder() {
225        return InstanceManager.sensorManagerInstance().getXMLOrder();
226    }
227
228    private final static Logger log = LoggerFactory.getLogger(AbstractSensorManagerConfigXML.class);
229
230}