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