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