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 return; 348 } 349 HashMap<NamedBean, List<NamedBean>> validPaths = lbm.getLayoutBlockConnectivityTools() 350 .discoverValidBeanPairs(null, SignalMast.class, LayoutBlockConnectivityTools.Routing.MASTTOMAST); 351 firePropertyChange(PROPERTY_AUTO_GENERATE_UPDATE, null, 352 ("Found " + validPaths.size() + " masts as sources for logic")); 353 InstanceManager.getDefault(SignalMastManager.class).getNamedBeanSet().forEach(nb -> 354 nb.removeProperty(PROPERTY_INTERMEDIATE_SIGNAL)); 355 for (Entry<NamedBean, List<NamedBean>> e : validPaths.entrySet()) { 356 SignalMast key = (SignalMast) e.getKey(); 357 SignalMastLogic sml = getSignalMastLogic(key); 358 if (sml == null) { 359 sml = newSignalMastLogic(key); 360 } 361 List<NamedBean> validDestMast = validPaths.get(key); 362 for (NamedBean nb : validDestMast) { 363 if (!sml.isDestinationValid((SignalMast) nb)) { 364 try { 365 sml.setDestinationMast((SignalMast) nb); 366 sml.useLayoutEditorDetails(true, true, (SignalMast) nb); 367 sml.useLayoutEditor(true, (SignalMast) nb); 368 } 369 catch (JmriException ex) { 370 //log.debug("we shouldn't get an exception here!"); 371 log.warn("Unexpected exception setting mast", ex); 372 } 373 } 374 } 375 if (sml.getDestinationList().size() == 1 376 && sml.getAutoTurnouts(sml.getDestinationList().get(0)).isEmpty()) { 377 key.setProperty(PROPERTY_INTERMEDIATE_SIGNAL, true); 378 } 379 } 380 initialise(); 381 firePropertyChange(PROPERTY_AUTO_GENERATE_COMPLETE, null, null); 382 } 383 384 /** 385 * Populate Sections of type SIGNALMASTLOGIC used with Layout Editor with 386 * Signal Mast attributes as stored in Signal Mast Logic. 387 */ 388 public void generateSection() { 389 SectionManager sm = InstanceManager.getDefault(SectionManager.class); 390 sm.getNamedBeanSet().stream().map( nb -> { 391 if (nb.getSectionType() == Section.SIGNALMASTLOGIC) { 392 nb.removeProperty(PROPERTY_INTERMEDIATE_SECTION); 393 } 394 return nb; 395 }).forEachOrdered( nb -> nb.removeProperty("forwardMast")); 396 for (SignalMastLogic sml : getSignalMastLogicList()) { 397 LayoutBlock faceLBlock = sml.getFacingBlock(); 398 if (faceLBlock != null) { 399 boolean sourceIntermediate = false; 400 Object intermSigProp = sml.getSourceMast().getProperty(PROPERTY_INTERMEDIATE_SIGNAL); 401 if (intermSigProp != null) { 402 sourceIntermediate = ((Boolean) intermSigProp); 403 } 404 for (SignalMast destMast : sml.getDestinationList()) { 405 if (!sml.getAutoBlocksBetweenMasts(destMast).isEmpty()) { 406 String secUserName = sml.getSourceMast().getDisplayName() + ":" + destMast.getDisplayName(); 407 Section sec = sm.getSection(secUserName); 408 if (sec != null) { 409 //A Section already exists, lets check that it is one used with the SML, if so carry on using that. 410 if (sec.getSectionType() != Section.SIGNALMASTLOGIC) { 411 break; 412 } 413 } else { 414 try { 415 sec = sm.createNewSection(secUserName); 416 } catch(IllegalArgumentException ex){ 417 log.warn("Unable to create section for {} {}",secUserName,ex.getMessage()); 418 continue; 419 } 420 // new mast 421 sec.setSectionType(Section.SIGNALMASTLOGIC); 422 try { 423 //Auto running requires forward/reverse sensors, but at this stage SML does not support that, so just create dummy internal ones for now. 424 Sensor sen = InstanceManager.sensorManagerInstance().provideSensor("IS:" + sec.getSystemName() + ":forward"); 425 sen.setUserName(sec.getSystemName() + ":forward"); 426 427 sen = InstanceManager.sensorManagerInstance().provideSensor("IS:" + sec.getSystemName() + ":reverse"); 428 sen.setUserName(sec.getSystemName() + ":reverse"); 429 sec.setForwardBlockingSensorName(sec.getSystemName() + ":forward"); 430 sec.setReverseBlockingSensorName(sec.getSystemName() + ":reverse"); 431 } catch (IllegalArgumentException ex) { 432 log.warn("Failed to provide Sensor in generateSection"); 433 } 434 } 435 sml.setAssociatedSection(sec, destMast); 436 sec.setProperty("forwardMast", destMast.getDisplayName()); 437 boolean destIntermediate = false; 438 Object destMastImSigProp = destMast.getProperty(PROPERTY_INTERMEDIATE_SIGNAL); 439 if ( destMastImSigProp != null) { 440 destIntermediate = ((Boolean) destMastImSigProp); 441 } 442 443 sec.setProperty(PROPERTY_INTERMEDIATE_SECTION, sourceIntermediate || destIntermediate); 444 445 //Not 100% sure about this for now so will comment out 446 //sml.addSensor(sec.getSystemName()+":forward", Sensor.INACTIVE, destMast); 447 } 448 } 449 } else { 450 log.info("No facing block found {}", sml.getSourceMast().getDisplayName()); 451 } 452 } 453 } 454 455 /** {@inheritDoc} */ 456 @Override 457 public String getBeanTypeHandled(boolean plural) { 458 return Bundle.getMessage(plural ? "BeanNameSignalMastLogics" : "BeanNameSignalMastLogic"); 459 } 460 461 /** 462 * {@inheritDoc} 463 */ 464 @Override 465 public Class<SignalMastLogic> getNamedBeanClass() { 466 return SignalMastLogic.class; 467 } 468 469 /** 470 * {@inheritDoc} 471 */ 472 @Override 473 public int setupSignalMastsDirectionSensors() { 474 int errorCount = 0; 475 for (SignalMastLogic sml : getSignalMastLogicList()) { 476 errorCount += sml.setupDirectionSensors(); 477 } 478 return errorCount; 479 } 480 481 /** 482 * {@inheritDoc} 483 */ 484 @Override 485 public void removeSignalMastsDirectionSensors() { 486 for (SignalMastLogic sml : getSignalMastLogicList()) { 487 sml.removeDirectionSensors(); 488 } 489 } 490 491 /** {@inheritDoc} */ 492 @Override 493 public void dispose(){ 494 InstanceManager.getDefault(LayoutBlockManager.class).removePropertyChangeListener(propertyBlockManagerListener); 495 InstanceManager.getDefault(SignalMastManager.class).removeVetoableChangeListener(this); 496 InstanceManager.getDefault(TurnoutManager.class).removeVetoableChangeListener(this); 497 super.dispose(); 498 } 499 500 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultSignalMastLogicManager.class); 501}