001package jmri.jmrit.logix.configurexml; 002 003import java.awt.GraphicsEnvironment; 004import java.util.HashMap; 005import java.util.List; 006import java.util.SortedSet; 007 008import javax.annotation.Nonnull; 009 010import jmri.BeanSetting; 011import jmri.InstanceManager; 012import jmri.NamedBean; 013import jmri.Path; 014import jmri.Reporter; 015import jmri.Turnout; 016import jmri.jmrit.logix.OBlock; 017import jmri.jmrit.logix.OBlockManager; 018import jmri.jmrit.logix.OPath; 019import jmri.jmrit.logix.Portal; 020import jmri.jmrit.logix.PortalManager; 021import jmri.util.swing.JmriJOptionPane; 022 023import org.jdom2.Attribute; 024import org.jdom2.Element; 025 026/** 027 * Provides the abstract base and store functionality for configuring the 028 * OBlockManager. 029 * <p> 030 * Typically, a subclass will just implement the load(Element oblocks) 031 * class, relying on implementation here to load the individual oblock 032 * objects. 033 * 034 * @author Pete Cressman Copyright: Copyright (c) 2009 035 */ 036public class OBlockManagerXml // extends XmlFile 037 extends jmri.configurexml.AbstractXmlAdapter { 038 039 public OBlockManagerXml() { 040 } 041 042 /** 043 * Store the contents of a OBlockManager. 044 * 045 * @param o Object to store, of type BlockManager 046 * @return Element containing the complete info 047 */ 048 @Override 049 public Element store(Object o) { 050 Element blocks = new Element("oblocks"); 051 blocks.setAttribute("class", "jmri.jmrit.logix.configurexml.OBlockManagerXml"); 052 OBlockManager obm = (OBlockManager) o; 053 if (obm != null) { 054 SortedSet<OBlock> oblockList = obm.getNamedBeanSet(); 055 // don't return an element if there are no oblocks to include 056 if (oblockList.isEmpty()) { 057 return null; 058 } 059 for (OBlock block : oblockList) { 060 String sName = block.getSystemName(); 061 String uName = block.getUserName(); 062 log.debug("OBlock: sysName= {}, userName= {}", sName, uName); 063 Element elem = new Element("oblock"); 064 elem.setAttribute("systemName", sName); 065 if (uName != null && !uName.isEmpty()) { 066 elem.setAttribute("userName", uName); // doing this for compatibility during 2.9.* series 067 elem.addContent(new Element("userName").addContent(uName)); 068 } 069 String comment = block.getComment(); 070 if (comment != null && !comment.isEmpty()) { 071 Element c = new Element("comment"); 072 c.addContent(comment); 073 elem.addContent(c); 074 } 075 elem.setAttribute("length", "" + block.getLengthMm()); 076 elem.setAttribute("units", block.isMetric() ? "true" : "false"); 077 elem.setAttribute("curve", "" + block.getCurvature()); 078 var namedSensor = block.getNamedSensor(); 079 if (namedSensor != null) { 080 Element se = new Element("sensor"); 081 se.setAttribute("systemName", namedSensor.getName()); 082 elem.addContent(se); 083 } 084 var errorSensor = block.getNamedErrorSensor(); 085 if ( errorSensor != null) { 086 Element se = new Element("errorSensor"); 087 se.setAttribute("systemName", errorSensor.getName()); 088 elem.addContent(se); 089 } 090 var reporter = block.getReporter(); 091 if ( reporter != null) { 092 Element se = new Element("reporter"); 093 se.setAttribute("systemName", reporter.getSystemName()); 094 se.setAttribute("reportCurrent", block.isReportingCurrent() ? "true" : "false"); 095 elem.addContent(se); 096 } 097 elem.setAttribute("permissive", block.getPermissiveWorking() ? "true" : "false"); 098 elem.setAttribute("speedNotch", block.getBlockSpeed()); 099 100 List<Path> paths = block.getPaths(); 101 for (Path op : paths) { 102 if ( op instanceof OPath ) { 103 elem.addContent(storePath((OPath) op)); 104 } 105 } 106 List<Portal> portals = block.getPortals(); 107 for (Portal po : portals) { 108 elem.addContent(storePortal(po)); 109 } 110 // and put this element out 111 blocks.addContent(elem); 112 } 113 } 114 return blocks; 115 } 116 117 private static Element storePortal(@Nonnull Portal portal) { 118 Element elem = new Element("portal"); 119 elem.setAttribute("portalName", portal.getName()); 120 OBlock block = portal.getFromBlock(); 121 if (block != null) { 122 Element fromElem = new Element("fromBlock"); 123 fromElem.setAttribute("blockName", block.getSystemName()); 124 List<OPath> paths = portal.getFromPaths(); 125 if (paths != null) { 126 for (OPath path : paths) { 127 fromElem.addContent(storePathKey(path)); 128 } 129 } 130 elem.addContent(fromElem); 131 } else { 132 log.error("Portal \"{}\" has no fromBlock!", portal.getName()); 133 } 134 NamedBean signal = portal.getFromSignal(); 135 if (signal != null) { 136 Element fromElem = new Element("fromSignal"); 137 fromElem.setAttribute("signalName", signal.getSystemName()); 138 fromElem.setAttribute("signalDelay", "" + portal.getFromSignalOffset()); // actually a Distance/Offset 139 elem.addContent(fromElem); 140 } 141 block = portal.getToBlock(); 142 if (block != null) { 143 Element toElem = new Element("toBlock"); 144 toElem.setAttribute("blockName", block.getSystemName()); 145 List<OPath> paths = portal.getToPaths(); 146 if (paths != null) { 147 for (OPath path : paths) { 148 toElem.addContent(storePathKey(path)); 149 } 150 } 151 elem.addContent(toElem); 152 } else { 153 log.error("Portal \"{}\" has no toBlock!", portal.getName()); 154 } 155 signal = portal.getToSignal(); 156 if (signal != null) { 157 Element toElem = new Element("toSignal"); 158 toElem.setAttribute("signalName", signal.getSystemName()); 159 toElem.setAttribute("signalDelay", "" + portal.getToSignalOffset()); 160 elem.addContent(toElem); 161 } 162 return elem; 163 } // storePortal 164 165 /** 166 * Key is sufficient to mark the Portal's knowledge of the path. Full path 167 * info will get loaded from the HashMap. 168 */ 169 @Nonnull 170 private static Element storePathKey( @Nonnull OPath path) { 171 Element elem = new Element("path"); 172 elem.setAttribute("pathName", path.getName()); 173 elem.setAttribute("blockName", "" + path.getBlock().getSystemName()); 174 return elem; 175 } 176 177 @Nonnull 178 private static Element storePath( @Nonnull OPath path) { 179 Element elem = new Element("path"); 180 elem.setAttribute("pathName", path.getName()); 181 elem.setAttribute("blockName", "" + path.getBlock().getSystemName()); 182 Portal portal = path.getFromPortal(); 183 if (portal != null) { 184 elem.setAttribute("fromPortal", portal.getName()); 185 } 186 portal = path.getToPortal(); 187 if (portal != null) { 188 elem.setAttribute("toPortal", portal.getName()); 189 } 190 List<BeanSetting> list = path.getSettings(); 191 for (BeanSetting bs : list) { 192 Element e = new Element("setting"); 193 e.setAttribute("turnout", bs.getBeanName()); 194 e.setAttribute("set", "" + bs.getSetting()); 195 elem.addContent(e); 196 } 197 elem.setAttribute("fromDirection", "" + path.getFromBlockDirection()); 198 elem.setAttribute("toDirection", "" + path.getToBlockDirection()); 199 // get actual object stored length. 200 elem.setAttribute("length", "" + path.getLength()); 201 return elem; 202 } 203 204 /** 205 * Due to the forward and backward referencing among OBlock, OPath and 206 * Portal no precedence order exists to fully create these objects in one 207 * pass. The unique naming of these objects allows the use of Hashmaps to 208 * hold them for update. 209 */ 210 private HashMap<String, OBlock> _blockMap; 211 private HashMap<String, OPath> _pathMap; 212 private OBlockManager _manager; 213 private PortalManager _portalMgr; 214 215 private OBlock getBlock(String sysName) { 216 OBlock block = _blockMap.get(sysName); 217 if (block == null) { 218 try { 219 block = _manager.provideOBlock(sysName); 220 log.debug("found OBlock: ({}) {}", sysName, block); 221 } catch (IllegalArgumentException ex) { 222 block = _manager.createNewOBlock(sysName, null); 223 log.debug("create OBlock: ({})", sysName); 224 } 225 _blockMap.put(sysName, block); 226 } 227 return block; 228 } 229 230 @Nonnull 231 private OPath getPath(OBlock block, String name) { 232 String key = block.getSystemName() + name; 233 OPath path = _pathMap.get(key); 234 if (path == null) { 235 path = new OPath(block, name); 236 _pathMap.put(key, path); 237 log.debug("create OPath: \"{}\" in block {}", name, block.getSystemName()); 238 } 239 return path; 240 } 241 242 @Override 243 public boolean load(Element shared, Element perNode) { 244 _blockMap = new HashMap<>(); 245 _pathMap = new HashMap<>(); 246 _manager = InstanceManager.getDefault(OBlockManager.class); 247 _portalMgr = InstanceManager.getDefault(PortalManager.class); 248 List<Element> blockList = shared.getChildren("oblock"); 249 log.debug("Found {} OBlock objects", blockList.size()); 250 for (Element bl : blockList) { 251 loadBlock(bl); 252 } 253 return true; 254 } 255 256 @Override 257 public void load(Element element, Object o) { 258 log.error("load called. Invalid method."); 259 } 260 261 private void loadBlock(@Nonnull Element elem) { 262 if (elem.getAttribute("systemName") == null) { 263 log.error("unexpected null for block systemName elem = {}", elem); 264 return; 265 } 266 String systemName = elem.getAttribute("systemName").getValue(); 267 String userName = null; 268 if (elem.getAttribute("userName") != null) { 269 userName = elem.getAttribute("userName").getValue(); 270 } 271 log.debug("Load block sysName= {}, userName= {}", systemName, userName); 272 // Portal may have already created a skeleton of this block 273 OBlock block = getBlock(systemName); // never null (for a valid systemName) 274 block.setUserName(userName); 275 String c = elem.getChildText("comment"); 276 if (c != null) { 277 block.setComment(c); 278 } 279 if (elem.getAttribute("units") != null) { 280 block.setMetricUnits(elem.getAttribute("units").getValue().equals("true")); 281 } else { 282 block.setMetricUnits(false); 283 } 284 if (elem.getAttribute("length") != null) { 285 block.setLength(Float.parseFloat(elem.getAttribute("length").getValue())); 286 } 287 if (elem.getAttribute("curve") != null) { 288 block.setCurvature(Integer.parseInt((elem.getAttribute("curve")).getValue())); 289 } 290 List<Element> sensors = elem.getChildren("sensor"); 291 if (sensors.size() > 1) { 292 log.error("More than one sensor present: {}", sensors.size()); 293 } 294 if (!sensors.isEmpty()) { 295 // sensor 296 String name = sensors.get(0).getAttribute("systemName").getValue(); 297 block.setSensor(name); 298 } 299 Element errSensor = elem.getChild("errorSensor"); 300 if (errSensor != null) { 301 // sensor 302 String name = errSensor.getAttribute("systemName").getValue(); 303 block.setErrorSensor(name); 304 } 305 Element reporter = elem.getChild("reporter"); 306 if (reporter != null) { 307 // sensor 308 String name = reporter.getAttribute("systemName").getValue(); 309 try { 310 Reporter rep = InstanceManager.getDefault(jmri.ReporterManager.class).getReporter(name); 311 if (rep != null) { 312 block.setReporter(rep); 313 } 314 } catch (Exception ex) { 315 log.error("No Reporter named \"{}\" found. threw exception", name, ex); 316 } 317 if (reporter.getAttribute("reportCurrent") != null) { 318 block.setReportingCurrent(reporter.getAttribute("reportCurrent").getValue().equals("true")); 319 } else { 320 block.setReportingCurrent(false); 321 } 322 } 323 if (elem.getAttribute("permissive") != null) { 324 block.setPermissiveWorking(elem.getAttribute("permissive").getValue().equals("true")); 325 } else { 326 block.setPermissiveWorking(false); 327 } 328 if (elem.getAttribute("speedNotch") != null) { 329 try { 330 block.setBlockSpeed(elem.getAttribute("speedNotch").getValue()); 331 } catch (jmri.JmriException ex) { 332 log.error("Error setting SpeedNotch {} threw exception", elem.getAttribute("speedNotch").getValue(), ex); 333 if (!GraphicsEnvironment.isHeadless()) { 334 JmriJOptionPane.showMessageDialog(null, ex.getMessage() + "\n" + elem.getAttribute("speedNotch").getValue()); 335 } 336 } 337 } 338 339 List<Element> portals = elem.getChildren("portal"); 340 for (Element po : portals) { 341 Portal portal = loadPortal(po); 342 if (portal != null) { 343 block.addPortal(portal); 344 } 345 } 346 347 List<Element> paths = elem.getChildren("path"); 348 for (Element pa : paths) { 349 if (!block.addPath(loadPath(pa, block))) { 350 log.error("load: block \"{}\" failed to add path \"{}\" in block \"{}\"", 351 systemName, pa.getName(), block.getSystemName()); 352 } 353 } 354 } // loadBlock 355 356 private Portal loadPortal(Element elem) { 357 String userName = elem.getAttribute("portalName").getValue(); 358 String fromBlockName = null; 359 String toBlockName = null; 360 // Portals must have user names. 361 Portal portal = _portalMgr.getPortal(userName); 362 if (portal != null) { 363 OBlock block = portal.getFromBlock(); 364 if (block != null) { 365 fromBlockName = block.getSystemName(); 366 } 367 block = portal.getToBlock(); 368 if (block != null) { 369 toBlockName = block.getSystemName(); 370 } 371 } else { 372 portal = _portalMgr.providePortal(userName); 373 } 374 if (portal == null) { 375 log.error("unable to create Portal ({}) elem attrs= {}", 376 userName, elem.getAttributes()); 377 return null; 378 } 379 log.debug("create Portal: ({})", userName); 380 381 OBlock fromBlock = null; 382 Element eFromBlk = elem.getChild("fromBlock"); 383 if (eFromBlk != null && eFromBlk.getAttribute("blockName") != null) { 384 String name = eFromBlk.getAttribute("blockName").getValue(); 385 if (fromBlockName != null && !fromBlockName.equals(name)) { 386 log.error("Portal user name \"{}\" has conflicting fromBlock \"{}\". Should be \"{}\"", 387 userName, fromBlockName, name); 388 } else { 389 fromBlock = getBlock(name); 390 if (fromBlock != null) { 391 portal.setFromBlock(fromBlock, false); 392 fromBlock.addPortal(portal); 393 394 List<Element> ePathsFromBlock = eFromBlk.getChildren("path"); 395 for (Element e : ePathsFromBlock) { 396 String pathName = e.getAttribute("pathName").getValue(); 397 String blockName = e.getAttribute("blockName").getValue(); 398 log.debug("Load portal= \"{}\" fromBlock= {}, pathName= {}, blockName= {}", 399 userName, fromBlock.getSystemName(), pathName, blockName); 400 OPath path = getPath(fromBlock, pathName); 401 portal.addPath(path); 402 } 403 } 404 } 405 } else { 406 log.error("Portal \"{}\" has no fromBlock!", userName); 407 } 408 409 OBlock toBlock = null; 410 Element eToBlk = elem.getChild("toBlock"); 411 if (eToBlk != null && eToBlk.getAttribute("blockName") != null) { 412 String name = eToBlk.getAttribute("blockName").getValue(); 413 if (toBlockName != null && !toBlockName.equals(name)) { 414 log.error("Portal user name \"{}\" has conflicting toBlock \"{}\". Should be \"{}\"", 415 userName, toBlockName, name); 416 } else { 417 toBlock = getBlock(name); 418 if (toBlock != null) { 419 portal.setToBlock(toBlock, false); 420 toBlock.addPortal(portal); 421 422 List<Element> ePathsToBlock = eToBlk.getChildren("path"); 423 for (Element ePath : ePathsToBlock) { 424 String pathName = ePath.getAttribute("pathName").getValue(); 425 String blockName = ePath.getAttribute("blockName").getValue(); 426 log.debug("Load portal= \"{}\" toBlock= {}, pathName= {}, blockName= {}", userName, toBlock.getSystemName(), pathName, blockName); 427 // path is in the toBlock 428 OPath path = getPath(toBlock, pathName); 429 portal.addPath(path); 430 } 431 } 432 } 433 } else { 434 log.error("Portal \"{}\" has no toBlock!", userName); 435 } 436 Element eSignal = elem.getChild("fromSignal"); 437 if (eSignal != null) { 438 String name = eSignal.getAttribute("signalName").getValue(); 439 float length = 0.0f; 440 try { 441 Attribute attr = eSignal.getAttribute("signalDelay"); // actually a Distance/Offset 442 if (attr != null) { 443 length = attr.getFloatValue(); 444 } 445 } catch (org.jdom2.DataConversionException e) { 446 log.error("Could not parse signalDelay fromSignal ({}) in portal ({})", name, userName); 447 } 448 portal.setProtectSignal(Portal.getSignal(name), length, toBlock); 449 } 450 eSignal = elem.getChild("toSignal"); 451 if (eSignal != null) { 452 String name = eSignal.getAttribute("signalName").getValue(); 453 float length = 0.0f; 454 try { 455 Attribute attr = eSignal.getAttribute("signalDelay"); // actually a Distance/Offset 456 if (attr != null) { 457 length = attr.getFloatValue(); 458 } 459 } catch (org.jdom2.DataConversionException e) { 460 log.error("Could not parse signalDelay toSignal ({}) in portal ({})", name, userName); 461 } 462 portal.setProtectSignal(Portal.getSignal(name), length, fromBlock); 463 } 464 465 log.debug("End Load portal {}", userName); 466 return portal; 467 } // loadPortal 468 469 OPath loadPath(Element elem, OBlock block) { 470 String pName = elem.getAttribute("pathName").getValue(); 471 OPath path = getPath(block, pName); 472 try { 473 Attribute attr = elem.getAttribute("fromDirection"); 474 if (attr != null) { 475 path.setFromBlockDirection(attr.getIntValue()); 476 } 477 attr = elem.getAttribute("toDirection"); 478 if (attr != null) { 479 path.setToBlockDirection(attr.getIntValue()); 480 } 481 attr = elem.getAttribute("length"); 482 if (attr != null) { 483 path.setLength(attr.getFloatValue()); 484 } 485 } catch (org.jdom2.DataConversionException e) { 486 log.error("Could not parse attribute of path \"{}\" in block \"{}\")", 487 pName, block.getSystemName()); 488 } 489 490 Attribute attr = elem.getAttribute("fromPortal"); 491 if (attr != null) { 492 Portal portal = _portalMgr.providePortal(attr.getValue()); 493 if (portal != null) { 494 path.setFromPortal(portal); 495 portal.addPath(path); 496 } 497 } 498 attr = elem.getAttribute("toPortal"); 499 if (attr != null) { 500 Portal portal = _portalMgr.providePortal(attr.getValue()); 501 if (portal != null) { 502 path.setToPortal(portal); 503 portal.addPath(path); 504 } 505 } 506 507 List<Element> settings = elem.getChildren("setting"); 508 log.debug("Path \"{}\" has {} settings.", pName, settings.size()); 509 java.util.HashSet<String> turnouts = new java.util.HashSet<>(); 510 int dups = 0; 511 for (Element setElem : settings) { 512 int setting = 0; 513 try { 514 setting = setElem.getAttribute("set").getIntValue(); 515 } catch (org.jdom2.DataConversionException e) { 516 log.error("Could not parse 'set' attribute for path path \"{}\" in block \"{}\"", 517 pName, block.getSystemName()); 518 } 519 String sysName = setElem.getAttribute("turnout").getValue(); 520 if (!turnouts.contains(sysName)) { 521 Turnout to = InstanceManager.turnoutManagerInstance().provideTurnout(sysName); 522 turnouts.add(sysName); 523 BeanSetting bs = new BeanSetting(to, sysName, setting); 524 path.addSetting(bs); 525 } else { 526 dups++; 527 } 528 } 529 if (dups > 0) { 530 log.warn("{} duplicate settings not loaded for path \"{}\"", dups, pName); 531 } 532 return path; 533 } // loadPath 534 535 @Override 536 public int loadOrder() { 537 return InstanceManager.getDefault(OBlockManager.class).getXMLOrder(); 538 } 539 540 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OBlockManagerXml.class); 541 542}