001package jmri.jmrit.logix.configurexml;
002
003import java.util.List;
004import java.util.SortedSet;
005
006import jmri.DccLocoAddress;
007import jmri.InstanceManager;
008import jmri.SpeedStepMode;
009import jmri.jmrit.logix.BlockOrder;
010import jmri.jmrit.logix.OBlock;
011import jmri.jmrit.logix.SCWarrant;
012import jmri.jmrit.logix.SpeedUtil;
013import jmri.jmrit.logix.ThrottleSetting;
014import jmri.jmrit.logix.ThrottleSetting.Command;
015import jmri.jmrit.logix.ThrottleSetting.CommandValue;
016import jmri.jmrit.logix.ThrottleSetting.ValueType;
017import jmri.jmrit.logix.Warrant;
018import jmri.jmrit.logix.WarrantManager;
019import org.jdom2.Attribute;
020import org.jdom2.DataConversionException;
021import org.jdom2.Element;
022
023/**
024 * Provides the abstract base and store functionality for
025 * configuring the WarrantManager.
026 * <p>
027 * Typically, a subclass will just implement the load(Element warrant)
028 * class, relying on implementation here to load the individual Warrant objects.
029 *
030 * @author Pete Cressman Copyright: Copyright (c) 2009
031 */
032public class WarrantManagerXml extends jmri.configurexml.AbstractXmlAdapter {
033
034    public WarrantManagerXml() {
035    }
036
037    /**
038     * Store the contents of a WarrantManager.
039     *
040     * @param o Object to store, of type warrantManager
041     * @return Element containing the complete info
042     */
043    @Override
044    public Element store(Object o) {
045        Element warrants = new Element("warrants");
046        warrants.setAttribute("class", "jmri.jmrit.logix.configurexml.WarrantManagerXml");
047        WarrantManager wm = (WarrantManager) o;
048        if (wm != null) {
049            SortedSet<Warrant> warrantList = wm.getNamedBeanSet();
050            // don't return an element if there are no warrants to include
051            if (warrantList.isEmpty()) {
052                return null;
053            }
054            for (Warrant warrant : warrantList) {
055                String sName = warrant.getSystemName();
056                String uName = warrant.getUserName();
057                log.debug("Warrant: sysName= {}, userName= {}", sName, uName);
058                Element elem = new Element("warrant");
059                elem.setAttribute("systemName", sName);
060                if (uName == null) {
061                    uName = "";
062                }
063                if (uName.length() > 0) {
064                    elem.setAttribute("userName", uName);
065                }
066                if (warrant instanceof SCWarrant) {
067                    elem.setAttribute("wtype", "SC");
068                    elem.setAttribute("speedFactor", "" + ((SCWarrant) warrant).getSpeedFactor());
069                    elem.setAttribute("timeToPlatform", "" + ((SCWarrant) warrant).getTimeToPlatform());
070                    elem.setAttribute("forward", ((SCWarrant) warrant).getForward() ? "true" : "false");
071                } else {
072                    elem.setAttribute("wtype", "normal");
073                }
074                String comment = warrant.getComment();
075                if (comment != null) {
076                    Element c = new Element("comment");
077                    c.addContent(comment);
078                    elem.addContent(c);
079                }
080
081                List<BlockOrder> orders = warrant.getBlockOrders();
082                if (orders == null) {
083                    log.error("Warrant {} has no Route defined. (no BlockOrders) Cannot store.", warrant.getDisplayName());
084                    continue;
085                }
086                for (BlockOrder bo : orders) {
087                    elem.addContent(storeOrder(bo, "blockOrder"));
088                }
089
090                BlockOrder viaOrder = warrant.getViaOrder();
091                if (viaOrder != null) {
092                    elem.addContent(storeOrder(viaOrder, "viaOrder"));
093                }
094                BlockOrder avoidOrder = warrant.getAvoidOrder();
095                if (avoidOrder != null) {
096                    elem.addContent(storeOrder(avoidOrder, "avoidOrder"));
097                }
098
099                List<ThrottleSetting> throttleCmds = warrant.getThrottleCommands();
100                for (ThrottleSetting ts : throttleCmds) {
101                    elem.addContent(storeThrottleSetting(ts));
102                }
103
104                elem.addContent(storeTrain(warrant, "train"));
105
106                // and put this element out
107                warrants.addContent(elem);
108            }
109        }
110        return warrants;
111    }
112
113    private static Element storeTrain(Warrant warrant, String type) {
114        Element elem = new Element(type);
115        SpeedUtil speedUtil = warrant.getSpeedUtil();
116        String str = speedUtil.getRosterId();
117        if (str==null) {
118            str = "";
119        }
120        elem.setAttribute("trainId", str);
121
122        DccLocoAddress addr = speedUtil.getDccAddress();
123        if (addr != null) {
124            elem.setAttribute("dccAddress", ""+addr.getNumber());
125            elem.setAttribute("dccType", ""+addr.getProtocol().getShortName());
126        }
127        elem.setAttribute("runBlind", warrant.getRunBlind()?"true":"false");
128        elem.setAttribute("shareRoute", warrant.getShareRoute()?"true":"false");
129        elem.setAttribute("noRamp", warrant.getNoRamp()?"true":"false");
130
131        str = warrant.getTrainName();
132        if (str==null) {
133            str = "";
134        }
135        elem.setAttribute("trainName", str);
136
137        return elem;
138    }
139
140    private static Element storeOrder(BlockOrder order, String type) {
141        Element elem = new Element(type);
142        OBlock block = order.getBlock();
143        Element blk = new Element("block");
144        blk.setAttribute("systemName", block.getSystemName());
145        String uname = block.getUserName();
146        if (uname==null) {
147            uname = "";
148        }
149        if (uname.length()>0) {
150            blk.setAttribute("userName", uname);
151        }
152        elem.addContent(blk);
153
154        String str = order.getPathName();
155        if (str == null) {
156            str = "";
157        }
158        elem.setAttribute("pathName", str);
159
160        str = order.getEntryName();
161        if (str == null) {
162            str = "";
163        }
164        elem.setAttribute("entryName", str);
165
166        str = order.getExitName();
167        if (str == null) {
168            str = "";
169        }
170        elem.setAttribute("exitName", str);
171
172        return elem;
173    }
174    private static Element storeThrottleSetting(ThrottleSetting ts) {
175        Element element = new Element("throttleSetting");
176        element.setAttribute("elapsedTime", String.valueOf(ts.getTime()));
177        String name = ts.getBeanSystemName();
178        if (name != null) {
179            element.setAttribute("beanName", name);
180        } else {
181            element.setAttribute("beanName", "");
182        }
183        element.setAttribute("trackSpeed", String.valueOf(ts.getTrackSpeed()));
184
185        Element elem = new Element("command");
186        Command cmd = ts.getCommand();
187        elem.setAttribute("commandType", String.valueOf(cmd.getIntId()));
188        elem.setAttribute("fKey", String.valueOf(ts.getKeyNum()));
189        element.addContent(elem);
190
191        elem = new Element("commandValue");
192        CommandValue cmdVal = ts.getValue();
193        elem.setAttribute("valueType", String.valueOf(cmdVal.getType().getIntId()));
194        elem.setAttribute("speedMode", cmdVal.getMode().name);
195        elem.setAttribute("floatValue", String.valueOf(cmdVal.getFloat()));
196        if (!cmdVal.getText().isEmpty()) {
197            elem.setAttribute("textValue", cmdVal.getText());
198        }
199        element.addContent(elem);
200
201        return element;
202    }
203
204    @Override
205    public boolean load(Element shared, Element perNode) {
206
207        WarrantManager manager = InstanceManager.getDefault(WarrantManager.class);
208
209        if (shared.getChildren().isEmpty()) {
210            return true;
211        }
212
213        List<Element> warrantList = shared.getChildren("warrant");
214        log.debug("Found {} Warrant objects", warrantList.size());
215        boolean previouslyLoaded = false;
216        for (Element elem : warrantList) {
217            if (elem.getAttribute("systemName") == null) {
218                log.warn("unexpected null for systemName in elem {}", elem);
219                break;
220            }
221            String sysName = null;
222            if (elem.getAttribute("systemName") != null) {
223                sysName = elem.getAttribute("systemName").getValue();
224            }
225            String userName = null;
226            if (elem.getAttribute("userName") != null) {
227                userName = elem.getAttribute("userName").getValue();
228            }
229            boolean SCWa = true;
230            log.debug("loading warrant {}", sysName);
231            Attribute wType = elem.getAttribute("wtype");
232            if (wType == null) {
233                log.debug("wtype is null for {}", sysName);
234                SCWa = false;
235            } else if (!wType.getValue().equals("SC")) {
236                log.debug("wtype is {} for {}", wType.getValue(), sysName);
237                SCWa = false;
238            }
239
240            long timeToPlatform = 500;
241            Attribute TTP = elem.getAttribute("timeToPlatform");
242            if (TTP != null) {
243                try {
244                    timeToPlatform = TTP.getLongValue();
245                } catch (DataConversionException e) {
246                    log.debug("ignoring DataConversionException (and reverting to default value): {}", e.toString());
247                }
248            }
249
250            Warrant warrant = manager.createNewWarrant(sysName, userName, SCWa, timeToPlatform);
251            if (warrant == null) {
252                log.info("Warrant \"{}\" (userName={}) previously loaded. This version not loaded.", sysName, userName);
253                previouslyLoaded = true;
254                continue;
255            }
256            previouslyLoaded = false;
257            if (SCWa && warrant instanceof SCWarrant) {
258                if (elem.getAttribute("forward") != null) {
259                    ((SCWarrant)warrant).setForward(elem.getAttribute("forward").getValue().equals("true"));
260                }
261                if (elem.getAttribute("speedFactor") != null) {
262                    try {
263                        ((SCWarrant)warrant).setSpeedFactor(elem.getAttribute("speedFactor").getFloatValue());
264                    } catch (DataConversionException e) {
265                        log.warn("error converting speed value");
266                    }
267                }
268                warrant.setNoRamp(SCWa);
269                warrant.setShareRoute(SCWa);
270            }
271            List<Element> orders = elem.getChildren("blockOrder");
272            int count = 0;
273            for (Element ord : orders) {
274                BlockOrder bo = loadBlockOrder(ord);
275                if (bo == null) {
276                    log.error("Bad BlockOrder in warrant \"{}\" elem= {}.", warrant.getDisplayName(), elem.getText());
277                } else {
278                    bo.setIndex(count++);
279                    warrant.addBlockOrder(bo);
280                }
281            }
282            String c = elem.getChildText("comment");
283            if (c != null) {
284                warrant.setComment(c);
285            }
286
287            Element order = elem.getChild("viaOrder");
288            if (order!=null) {
289                warrant.setViaOrder(loadBlockOrder(order));
290            }
291            order = elem.getChild("avoidOrder");
292            if (order!=null) {
293                warrant.setAvoidOrder(loadBlockOrder(order));
294            }
295
296            if (SCWa) {
297                boolean forward =true;
298                if (elem.getAttribute("forward") != null) {
299                    forward = elem.getAttribute("forward").getValue().equals("true");
300                }
301                if (warrant instanceof SCWarrant) {
302                    ((SCWarrant)warrant).setForward(forward);
303                }
304                warrant.setNoRamp(SCWa);
305                warrant.setShareRoute(SCWa);
306            }
307            Element train = elem.getChild("train");
308            if (train!=null) {
309                loadTrain(train, warrant);
310            }
311        }
312        if (!previouslyLoaded) {
313            // A second pass through the warrant list done to load the commands. This is done so that
314            // references made to warrants in commands are fully specified. Due to ThrottleSetting
315            // Ctor using provideWarrant to establish the referenced warrant.
316            warrantList = shared.getChildren("warrant");
317            for (Element elem : warrantList) {
318                // boolean forward = true;  // variable not used, see GitHub JMRI/JMRI Issue #5661
319                if (elem.getAttribute("systemName") == null) {
320                    break;
321                }
322                if (elem.getAttribute("systemName") != null) {
323                    String sysName = elem.getAttribute("systemName").getValue();
324                    if (sysName != null) {
325                        Warrant warrant = manager.getBySystemName(sysName);
326                        List<Element> throttleCmds;
327                        if (warrant != null) {
328                            log.debug("warrant: {}", warrant.getDisplayName());
329                            throttleCmds = elem.getChildren("throttleCommand");
330                            if (throttleCmds != null && !throttleCmds.isEmpty()) {
331                                log.debug("throttleCommand size= {}",throttleCmds.size());
332                                throttleCmds.forEach((e) -> {
333                                    warrant.addThrottleCommand(loadThrottleCommand(e, warrant));
334                                });
335                                warrant.setTrackSpeeds();
336                            } else {
337                                throttleCmds = elem.getChildren("throttleSetting");
338                                if (throttleCmds != null) {
339                                    log.debug("throttleSetting size= {}",throttleCmds.size());
340                                    throttleCmds.forEach((e) -> {
341                                        warrant.addThrottleCommand(loadThrottleSetting(e, warrant));
342                                    });
343                                }
344                            }
345                        }
346                    }
347                }
348            }
349        }
350        return true;
351    }
352
353    private static void loadTrain(Element elem, Warrant warrant) {
354        SpeedUtil speedUtil = warrant.getSpeedUtil();
355        // if a RosterEntry exists "trainId" will be the Roster Id, otherwise a train name
356        if (elem.getAttribute("trainId") != null) {
357            speedUtil.setRosterId(elem.getAttribute("trainId").getValue());
358        }
359        if (speedUtil.getRosterEntry() == null) {
360            if (elem.getAttribute("dccAddress") != null) {
361                try {
362                   int address = elem.getAttribute("dccAddress").getIntValue();
363                   String type = "L";
364                   if (elem.getAttribute("dccType") != null) {
365                       type = elem.getAttribute("dccType").getValue();
366                   }
367                   if (!speedUtil.setDccAddress(address, type)) {
368                       log.error("dccAddress {}, dccType {} in Warrant {}",
369                               address, type, warrant.getDisplayName());
370                   }
371                } catch (org.jdom2.DataConversionException dce) {
372                    log.error("{} for dccAddress in Warrant {}", dce, warrant.getDisplayName());
373                }
374            }
375        }
376        if (elem.getAttribute("runBlind") != null) {
377            warrant.setRunBlind(elem.getAttribute("runBlind").getValue().equals("true"));
378        }
379        if (elem.getAttribute("shareRoute") != null) {
380            warrant.setShareRoute(elem.getAttribute("shareRoute").getValue().equals("true"));
381        }
382        if (elem.getAttribute("noRamp") != null) {
383            warrant.setNoRamp(elem.getAttribute("noRamp").getValue().equals("true"));
384        }
385        if (elem.getAttribute("trainName") != null) {
386            warrant.setTrainName(elem.getAttribute("trainName").getValue());
387        }
388    }
389
390    private static BlockOrder loadBlockOrder(Element elem) {
391
392        OBlock block;
393        List<Element> blocks = elem.getChildren("block");
394        if (blocks.size()>1) log.error("More than one block present: {}", blocks.size());
395        if (blocks.size()>0) {
396            String name = blocks.get(0).getAttribute("systemName").getValue();
397            block = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(name);
398            if (block == null) {
399                log.error("No such Block \"{}\" found.", name);
400                return null;
401            }
402            if (log.isDebugEnabled()) log.debug("Load Block {}.", name);
403        } else {
404            log.error("Null BlockOrder element");
405            return null;
406        }
407        Attribute attr = elem.getAttribute("pathName");
408        String pathName = null;
409        if (attr != null) {
410            pathName = attr.getValue();
411        }
412
413        attr = elem.getAttribute("entryName");
414        String entryName = null;
415        if (attr != null) {
416            entryName =attr.getValue();
417        }
418        attr = elem.getAttribute("exitName");
419        String exitName = null;
420        if (attr != null) {
421            exitName =attr.getValue();
422        }
423        return new BlockOrder(block, pathName, entryName, exitName);
424    }
425
426    private static ThrottleSetting loadThrottleSetting(Element element, Warrant w) {
427
428        ThrottleSetting ts = new ThrottleSetting();
429
430        Attribute attr = element.getAttribute("elapsedTime");
431        if (attr != null) {
432            ts.setTime(Long.parseLong(attr.getValue()));
433        }
434
435        Command cmd = null;
436        Element elem = element.getChild("command");
437        if (elem != null) {
438            attr = elem.getAttribute("commandType");
439            if (attr != null) {
440                try {
441                    cmd = ThrottleSetting.getCommandTypeFromInt(Integer.parseInt(attr.getValue()));
442                    ts.setCommand(cmd);
443                } catch (IllegalArgumentException iae) {
444                    log.error("{} for {} in warrant {}",iae.getMessage(), ts.toString(), w.getDisplayName());
445                }
446            } else {
447                log.error("Command type is null for {} in warrant {}", ts.toString(), w.getDisplayName());
448            }
449            attr = elem.getAttribute("fKey");
450            if (attr != null) {
451                ts.setKeyNum(Integer.parseInt(attr.getValue()));
452            }
453        }
454
455        elem = element.getChild("commandValue");
456        ValueType valType = null;
457        SpeedStepMode mode = null;
458        float floatVal = 0;
459        String textVal = "";
460        if (elem != null) {
461            attr = elem.getAttribute("valueType");
462            if (attr != null) {
463                try {
464                    valType = ThrottleSetting.getValueTypeFromInt(Integer.parseInt(attr.getValue()));
465                } catch (IllegalArgumentException iae) {
466                    log.error("{} for throttleSetting {} in warrant {}",iae.getMessage(), ts.toString(), w.getDisplayName());
467                }
468            } else {
469                log.error("Value type is null for {} in warrant {}", ts.toString(), w.getDisplayName());
470            }
471            attr = elem.getAttribute("speedMode");
472            if (attr != null) {
473                mode = SpeedStepMode.getByName(attr.getValue());
474            }
475            attr = elem.getAttribute("floatValue");
476            if (attr != null) {
477                floatVal = Float.parseFloat(attr.getValue());
478            }
479
480            attr = elem.getAttribute("textValue");
481            if (attr != null) {
482                textVal = attr.getValue();
483            }
484        }
485        ts.setValue(valType, mode, floatVal, textVal);
486
487        attr = element.getAttribute("trackSpeed");
488        if (attr != null) {
489            ts.setTrackSpeed(Float.parseFloat(attr.getValue()));
490        }
491
492        attr = element.getAttribute("beanName");
493        if (attr != null) {
494            String errMsg = ts.setNamedBean(cmd, attr.getValue());
495            if (errMsg != null) {
496                log.error("{} for {} in warrant {}", errMsg, ts.toString(), w.getDisplayName());
497            }
498        }
499        return ts;
500    }
501
502    // pre 4.21.3
503//    @SuppressFBWarnings(value="NP_LOAD_OF_KNOWN_NULL_VALUE", justification="nothing wrong about a null return")
504    private static ThrottleSetting loadThrottleCommand(Element elem, Warrant w) {
505        long time = 0;
506        try {
507            time = elem.getAttribute("time").getLongValue();
508        } catch (org.jdom2.DataConversionException dce) {
509            log.warn("error loading throttle command");
510        }
511
512        Attribute attr = elem.getAttribute("command");
513        String command;
514        if (attr != null) {
515            command = attr.getValue();
516        } else {
517            log.error("Command type is null. ThrottleSetting not loaded for warrant {}", w.getDisplayName());
518            return null;
519        }
520
521        attr = elem.getAttribute("value");
522        String value = null;
523        if (attr != null) {
524            value =attr.getValue();
525        }
526        attr = elem.getAttribute("block");
527        String block = null;
528        if (attr != null) {
529            block =attr.getValue();
530        }
531        float speed = 0.0f;
532        attr = elem.getAttribute("trackSpeed");
533        if (attr != null) {
534            try {
535                speed = attr.getFloatValue();
536            } catch (DataConversionException ex) {
537                speed = 0.0f;
538                log.error("Unable to read speed of command.", ex);
539            }
540        }
541
542        return new ThrottleSetting(time, command, value, block, speed);
543    }
544
545    @Override
546    public int loadOrder(){
547        return InstanceManager.getDefault(WarrantManager.class).getXMLOrder();
548    }
549
550    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(WarrantManagerXml.class);
551
552}