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 element.addContent(elem); 201 202 return element; 203 } 204 205 @Override 206 public boolean load(Element shared, Element perNode) { 207 208 WarrantManager manager = InstanceManager.getDefault(WarrantManager.class); 209 210 if (shared.getChildren().isEmpty()) { 211 return true; 212 } 213 214 List<Element> warrantList = shared.getChildren("warrant"); 215 log.debug("Found {} Warrant objects", warrantList.size()); 216 boolean previouslyLoaded = false; 217 for (Element elem : warrantList) { 218 if (elem.getAttribute("systemName") == null) { 219 log.warn("unexpected null for systemName in elem {}", elem); 220 break; 221 } 222 String sysName = null; 223 if (elem.getAttribute("systemName") != null) 224 sysName = elem.getAttribute("systemName").getValue(); 225 226 String userName = null; 227 if (elem.getAttribute("userName") != null) 228 userName = elem.getAttribute("userName").getValue(); 229 230 boolean SCWa = true; 231 log.debug("loading warrant {}", sysName); 232 Attribute wType = elem.getAttribute("wtype"); 233 if (wType == null) { 234 log.debug("wtype is null for {}", sysName); 235 SCWa = false; 236 } else if (!wType.getValue().equals("SC")) { 237 log.debug("wtype is {} for {}", wType.getValue(), sysName); 238 SCWa = false; 239 } 240 241 long timeToPlatform = 500; 242 Attribute TTP = elem.getAttribute("timeToPlatform"); 243 if (TTP != null) { 244 try { 245 timeToPlatform = TTP.getLongValue(); 246 } catch (DataConversionException e) { 247 log.debug("ignoring DataConversionException (and reverting to default value): {}", e.toString()); 248 } 249 } 250 251 Warrant warrant = manager.createNewWarrant(sysName, userName, SCWa, timeToPlatform); 252 if (warrant == null) { 253 log.info("Warrant \"{}\" (userName={}) previously loaded. This version not loaded.", sysName, userName); 254 previouslyLoaded = true; 255 continue; 256 } 257 previouslyLoaded = false; 258 if (SCWa && warrant instanceof SCWarrant) { 259 if (elem.getAttribute("forward") != null) { 260 ((SCWarrant)warrant).setForward(elem.getAttribute("forward").getValue().equals("true")); 261 } 262 if (elem.getAttribute("speedFactor") != null) { 263 try { 264 ((SCWarrant)warrant).setSpeedFactor(elem.getAttribute("speedFactor").getFloatValue()); 265 } catch (DataConversionException e) { 266 log.warn("error converting speed value"); 267 } 268 } 269 warrant.setNoRamp(SCWa); 270 warrant.setShareRoute(SCWa); 271 } 272 List<Element> orders = elem.getChildren("blockOrder"); 273 int count = 0; 274 for (Element ord : orders) { 275 BlockOrder bo = loadBlockOrder(ord); 276 if (bo == null) { 277 log.error("Bad BlockOrder in warrant \"{}\" elem= {}.", warrant.getDisplayName(), elem.getText()); 278 } else { 279 bo.setIndex(count++); 280 warrant.addBlockOrder(bo); 281 } 282 } 283 String c = elem.getChildText("comment"); 284 if (c != null) { 285 warrant.setComment(c); 286 } 287 288 Element order = elem.getChild("viaOrder"); 289 if (order!=null) { 290 warrant.setViaOrder(loadBlockOrder(order)); 291 } 292 order = elem.getChild("avoidOrder"); 293 if (order!=null) { 294 warrant.setAvoidOrder(loadBlockOrder(order)); 295 } 296 297 if (SCWa) { 298 boolean forward =true; 299 if (elem.getAttribute("forward") != null) { 300 forward = elem.getAttribute("forward").getValue().equals("true"); 301 } 302 if (warrant instanceof SCWarrant) { 303 ((SCWarrant)warrant).setForward(forward); 304 } 305 warrant.setNoRamp(SCWa); 306 warrant.setShareRoute(SCWa); 307 } 308 Element train = elem.getChild("train"); 309 if (train!=null) { 310 loadTrain(train, warrant); 311 } 312 } 313 if (!previouslyLoaded) { 314 // A second pass through the warrant list done to load the commands. This is done so that 315 // references made to warrants in commands are fully specified. Due to ThrottleSetting 316 // Ctor using provideWarrant to establish the referenced warrant. 317 warrantList = shared.getChildren("warrant"); 318 for (Element elem : warrantList) { 319 // boolean forward = true; // variable not used, see GitHub JMRI/JMRI Issue #5661 320 if (elem.getAttribute("systemName") == null) { 321 break; 322 } 323 if (elem.getAttribute("systemName") != null) { 324 String sysName = elem.getAttribute("systemName").getValue(); 325 if (sysName != null) { 326 Warrant warrant = manager.getBySystemName(sysName); 327 List<Element> throttleCmds; 328 if (warrant != null) { 329 log.debug("warrant: {}", warrant.getDisplayName()); 330 throttleCmds = elem.getChildren("throttleCommand"); 331 if (throttleCmds != null && !throttleCmds.isEmpty()) { 332 log.debug("throttleCommand size= {}",throttleCmds.size()); 333 throttleCmds.forEach((e) -> { 334 warrant.addThrottleCommand(loadThrottleCommand(e, warrant)); 335 }); 336 warrant.setTrackSpeeds(); 337 } else { 338 throttleCmds = elem.getChildren("throttleSetting"); 339 if (throttleCmds != null) { 340 log.debug("throttleSetting size= {}",throttleCmds.size()); 341 throttleCmds.forEach((e) -> { 342 warrant.addThrottleCommand(loadThrottleSetting(e, warrant)); 343 }); 344 } 345 } 346 } 347 } 348 } 349 } 350 } 351 return true; 352 } 353 354 private static void loadTrain(Element elem, Warrant warrant) { 355 SpeedUtil speedUtil = warrant.getSpeedUtil(); 356 // if a RosterEntry exists "trainId" will be the Roster Id, otherwise a train name 357 if (elem.getAttribute("trainId") != null) { 358 speedUtil.setRosterId(elem.getAttribute("trainId").getValue()); 359 } 360 if (speedUtil.getRosterEntry() == null) { 361 if (elem.getAttribute("dccAddress") != null) { 362 try { 363 int address = elem.getAttribute("dccAddress").getIntValue(); 364 String type = "L"; 365 if (elem.getAttribute("dccType") != null) { 366 type = elem.getAttribute("dccType").getValue(); 367 } 368 if (!speedUtil.setDccAddress(address, type)) { 369 log.error("dccAddress {}, dccType {} in Warrant {}", 370 address, type, warrant.getDisplayName()); 371 } 372 } catch (org.jdom2.DataConversionException dce) { 373 log.error("{} for dccAddress in Warrant {}", dce, warrant.getDisplayName()); 374 } 375 } 376 } 377 if (elem.getAttribute("runBlind") != null) { 378 warrant.setRunBlind(elem.getAttribute("runBlind").getValue().equals("true")); 379 } 380 if (elem.getAttribute("shareRoute") != null) { 381 warrant.setShareRoute(elem.getAttribute("shareRoute").getValue().equals("true")); 382 } 383 if (elem.getAttribute("noRamp") != null) { 384 warrant.setNoRamp(elem.getAttribute("noRamp").getValue().equals("true")); 385 } 386 if (elem.getAttribute("trainName") != null) { 387 warrant.setTrainName(elem.getAttribute("trainName").getValue()); 388 } 389 } 390 391 private static BlockOrder loadBlockOrder(Element elem) { 392 393 OBlock block = null; 394 List<Element> blocks = elem.getChildren("block"); 395 if (blocks.size()>1) log.error("More than one block present: {}", blocks.size()); 396 if (blocks.size()>0) { 397 String name = blocks.get(0).getAttribute("systemName").getValue(); 398 block = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(name); 399 if (block == null) { 400 log.error("No such Block \"{}\" found.", name); 401 return null; 402 } 403 if (log.isDebugEnabled()) log.debug("Load Block {}.", name); 404 } else { 405 log.error("Null BlockOrder element"); 406 return null; 407 } 408 Attribute attr = elem.getAttribute("pathName"); 409 String pathName = null; 410 if (attr != null) { 411 pathName = attr.getValue(); 412 } 413 414 attr = elem.getAttribute("entryName"); 415 String entryName = null; 416 if (attr != null) 417 entryName =attr.getValue(); 418 419 attr = elem.getAttribute("exitName"); 420 String exitName = null; 421 if (attr != null) 422 exitName =attr.getValue(); 423 424 return new BlockOrder(block, pathName, entryName, exitName); 425 } 426 427 private static ThrottleSetting loadThrottleSetting(Element element, Warrant w) { 428 429 ThrottleSetting ts = new ThrottleSetting(); 430 431 Attribute attr = element.getAttribute("elapsedTime"); 432 if (attr != null) { 433 ts.setTime(Long.parseLong(attr.getValue())); 434 } 435 436 Command cmd = null; 437 Element elem = element.getChild("command"); 438 if (elem != null) { 439 attr = elem.getAttribute("commandType"); 440 if (attr != null) { 441 try { 442 cmd = ThrottleSetting.getCommandTypeFromInt(Integer.parseInt(attr.getValue())); 443 ts.setCommand(cmd); 444 } catch (IllegalArgumentException iae) { 445 log.error("{} for {} in warrant {}",iae.getMessage(), ts.toString(), w.getDisplayName()); 446 } 447 } else { 448 log.error("Command type is null for {} in warrant {}", ts.toString(), w.getDisplayName()); 449 } 450 attr = elem.getAttribute("fKey"); 451 if (attr != null) { 452 ts.setKeyNum(Integer.parseInt(attr.getValue())); 453 } 454 } 455 456 elem = element.getChild("commandValue"); 457 ValueType valType = null; 458 SpeedStepMode mode = null; 459 float floatVal = 0; 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 ts.setValue(valType, mode, floatVal); 481 482 attr = element.getAttribute("trackSpeed"); 483 if (attr != null) { 484 ts.setTrackSpeed(Float.parseFloat(attr.getValue())); 485 } 486 487 attr = element.getAttribute("beanName"); 488 if (attr != null) { 489 String errMsg = ts.setNamedBean(cmd, attr.getValue()); 490 if (errMsg != null) { 491 log.error("{} for {} in warrant {}", errMsg, ts.toString(), w.getDisplayName()); 492 } 493 } 494 return ts; 495 } 496 497 // pre 4.21.3 498// @SuppressFBWarnings(value="NP_LOAD_OF_KNOWN_NULL_VALUE", justification="nothing wrong about a null return") 499 private static ThrottleSetting loadThrottleCommand(Element elem, Warrant w) { 500 long time = 0; 501 try { 502 time = elem.getAttribute("time").getLongValue(); 503 } catch (org.jdom2.DataConversionException dce) { 504 log.warn("error loading throttle command"); 505 } 506 507 Attribute attr = elem.getAttribute("command"); 508 String command = null; 509 if (attr != null) { 510 command = attr.getValue(); 511 } else { 512 log.error("Command type is null. ThrottleSetting not loaded for warrant {}", w.getDisplayName()); 513 return null; 514 } 515 516 attr = elem.getAttribute("value"); 517 String value = null; 518 if (attr != null) 519 value =attr.getValue(); 520 521 attr = elem.getAttribute("block"); 522 String block = null; 523 if (attr != null) 524 block =attr.getValue(); 525 526 float speed = 0.0f; 527 attr = elem.getAttribute("trackSpeed"); 528 if (attr != null) { 529 try { 530 speed = attr.getFloatValue(); 531 } catch (DataConversionException ex) { 532 speed = 0.0f; 533 log.error("Unable to read speed of command.", ex); 534 } 535 } 536 537 return new ThrottleSetting(time, command, value, block, speed); 538 } 539 540 @Override 541 public int loadOrder(){ 542 return InstanceManager.getDefault(WarrantManager.class).getXMLOrder(); 543 } 544 545 private final static Logger log = LoggerFactory.getLogger(WarrantManagerXml.class); 546 547}