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