001package jmri.managers; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.*; 006import java.util.Map.Entry; 007 008import javax.annotation.Nonnull; 009 010import jmri.*; 011import jmri.implementation.DefaultSignalMastLogic; 012import jmri.implementation.SignalSpeedMap; 013import jmri.jmrit.display.layoutEditor.*; 014import jmri.jmrix.internal.InternalSystemConnectionMemo; 015 016import org.slf4j.Logger; 017import org.slf4j.LoggerFactory; 018 019/** 020 * Default implementation of a SignalMastLogicManager. 021 * 022 * @see jmri.SignalMastLogicManager 023 * 024 * @author Kevin Dickerson Copyright (C) 2011 025 */ 026public class DefaultSignalMastLogicManager 027 extends AbstractManager<SignalMastLogic> 028 implements SignalMastLogicManager { 029 030 public DefaultSignalMastLogicManager(InternalSystemConnectionMemo memo) { 031 super(memo); 032 registerSelf(); 033 addListeners(); 034 } 035 036 final void addListeners(){ 037 InstanceManager.getDefault(LayoutBlockManager.class).addPropertyChangeListener(propertyBlockManagerListener); 038 InstanceManager.getDefault(SignalMastManager.class).addVetoableChangeListener(this); 039 InstanceManager.getDefault(TurnoutManager.class).addVetoableChangeListener(this); 040 } 041 042 /** 043 * {@inheritDoc} 044 */ 045 @Override 046 public int getXMLOrder() { 047 return Manager.SIGNALMASTLOGICS; 048 } 049 050 private static final SignalSpeedMap _speedMap = InstanceManager.getDefault(SignalSpeedMap.class); 051 052 public final static SignalSpeedMap getSpeedMap() { 053 return _speedMap; 054 } 055 056 /** 057 * {@inheritDoc} 058 */ 059 @Override 060 public SignalMastLogic getSignalMastLogic(SignalMast source) { 061 for (SignalMastLogic signalMastLogic : _beans) { 062 if (signalMastLogic.getSourceMast() == source) { 063 return signalMastLogic; 064 } 065 } 066 return null; 067 } 068 069 /** 070 * Provide / create New SML. 071 * {@inheritDoc} 072 */ 073 @Nonnull 074 @Override 075 public SignalMastLogic newSignalMastLogic(SignalMast source) throws IllegalArgumentException { 076 for (SignalMastLogic signalMastLogic : _beans) { 077 if (signalMastLogic.getSourceMast() == source) { 078 return signalMastLogic; 079 } 080 } 081 SignalMastLogic logic = new DefaultSignalMastLogic(source); 082 _beans.add(logic); 083 firePropertyChange("length", null, _beans.size()); 084 return logic; 085 } 086 087 /** {@inheritDoc} */ 088 @Override 089 public void replaceSignalMast(SignalMast oldMast, SignalMast newMast) { 090 if (oldMast == null || newMast == null) { 091 return; 092 } 093 for (SignalMastLogic source : _beans) { 094 if (source.getSourceMast() == oldMast) { 095 source.replaceSourceMast(oldMast, newMast); 096 } else { 097 source.replaceDestinationMast(oldMast, newMast); 098 } 099 } 100 } 101 102 /** {@inheritDoc} */ 103 @Override 104 public void swapSignalMasts(SignalMast mastA, SignalMast mastB) { 105 if (mastA == null || mastB == null) { 106 return; 107 } 108 List<SignalMastLogic> mastALogicList = getLogicsByDestination(mastA); 109 SignalMastLogic mastALogicSource = getSignalMastLogic(mastA); 110 111 List<SignalMastLogic> mastBLogicList = getLogicsByDestination(mastB); 112 SignalMastLogic mastBLogicSource = getSignalMastLogic(mastB); 113 114 if (mastALogicSource != null) { 115 mastALogicSource.replaceSourceMast(mastA, mastB); 116 } 117 if (mastBLogicSource != null) { 118 mastBLogicSource.replaceSourceMast(mastB, mastA); 119 } 120 121 for (SignalMastLogic mastALogic : mastALogicList) { 122 mastALogic.replaceDestinationMast(mastA, mastB); 123 } 124 for (SignalMastLogic mastBLogic : mastBLogicList) { 125 mastBLogic.replaceDestinationMast(mastB, mastA); 126 } 127 128 } 129 130 /** {@inheritDoc} */ 131 @Nonnull 132 @Override 133 public List<SignalMastLogic> getLogicsByDestination(SignalMast destination) { 134 List<SignalMastLogic> list = new ArrayList<>(); 135 for (SignalMastLogic source : _beans) { 136 if (source.isDestinationValid(destination)) { 137 list.add(source); 138 } 139 } 140 return list; 141 } 142 143 /** {@inheritDoc} */ 144 @Nonnull 145 @Override 146 public List<SignalMastLogic> getSignalMastLogicList() { 147 return new ArrayList<>(_beans); 148 } 149 150 /** {@inheritDoc} */ 151 @Override 152 public boolean isSignalMastUsed(@Nonnull SignalMast mast) { 153 SignalMastLogic sml = getSignalMastLogic(mast); 154 if (sml != null) { 155 /* Although we might have it registered as a source, it may not have 156 any valid destination, so therefore it can be returned as not in use. */ 157 if (!sml.getDestinationList().isEmpty()) { 158 return true; 159 } 160 } 161 return !getLogicsByDestination(mast).isEmpty(); 162 } 163 164 /** {@inheritDoc} */ 165 @Override 166 public void removeSignalMastLogic(SignalMastLogic sml, SignalMast dest) { 167 if (sml.removeDestination(dest)) { 168 removeSignalMastLogic(sml); 169 } 170 } 171 172 /** {@inheritDoc} */ 173 @Override 174 public void removeSignalMastLogic(SignalMastLogic sml) { 175 if (sml == null) { 176 return; 177 } 178 //Need to provide a method to delete and dispose. 179 sml.dispose(); 180 181 _beans.remove(sml); 182 firePropertyChange("length", null, _beans.size()); 183 } 184 185 /** {@inheritDoc} */ 186 @Override 187 public void removeSignalMast(SignalMast mast) { 188 if (mast == null) { 189 return; 190 } 191 for (SignalMastLogic source : _beans) { 192 if (source.isDestinationValid(mast)) { 193 source.removeDestination(mast); 194 } 195 } 196 SignalMastLogic sml = getSignalMastLogic(mast); 197 if ( sml != null ) { 198 removeSignalMastLogic(sml); 199 } 200 } 201 202 /** 203 * Disable the use of info from the Layout Editor Panels to configure 204 * a Signal Mast Logic for a specific Signal Mast. 205 * 206 * @param mast The Signal Mast for which LE info is to be disabled 207 */ 208 @Override 209 public void disableLayoutEditorUse(SignalMast mast) { 210 SignalMastLogic source = getSignalMastLogic(mast); 211 if (source != null) { 212 source.disableLayoutEditorUse(); 213 } 214 for (SignalMastLogic sml : getLogicsByDestination(mast)) { 215 try { 216 sml.useLayoutEditor(false, mast); 217 } catch (JmriException e) { 218 log.error("Error occurred while trying to disable layout editor use", e); 219 } 220 } 221 } 222 223 // Abstract methods to be extended by subclasses: 224 225 /** 226 * Initialise all the Signal Mast Logics. Primarily used after 227 * loading a configuration. 228 */ 229 @Override 230 public void initialise() { 231 for (SignalMastLogic signalMastLogic : _beans) { 232 signalMastLogic.initialise(); 233 } 234 } 235 236 /** {@inheritDoc} */ 237 @Override 238 public char typeLetter() { 239 throw new UnsupportedOperationException("Not supported yet."); 240 } 241 242 int signalLogicDelay = 500; 243 244 /** {@inheritDoc} */ 245 @Override 246 public int getSignalLogicDelay() { 247 return signalLogicDelay; 248 } 249 250 /** {@inheritDoc} */ 251 @Override 252 public void setSignalLogicDelay(int l) { 253 signalLogicDelay = l; 254 } 255 256 protected PropertyChangeListener propertyBlockManagerListener = new PropertyChangeListener() { 257 @Override 258 public void propertyChange(PropertyChangeEvent e) { 259 if (e.getPropertyName().equals("topology")) { 260 //boolean newValue = new Boolean.parseBoolean(String.valueOf(e.getNewValue())); 261 boolean newValue = (Boolean) e.getNewValue(); 262 if (newValue) { 263 for (SignalMastLogic signalMastLogic : _beans) { 264 signalMastLogic.setupLayoutEditorDetails(); 265 } 266 if (runWhenStablised) { 267 try { 268 automaticallyDiscoverSignallingPairs(); 269 } catch (JmriException je) { 270 //Considered normal if routing not enabled 271 } 272 } 273 } 274 } 275 } 276 }; 277 278 boolean runWhenStablised = false; 279 280 /** 281 * Discover valid destination Signal Masts for a given source Signal Mast on a 282 * given Layout Editor Panel. 283 * 284 * @param source Source SignalMast 285 * @param layout Layout Editor panel to check. 286 */ 287 @Override 288 public void discoverSignallingDest(SignalMast source, LayoutEditor layout) throws JmriException { 289 firePropertyChange("autoSignalMastGenerateStart", null, source.getDisplayName()); 290 291 HashMap<SignalMast, List<NamedBean>> validPaths = new HashMap<>(); 292 LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class); 293 if (!lbm.isAdvancedRoutingEnabled()) { 294 //log.debug("advanced routing not enabled"); 295 throw new JmriException("advanced routing not enabled"); 296 } 297 if (!lbm.routingStablised()) { 298 throw new JmriException("routing not stabilised"); 299 } 300 try { 301 validPaths.put(source, lbm.getLayoutBlockConnectivityTools().discoverPairDest(source, layout, SignalMast.class, LayoutBlockConnectivityTools.Routing.MASTTOMAST)); 302 } 303 catch (JmriException e) { 304 throw e; 305 } 306 307 validPaths.entrySet().forEach((entry) -> { 308 SignalMast key = entry.getKey(); 309 SignalMastLogic sml = getSignalMastLogic(key); 310 if (sml == null) { 311 sml = newSignalMastLogic(key); 312 } 313 314 List<NamedBean> validDestMast = entry.getValue(); 315 for (NamedBean sm : validDestMast) { 316 if (!sml.isDestinationValid((SignalMast) sm)) { 317 try { 318 sml.setDestinationMast((SignalMast) sm); 319 sml.useLayoutEditorDetails(true, true, (SignalMast) sm); 320 sml.useLayoutEditor(true, (SignalMast) sm); 321 } catch (JmriException e) { 322 //log.debug("We shouldn't get an exception here"); 323 log.error("Exception found when adding pair {} to destination {}/\n{}", source.getDisplayName(), sm.getDisplayName(), e.toString()); 324 //throw e; 325 } 326 } 327 } 328 if (sml.getDestinationList().size() == 1 && sml.getAutoTurnouts(sml.getDestinationList().get(0)).isEmpty()) { 329 key.setProperty("intermediateSignal", true); 330 } else { 331 key.removeProperty("intermediateSignal"); 332 } 333 }); 334 initialise(); 335 firePropertyChange("autoSignalMastGenerateComplete", null, source.getDisplayName()); 336 } 337 338 /** 339 * Discover all possible valid source + destination signal mast pairs on all 340 * Layout Editor Panels. 341 */ 342 @Override 343 public void automaticallyDiscoverSignallingPairs() throws JmriException { 344 runWhenStablised = false; 345 LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class); 346 if (!lbm.isAdvancedRoutingEnabled()) { 347 throw new JmriException("advanced routing not enabled"); 348 } 349 if (!lbm.routingStablised()) { 350 runWhenStablised = true; 351 return; 352 } 353 HashMap<NamedBean, List<NamedBean>> validPaths = lbm.getLayoutBlockConnectivityTools().discoverValidBeanPairs(null, SignalMast.class, LayoutBlockConnectivityTools.Routing.MASTTOMAST); 354 firePropertyChange("autoGenerateUpdate", null, ("Found " + validPaths.size() + " masts as sources for logic")); 355 InstanceManager.getDefault(SignalMastManager.class).getNamedBeanSet().forEach((nb) -> { 356 nb.removeProperty("intermediateSignal"); 357 }); 358 for (Entry<NamedBean, List<NamedBean>> e : validPaths.entrySet()) { 359 SignalMast key = (SignalMast) e.getKey(); 360 SignalMastLogic sml = getSignalMastLogic(key); 361 if (sml == null) { 362 sml = newSignalMastLogic(key); 363 } 364 List<NamedBean> validDestMast = validPaths.get(key); 365 for (NamedBean nb : validDestMast) { 366 if (!sml.isDestinationValid((SignalMast) nb)) { 367 try { 368 sml.setDestinationMast((SignalMast) nb); 369 sml.useLayoutEditorDetails(true, true, (SignalMast) nb); 370 sml.useLayoutEditor(true, (SignalMast) nb); 371 } 372 catch (JmriException ex) { 373 //log.debug("we shouldn't get an exception here!"); 374 log.warn("Unexpected exception setting mast", ex); 375 } 376 } 377 } 378 if (sml.getDestinationList().size() == 1 && sml.getAutoTurnouts(sml.getDestinationList().get(0)).isEmpty()) { 379 key.setProperty("intermediateSignal", true); 380 } 381 } 382 initialise(); 383 firePropertyChange("autoGenerateComplete", null, null); 384 } 385 386 /** 387 * Populate Sections of type SIGNALMASTLOGIC used with Layout Editor with 388 * Signal Mast attributes as stored in Signal Mast Logic. 389 */ 390 public void generateSection() { 391 SectionManager sm = InstanceManager.getDefault(SectionManager.class); 392 sm.getNamedBeanSet().stream().map((nb) -> { 393 if (nb.getSectionType() == Section.SIGNALMASTLOGIC) { 394 nb.removeProperty("intermediateSection"); 395 } 396 return nb; 397 }).forEachOrdered((nb) -> { 398 nb.removeProperty("forwardMast"); 399 }); 400 for (SignalMastLogic sml : getSignalMastLogicList()) { 401 LayoutBlock faceLBlock = sml.getFacingBlock(); 402 if (faceLBlock != null) { 403 boolean sourceIntermediate = false; 404 Object intermSigProp = sml.getSourceMast().getProperty("intermediateSignal"); 405 if (intermSigProp != null) { 406 sourceIntermediate = ((Boolean) intermSigProp); 407 } 408 for (SignalMast destMast : sml.getDestinationList()) { 409 if (!sml.getAutoBlocksBetweenMasts(destMast).isEmpty()) { 410 String secUserName = sml.getSourceMast().getDisplayName() + ":" + destMast.getDisplayName(); 411 Section sec = sm.getSection(secUserName); 412 if (sec != null) { 413 //A Section already exists, lets check that it is one used with the SML, if so carry on using that. 414 if (sec.getSectionType() != Section.SIGNALMASTLOGIC) { 415 break; 416 } 417 } else { 418 try { 419 sec = sm.createNewSection(secUserName); 420 } catch(IllegalArgumentException ex){ 421 log.warn("Unable to create section for {} {}",secUserName,ex.getMessage()); 422 continue; 423 } 424 // new mast 425 sec.setSectionType(Section.SIGNALMASTLOGIC); 426 try { 427 //Auto running requires forward/reverse sensors, but at this stage SML does not support that, so just create dummy internal ones for now. 428 Sensor sen = InstanceManager.sensorManagerInstance().provideSensor("IS:" + sec.getSystemName() + ":forward"); 429 sen.setUserName(sec.getSystemName() + ":forward"); 430 431 sen = InstanceManager.sensorManagerInstance().provideSensor("IS:" + sec.getSystemName() + ":reverse"); 432 sen.setUserName(sec.getSystemName() + ":reverse"); 433 sec.setForwardBlockingSensorName(sec.getSystemName() + ":forward"); 434 sec.setReverseBlockingSensorName(sec.getSystemName() + ":reverse"); 435 } catch (IllegalArgumentException ex) { 436 log.warn("Failed to provide Sensor in generateSection"); 437 } 438 } 439 sml.setAssociatedSection(sec, destMast); 440 sec.setProperty("forwardMast", destMast.getDisplayName()); 441 boolean destIntermediate = false; 442 Object destMastImSigProp = destMast.getProperty("intermediateSignal"); 443 if ( destMastImSigProp != null) { 444 destIntermediate = ((Boolean) destMastImSigProp); 445 } 446 if (sourceIntermediate || destIntermediate) { 447 sec.setProperty("intermediateSection", true); 448 } else { 449 sec.setProperty("intermediateSection", false); 450 } 451 //Not 100% sure about this for now so will comment out 452 //sml.addSensor(sec.getSystemName()+":forward", Sensor.INACTIVE, destMast); 453 } 454 } 455 } else { 456 log.info("No facing block found {}", sml.getSourceMast().getDisplayName()); 457 } 458 } 459 } 460 461 /** {@inheritDoc} */ 462 @Override 463 public String getBeanTypeHandled(boolean plural) { 464 return Bundle.getMessage(plural ? "BeanNameSignalMastLogics" : "BeanNameSignalMastLogic"); 465 } 466 467 /** 468 * {@inheritDoc} 469 */ 470 @Override 471 public Class<SignalMastLogic> getNamedBeanClass() { 472 return SignalMastLogic.class; 473 } 474 475 /** 476 * {@inheritDoc} 477 */ 478 @Override 479 public int setupSignalMastsDirectionSensors() { 480 int errorCount = 0; 481 for (SignalMastLogic sml : getSignalMastLogicList()) { 482 errorCount += sml.setupDirectionSensors(); 483 } 484 return errorCount; 485 } 486 487 /** 488 * {@inheritDoc} 489 */ 490 @Override 491 public void removeSignalMastsDirectionSensors() { 492 for (SignalMastLogic sml : getSignalMastLogicList()) { 493 sml.removeDirectionSensors(); 494 } 495 } 496 497 /** {@inheritDoc} */ 498 @Override 499 public void dispose(){ 500 InstanceManager.getDefault(LayoutBlockManager.class).removePropertyChangeListener(propertyBlockManagerListener); 501 InstanceManager.getDefault(SignalMastManager.class).removeVetoableChangeListener(this); 502 InstanceManager.getDefault(TurnoutManager.class).removeVetoableChangeListener(this); 503 super.dispose(); 504 } 505 506 private final static Logger log = LoggerFactory.getLogger(DefaultSignalMastLogicManager.class); 507}