001package jmri.managers.configurexml; 002 003import java.util.List; 004import java.util.SortedSet; 005import jmri.InstanceManager; 006import jmri.NamedBeanHandle; 007import jmri.Sensor; 008import jmri.Turnout; 009import jmri.TurnoutManager; 010import jmri.TurnoutOperation; 011import jmri.TurnoutOperationManager; 012import jmri.configurexml.TurnoutOperationManagerXml; 013import jmri.configurexml.turnoutoperations.TurnoutOperationXml; 014import org.jdom2.Attribute; 015import org.jdom2.Element; 016import org.slf4j.Logger; 017import org.slf4j.LoggerFactory; 018 019/** 020 * Provides the abstract base and store functionality for configuring 021 * TurnoutManagers, working with AbstractTurnoutManagers. 022 * <p> 023 * Typically, a subclass will just implement the load(Element turnouts) class, 024 * relying on implementation here to load the individual turnouts. Note that 025 * these are stored explicitly, so the resolution mechanism doesn't need to see 026 * *Xml classes for each specific Turnout or AbstractTurnout subclass at store 027 * time. 028 * 029 * @author Bob Jacobsen Copyright: Copyright (c) 2002 030 */ 031public abstract class AbstractTurnoutManagerConfigXML extends AbstractNamedBeanManagerConfigXML { 032 033 public AbstractTurnoutManagerConfigXML() { 034 } 035 036 /** 037 * Default implementation for storing the contents of a TurnoutManager and 038 * associated TurnoutOperations. 039 * 040 * @param o Object to store, of type TurnoutManager 041 * @return Element containing the complete info 042 */ 043 @Override 044 public Element store(Object o) { 045 Element turnouts = new Element("turnouts"); 046 setStoreElementClass(turnouts); 047 TurnoutManager tm = (TurnoutManager) o; 048 if (tm != null) { 049 TurnoutOperationManagerXml tomx = new TurnoutOperationManagerXml(); 050 Element opElem = tomx.store(InstanceManager.getDefault(TurnoutOperationManager.class)); 051 turnouts.addContent(opElem); 052 SortedSet<Turnout> tList = tm.getNamedBeanSet(); 053 // don't return an element if there are no turnouts to include 054 if (tList.isEmpty()) { 055 return null; 056 } 057 String defaultclosed = tm.getDefaultClosedSpeed(); 058 String defaultthrown = tm.getDefaultThrownSpeed(); 059 turnouts.addContent(new Element("defaultclosedspeed").addContent(defaultclosed)); 060 turnouts.addContent(new Element("defaultthrownspeed").addContent(defaultthrown)); 061 for (Turnout t : tList) { 062 // store the turnouts 063 String tName = t.getSystemName(); 064 log.debug("system name is {}", tName); 065 066 Element elem = new Element("turnout"); 067 elem.addContent(new Element("systemName").addContent(tName)); 068 log.debug("store Turnout {}", tName); 069 070 storeCommon(t, elem); 071 072 // include feedback info 073 elem.setAttribute("feedback", t.getFeedbackModeName()); 074 NamedBeanHandle<Sensor> s = t.getFirstNamedSensor(); 075 if (s != null) { 076 elem.setAttribute("sensor1", s.getName()); 077 } 078 s = t.getSecondNamedSensor(); 079 if (s != null) { 080 elem.setAttribute("sensor2", s.getName()); 081 } 082 083 // include turnout inverted 084 elem.setAttribute("inverted", t.getInverted() ? "true" : "false"); 085 086 if (t.canLock(Turnout.CABLOCKOUT | Turnout.PUSHBUTTONLOCKOUT)) { 087 // include turnout locked 088 elem.setAttribute("locked", t.getLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT) ? "true" : "false"); 089 // include turnout lock mode 090 String lockOpr; 091 if (t.canLock(Turnout.CABLOCKOUT) && t.canLock(Turnout.PUSHBUTTONLOCKOUT)) { 092 lockOpr = "both"; 093 } else if (t.canLock(Turnout.CABLOCKOUT)) { 094 lockOpr = "cab"; 095 } else if (t.canLock(Turnout.PUSHBUTTONLOCKOUT)) { 096 lockOpr = "pushbutton"; 097 } else { 098 lockOpr = "none"; 099 } 100 elem.setAttribute("lockMode", lockOpr); 101 // include turnout decoder 102 elem.setAttribute("decoder", t.getDecoderName()); 103 } 104 105 // include number of control bits, if different from one 106 int iNum = t.getNumberControlBits(); 107 if (iNum != 1) { 108 elem.setAttribute("numBits", "" + iNum); 109 } 110 111 // include turnout control type, if different from 0 112 int iType = t.getControlType(); 113 if (iType != 0) { 114 elem.setAttribute("controlType", "" + iType); 115 } 116 117 // add operation stuff 118 String opstr = null; 119 TurnoutOperation op = t.getTurnoutOperation(); 120 if (t.getInhibitOperation()) { 121 opstr = "Off"; 122 } else if (op == null) { 123 opstr = "Default"; 124 } else if (op.isNonce()) { // nonce operation appears as subelement 125 TurnoutOperationXml adapter = TurnoutOperationXml.getAdapter(op); 126 if (adapter != null) { 127 Element nonceOpElem = adapter.store(op); 128 elem.addContent(nonceOpElem); 129 } 130 } else { 131 opstr = op.getName(); 132 } 133 if (opstr != null) { 134 elem.setAttribute("automate", opstr); 135 } 136 if ((t.getDivergingSpeed() != null) && (!t.getDivergingSpeed().isEmpty()) && !t.getDivergingSpeed().contains("Global")) { 137 elem.addContent(new Element("divergingSpeed").addContent(t.getDivergingSpeed())); 138 } 139 if ((t.getStraightSpeed() != null) && (!t.getStraightSpeed().isEmpty()) && !t.getStraightSpeed().contains("Global")) { 140 elem.addContent(new Element("straightSpeed").addContent(t.getStraightSpeed())); 141 } 142 143 // add element 144 turnouts.addContent(elem); 145 } 146 } 147 return turnouts; 148 } 149 150 /** 151 * Subclass provides implementation to create the correct top element, 152 * including the type information. Default implementation is to use the 153 * local class here. 154 * 155 * @param turnouts The top-level element being created 156 */ 157 abstract public void setStoreElementClass(Element turnouts); 158 159 @Override 160 public abstract boolean load(Element shared, Element perNode); 161 162 /** 163 * Utility method to load the individual Turnout objects. If there's no 164 * additional info needed for a specific turnout type, invoke this with the 165 * parent of the set of Turnout elements. 166 * 167 * @param shared Element containing the Turnout elements to load. 168 * @param perNode Element containing per-node Turnout data. 169 * @return true if succeeded 170 */ 171 public boolean loadTurnouts(Element shared, Element perNode) { 172 boolean result = true; 173 List<Element> operationList = shared.getChildren("operations"); 174 if (operationList.size() > 1) { 175 log.warn("unexpected extra elements found in turnout operations list"); 176 result = false; 177 } 178 if (!operationList.isEmpty()) { 179 TurnoutOperationManagerXml tomx = new TurnoutOperationManagerXml(); 180 tomx.load(operationList.get(0), null); 181 } 182 List<Element> turnoutList = shared.getChildren("turnout"); 183 log.debug("Found {} turnouts", turnoutList.size()); 184 TurnoutManager tm = InstanceManager.turnoutManagerInstance(); 185 tm.setPropertyChangesSilenced("beans", true); 186 187 try { 188 if (shared.getChild("defaultclosedspeed") != null) { 189 String closedSpeed = shared.getChild("defaultclosedspeed").getText(); 190 if (closedSpeed != null && !closedSpeed.isEmpty()) { 191 tm.setDefaultClosedSpeed(closedSpeed); 192 } 193 } 194 } catch (jmri.JmriException ex) { 195 log.error("JmriException {}", ex.getMessage() ); 196 } 197 198 try { 199 if (shared.getChild("defaultthrownspeed") != null) { 200 String thrownSpeed = shared.getChild("defaultthrownspeed").getText(); 201 if (thrownSpeed != null && !thrownSpeed.isEmpty()) { 202 tm.setDefaultThrownSpeed(thrownSpeed); 203 } 204 } 205 } catch (jmri.JmriException ex) { 206 log.error("JmriException {}", ex.getMessage() ); 207 } 208 209 for (Element elem : turnoutList) { 210 String sysName = getSystemName(elem); 211 if (sysName == null) { 212 log.error("unexpected null in systemName {}", elem); 213 result = false; 214 break; 215 } 216 String userName = getUserName(elem); 217 218 checkNameNormalization(sysName, userName, tm); 219 220 log.debug("create turnout: ({})({})", sysName, (userName == null ? "<null>" : userName)); 221 Turnout t = tm.getBySystemName(sysName); 222 if (t == null) { 223 t = tm.newTurnout(sysName, userName); 224 // nothing is logged in the console window as the newTurnoutFunction already does this. 225 } else if (userName != null) { 226 t.setUserName(userName); 227 } 228 229 // Load common parts 230 loadCommon(t, elem); 231 232 // now configure feedback if needed 233 Attribute a; 234 a = elem.getAttribute("sensor1"); 235 if (a != null) { 236 try { 237 t.provideFirstFeedbackSensor(a.getValue()); 238 } catch (jmri.JmriException e) { 239 result = false; 240 } 241 } 242 a = elem.getAttribute("sensor2"); 243 if (a != null) { 244 try { 245 t.provideSecondFeedbackSensor(a.getValue()); 246 } catch (jmri.JmriException e) { 247 result = false; 248 } 249 } 250 a = elem.getAttribute("feedback"); 251 if (a != null) { 252 try { 253 t.setFeedbackMode(a.getValue()); 254 } catch (IllegalArgumentException e) { 255 log.error("Can not set feedback mode: '{}' for turnout: '{}' user name: '{}'", 256 a.getValue(), sysName, (userName == null ? "" : userName)); 257 result = false; 258 } 259 } 260 261 // check for turnout inverted 262 t.setInverted(getAttributeBool(elem, "inverted", false)); 263 264 // check for turnout decoder 265 a = elem.getAttribute("decoder"); 266 if (a != null) { 267 t.setDecoderName(a.getValue()); 268 } 269 270 // check for turnout lock mode 271 a = elem.getAttribute("lockMode"); 272 if (a != null) { 273 if (a.getValue().equals("both")) { 274 t.enableLockOperation(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, true); 275 } 276 if (a.getValue().equals("cab")) { 277 t.enableLockOperation(Turnout.CABLOCKOUT, true); 278 t.enableLockOperation(Turnout.PUSHBUTTONLOCKOUT, false); 279 } 280 if (a.getValue().equals("pushbutton")) { 281 t.enableLockOperation(Turnout.PUSHBUTTONLOCKOUT, true); 282 t.enableLockOperation(Turnout.CABLOCKOUT, false); 283 } 284 } 285 286 // check for turnout locked 287 a = elem.getAttribute("locked"); 288 if (a != null) { 289 t.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, a.getValue().equals("true")); 290 } 291 292 // number of bits, if present - if not, defaults to 1 293 a = elem.getAttribute("numBits"); 294 if (a == null) { 295 t.setNumberControlBits(1); 296 } else { 297 int iNum = Integer.parseInt(a.getValue()); 298 if ((iNum == 1) || (iNum == 2)) { 299 t.setNumberControlBits(iNum); 300 } else { 301 log.warn("illegal number of output bits for control of turnout {}", sysName); 302 t.setNumberControlBits(1); 303 result = false; 304 } 305 } 306 307 // control type, if present - if not, defaults to 0 308 a = elem.getAttribute("controlType"); 309 if (a == null) { 310 t.setControlType(0); 311 } else { 312 int iType = Integer.parseInt(a.getValue()); 313 if (iType >= 0) { 314 t.setControlType(iType); 315 } else { 316 log.warn("illegal control type for control of turnout {}", sysName); 317 t.setControlType(0); 318 result = false; 319 } 320 } 321 322 // operation stuff 323 List<Element> myOpList = elem.getChildren("operation"); 324 if (!myOpList.isEmpty()) { 325 if (myOpList.size() > 1) { 326 log.warn("unexpected extra elements found in turnout-specific operations"); 327 result = false; 328 } 329 TurnoutOperation toper = TurnoutOperationXml.loadOperation(myOpList.get(0)); 330 t.setTurnoutOperation(toper); 331 } else { 332 a = elem.getAttribute("automate"); 333 if (a != null) { 334 String str = a.getValue(); 335 if (str.equals("Off")) { 336 t.setInhibitOperation(true); 337 } else if (!str.equals("Default")) { 338 t.setInhibitOperation(false); 339 TurnoutOperation toper 340 = InstanceManager.getDefault(TurnoutOperationManager.class).getOperation(str); 341 t.setTurnoutOperation(toper); 342 } else { 343 t.setInhibitOperation(false); 344 } 345 } 346 } 347 348 // set initial state from sensor feedback if appropriate 349 t.setInitialKnownStateFromFeedback(); 350 try { 351 t.setDivergingSpeed("Global"); 352 if (elem.getChild("divergingSpeed") != null) { 353 String speed = elem.getChild("divergingSpeed").getText(); 354 if (speed != null && !speed.isEmpty() && !speed.contains("Global")) { 355 t.setDivergingSpeed(speed); 356 } 357 } 358 } catch (jmri.JmriException ex) { 359 log.error("Turnout {} : {}", t, ex.getMessage()); 360 } 361 362 try { 363 t.setStraightSpeed("Global"); 364 if (elem.getChild("straightSpeed") != null) { 365 String speed = elem.getChild("straightSpeed").getText(); 366 if (speed != null && !speed.isEmpty() && !speed.contains("Global")) { 367 t.setStraightSpeed(speed); 368 } 369 } 370 } catch (jmri.JmriException ex) { 371 log.error("Turnout {} : {}", t, ex.getMessage()); 372 } 373 } 374 375 tm.setPropertyChangesSilenced("beans", false); 376 377 return result; 378 } 379 380 @Override 381 public int loadOrder() { 382 return InstanceManager.turnoutManagerInstance().getXMLOrder(); 383 } 384 385 private final static Logger log = LoggerFactory.getLogger(AbstractTurnoutManagerConfigXML.class); 386 387}