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