001package jmri.implementation; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.beans.PropertyVetoException; 006import java.beans.VetoableChangeListener; 007import java.util.*; 008 009import javax.annotation.Nonnull; 010 011import jmri.*; 012import jmri.jmrit.display.EditorManager; 013import jmri.jmrit.display.layoutEditor.ConnectivityUtil; 014import jmri.jmrit.display.layoutEditor.LayoutBlock; 015import jmri.jmrit.display.layoutEditor.LayoutBlockConnectivityTools; 016import jmri.jmrit.display.layoutEditor.LayoutBlockManager; 017import jmri.jmrit.display.layoutEditor.LayoutEditor; 018import jmri.jmrit.display.layoutEditor.LayoutSlip; 019import jmri.jmrit.display.layoutEditor.LayoutTrackExpectedState; 020import jmri.jmrit.display.layoutEditor.LayoutTurnout; 021import jmri.jmrit.display.layoutEditor.LevelXing; 022import jmri.util.ThreadingUtil; 023 024/** 025 * Default implementation of {@link jmri.SignalMastLogic}. 026 * 027 * @author Kevin Dickerson Copyright (C) 2011 028 */ 029public class DefaultSignalMastLogic extends AbstractNamedBean implements SignalMastLogic, VetoableChangeListener { 030 031 SignalMast source; 032 SignalMast destination; 033 String stopAspect; 034 035 Hashtable<SignalMast, DestinationMast> destList = new Hashtable<>(); 036 LayoutEditor editor; 037 038 LayoutBlock facingBlock = null; 039 LayoutBlock remoteProtectingBlock = null; 040 041 boolean disposing = false; 042 043 /** 044 * Initialise a Signal Mast Logic for a given source Signal Mast. 045 * 046 * @param source The Signal Mast we are configuring an SML for 047 */ 048 public DefaultSignalMastLogic(@Nonnull SignalMast source) { 049 super(source.toString()); // default system name 050 this.source = source; 051 try { 052 this.stopAspect = source.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER); 053 this.source.addPropertyChangeListener(propertySourceMastListener); 054 if (source.getAspect() == null) { 055 source.setAspect(stopAspect); 056 } 057 } catch (Exception ex) { 058 log.error("Error while creating Signal Logic", ex); 059 } 060 } 061 062 // Most of the following methods will inherit Javadoc from SignalMastLogic.java 063 /** 064 * {@inheritDoc } 065 */ 066 @Override 067 public void setFacingBlock(LayoutBlock facing) { 068 facingBlock = facing; 069 } 070 071 /** 072 * {@inheritDoc } 073 */ 074 @Override 075 public LayoutBlock getFacingBlock() { 076 return facingBlock; 077 } 078 079 /** 080 * {@inheritDoc } 081 */ 082 @Override 083 public LayoutBlock getProtectingBlock(@Nonnull SignalMast dest) { 084 if (!destList.containsKey(dest)) { 085 return null; 086 } 087 return destList.get(dest).getProtectingBlock(); 088 } 089 090 /** 091 * {@inheritDoc } 092 */ 093 @Override 094 public SignalMast getSourceMast() { 095 return source; 096 } 097 098 /** 099 * {@inheritDoc } 100 */ 101 @Override 102 public void replaceSourceMast(SignalMast oldMast, SignalMast newMast) { 103 if (oldMast != source) { 104 // Old mast does not match new mast so will exit replace 105 return; 106 } 107 source.removePropertyChangeListener(propertySourceMastListener); 108 source = newMast; 109 stopAspect = source.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER); 110 source.addPropertyChangeListener(propertySourceMastListener); 111 if (source.getAspect() == null) { 112 source.setAspect(stopAspect); 113 } 114 getDestinationList().forEach(sm -> { 115 DestinationMast destMast = destList.get(sm); 116 if (destMast.getAssociatedSection() != null) { 117 String oldUserName = destMast.getAssociatedSection().getUserName(); 118 String newUserName = source.getDisplayName() + ":" + sm.getDisplayName(); 119 if (oldUserName != null) { 120 InstanceManager.getDefault(NamedBeanHandleManager.class) 121 .renameBean(oldUserName, newUserName, ((NamedBean) destMast.getAssociatedSection())); 122 } else { 123 log.warn("AssociatedSection oldUserName null for destination mast {}, skipped", 124 destMast.getDisplayName()); 125 } 126 } 127 }); 128 firePropertyChange(PROPERTY_UPDATED_SOURCE, oldMast, newMast); 129 } 130 131 /** 132 * {@inheritDoc } 133 */ 134 @Override 135 public void replaceDestinationMast(SignalMast oldMast, SignalMast newMast) { 136 if (!destList.containsKey(oldMast)) { 137 return; 138 } 139 DestinationMast destMast = destList.get(oldMast); 140 destMast.updateDestinationMast(newMast); 141 if (destination == oldMast) { 142 oldMast.removePropertyChangeListener(propertyDestinationMastListener); 143 newMast.addPropertyChangeListener(propertyDestinationMastListener); 144 destination = newMast; 145 setSignalAppearance(); 146 } 147 destList.remove(oldMast); 148 if (destMast.getAssociatedSection() != null) { 149 String oldUserName = destMast.getAssociatedSection().getUserName(); 150 String newUserName = source.getDisplayName() + ":" + newMast.getDisplayName(); 151 if (oldUserName != null) { 152 InstanceManager.getDefault(NamedBeanHandleManager.class) 153 .renameBean(oldUserName, newUserName, destMast.getAssociatedSection()); 154 } else { 155 log.warn("AssociatedSection oldUserName null for destination mast {}, skipped", 156 destMast.getDisplayName()); 157 } 158 } 159 destList.put(newMast, destMast); 160 firePropertyChange(PROPERTY_UPDATED_DESTINATION, oldMast, newMast); 161 } 162 163 /** 164 * {@inheritDoc } 165 */ 166 @Override 167 public void setDestinationMast(SignalMast dest) { 168 if (destList.containsKey(dest)) { 169 // if already present, not a change 170 log.debug("Destination mast '{}' was already defined in SML with this source mast", dest.getDisplayName()); 171 return; 172 } 173 int oldSize = destList.size(); 174 destList.put(dest, new DestinationMast(dest)); 175 //InstanceManager.getDefault(SignalMastLogicManager.class).addDestinationMastToLogic(this, dest); 176 firePropertyChange(PROPERTY_LENGTH, oldSize, destList.size()); 177 // make new dest mast appear in (update of) SignallingSourcePanel Table by having that table listen to PropertyChange Events from SML TODO 178 } 179 180 /** 181 * {@inheritDoc } 182 */ 183 @Override 184 public boolean isDestinationValid(SignalMast dest) { 185 if (dest == null) { 186 return false; 187 } 188 return destList.containsKey(dest); 189 } 190 191 /** 192 * {@inheritDoc } 193 */ 194 @Override 195 public List<SignalMast> getDestinationList() { 196 List<SignalMast> out = new ArrayList<>(); 197 Enumeration<SignalMast> en = destList.keys(); 198 while (en.hasMoreElements()) { 199 out.add(en.nextElement()); 200 } 201 return out; 202 } 203 204 /** 205 * {@inheritDoc } 206 */ 207 @Override 208 public String getComment(SignalMast dest) { 209 if (!destList.containsKey(dest)) { 210 return ""; 211 } 212 return destList.get(dest).getComment(); 213 } 214 215 /** 216 * {@inheritDoc } 217 */ 218 @Override 219 public void setComment(String comment, SignalMast dest) { 220 if (!destList.containsKey(dest)) { 221 return; 222 } 223 destList.get(dest).setComment(comment); 224 } 225 226 /** 227 * {@inheritDoc } 228 */ 229 @Override 230 public void setStore(int store, SignalMast destination) { 231 if (!destList.containsKey(destination)) { 232 return; 233 } 234 destList.get(destination).setStore(store); 235 } 236 237 /** 238 * {@inheritDoc } 239 */ 240 @Override 241 public int getStoreState(SignalMast destination) { 242 if (!destList.containsKey(destination)) { 243 return STORENONE; 244 } 245 return destList.get(destination).getStoreState(); 246 } 247 248 /** 249 * {@inheritDoc } 250 */ 251 @Override 252 public void setEnabled(SignalMast dest) { 253 if (!destList.containsKey(dest)) { 254 return; 255 } 256 destList.get(dest).setEnabled(); 257 } 258 259 /** 260 * {@inheritDoc } 261 */ 262 @Override 263 public void setDisabled(SignalMast dest) { 264 if (!destList.containsKey(dest)) { 265 return; 266 } 267 destList.get(dest).setDisabled(); 268 } 269 270 /** 271 * {@inheritDoc } 272 */ 273 @Override 274 public boolean isEnabled(SignalMast dest) { 275 if (!destList.containsKey(dest)) { 276 return false; 277 } 278 return destList.get(dest).isEnabled(); 279 } 280 281 /** 282 * {@inheritDoc } 283 */ 284 @Override 285 public boolean isActive(SignalMast dest) { 286 if (!destList.containsKey(dest)) { 287 return false; 288 } 289 return destList.get(dest).isActive(); 290 } 291 292 /** 293 * {@inheritDoc } 294 */ 295 @Override 296 public SignalMast getActiveDestination() { 297 for (SignalMast sm : getDestinationList()) { 298 if (destList.get(sm).isActive()) { 299 return sm; 300 } 301 } 302 return null; 303 } 304 305 /** 306 * {@inheritDoc } 307 */ 308 @Override 309 public boolean removeDestination(SignalMast dest) { 310 int oldSize = destList.size(); 311 if (destList.containsKey(dest)) { 312 //InstanceManager.getDefault(SignalMastLogicManager.class).removeDestinationMastToLogic(this, dest); 313 destList.get(dest).dispose(); 314 destList.remove(dest); 315 firePropertyChange(PROPERTY_LENGTH, oldSize, destList.size()); 316 } 317 return destList.isEmpty(); 318 } 319 320 /** 321 * {@inheritDoc } 322 */ 323 @Override 324 public void disableLayoutEditorUse() { 325 destList.values().forEach(dest -> { 326 try { 327 dest.useLayoutEditor(false); 328 } catch (JmriException e) { 329 log.error("Could not disable LayoutEditor ", e); 330 } 331 }); 332 } 333 334 /** 335 * {@inheritDoc } 336 */ 337 @Override 338 public void useLayoutEditor(boolean boo, SignalMast destination) throws JmriException { 339 if (!destList.containsKey(destination)) { 340 return; 341 } 342 if (boo) { 343 log.debug("Set use layout editor"); 344 Set<LayoutEditor> layout = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class); 345 /*We don't care which layout editor panel the signalmast is on, just so long as 346 the routing is done via layout blocks*/ 347 // TODO: what is this? 348 log.debug("userLayoutEditor finds layout list size is {}", layout.size()); 349 for (LayoutEditor findeditor : layout) { 350 log.debug("layouteditor {}", findeditor.getLayoutName()); 351 if (facingBlock == null) { 352 facingBlock = InstanceManager.getDefault(LayoutBlockManager.class) 353 .getFacingBlockByMast(getSourceMast(), findeditor); 354 } 355 } 356 } 357 destList.get(destination).useLayoutEditor(boo); 358 } 359 360 /** 361 * Add direction sensors to SML 362 * 363 * @return number of errors 364 */ 365 @Override 366 public int setupDirectionSensors() { 367 // iterrate over the signal masts 368 int errorCount = 0; 369 for (SignalMast sm : getDestinationList()) { 370 String displayName = sm.getDisplayName(); 371 Section sec = getAssociatedSection(sm); 372 if (sec != null) { 373 Block thisFacingBlock; 374 Sensor fwd = sec.getForwardBlockingSensor(); 375 Sensor rev = sec.getReverseBlockingSensor(); 376 LayoutBlock lBlock = getFacingBlock(); 377 if (lBlock == null) { 378 try { 379 useLayoutEditor(true, sm); // force a refind 380 } catch (JmriException ex) { 381 continue; 382 } 383 } 384 if (lBlock != null) { 385 thisFacingBlock = lBlock.getBlock(); 386 EntryPoint fwdEntryPoint = sec.getEntryPointFromBlock(thisFacingBlock, Section.FORWARD); 387 EntryPoint revEntryPoint = sec.getEntryPointFromBlock(thisFacingBlock, Section.REVERSE); 388 log.debug("Mast[{}] Sec[{}] Fwd[{}] Rev [{}]", 389 displayName, sec, fwd, rev); 390 if (fwd != null && fwdEntryPoint != null) { 391 addSensor(fwd.getUserName(), Sensor.INACTIVE, sm); 392 log.debug("Mast[{}] Sec[{}] Fwd[{}] fwdEP[{}]", 393 displayName, sec, fwd, 394 fwdEntryPoint.getBlock().getUserName()); 395 396 } else if (rev != null && revEntryPoint != null) { 397 addSensor(rev.getUserName(), Sensor.INACTIVE, sm); 398 log.debug("Mast[{}] Sec[{}] Rev [{}] revEP[{}]", 399 displayName, sec, rev, 400 revEntryPoint.getBlock().getUserName()); 401 402 } else { 403 log.error("Mast[{}] Cannot Establish entry point to protected section", displayName); 404 errorCount += 1; 405 } 406 } else { 407 log.error("Mast[{}] No Facing Block", displayName); 408 errorCount += 1; 409 } 410 } else { 411 log.error("Mast[{}] No Associated Section", displayName); 412 errorCount += 1; 413 } 414 } 415 return errorCount; 416 } 417 418 /** 419 * {@inheritDoc } 420 */ 421 @Override 422 public void removeDirectionSensors() { 423 //TODO find aaway of easilty identifying the ones we added. 424 } 425 426 /** 427 * {@inheritDoc } 428 */ 429 @Override 430 public boolean useLayoutEditor(SignalMast destination) { 431 if (!destList.containsKey(destination)) { 432 return false; 433 } 434 return destList.get(destination).useLayoutEditor(); 435 } 436 437 /** 438 * {@inheritDoc } 439 */ 440 @Override 441 public void useLayoutEditorDetails(boolean turnouts, boolean blocks, SignalMast destination) throws JmriException { 442 if (!destList.containsKey(destination)) { 443 return; 444 } 445 destList.get(destination).useLayoutEditorDetails(turnouts, blocks); 446 } 447 448 /** 449 * {@inheritDoc } 450 */ 451 @Override 452 public boolean useLayoutEditorBlocks(SignalMast destination) { 453 if (!destList.containsKey(destination)) { 454 return false; 455 } 456 return destList.get(destination).useLayoutEditorBlocks(); 457 } 458 459 /** 460 * {@inheritDoc } 461 */ 462 @Override 463 public boolean useLayoutEditorTurnouts(SignalMast destination) { 464 if (!destList.containsKey(destination)) { 465 return false; 466 } 467 return destList.get(destination).useLayoutEditorTurnouts(); 468 } 469 470 /** 471 * {@inheritDoc } 472 */ 473 @Override 474 public Section getAssociatedSection(SignalMast destination) { 475 if (!destList.containsKey(destination)) { 476 return null; 477 } 478 return destList.get(destination).getAssociatedSection(); 479 } 480 481 /** 482 * {@inheritDoc } 483 */ 484 @Override 485 public void setAssociatedSection(Section sec, SignalMast destination) { 486 if (!destList.containsKey(destination)) { 487 return; 488 } 489 destList.get(destination).setAssociatedSection(sec); 490 } 491 492 /** 493 * {@inheritDoc } 494 */ 495 @Override 496 public boolean allowAutoMaticSignalMastGeneration(SignalMast destination) { 497 if (!destList.containsKey(destination)) { 498 return false; 499 } 500 return destList.get(destination).allowAutoSignalMastGen(); 501 } 502 503 /** 504 * {@inheritDoc } 505 */ 506 @Override 507 public void allowAutoMaticSignalMastGeneration(boolean allow, SignalMast destination) { 508 if (!destList.containsKey(destination)) { 509 return; 510 } 511 destList.get(destination).allowAutoSignalMastGen(allow); 512 } 513 514 /** 515 * {@inheritDoc } 516 */ 517 @Override 518 public void allowTurnoutLock(boolean lock, SignalMast destination) { 519 if (!destList.containsKey(destination)) { 520 return; 521 } 522 destList.get(destination).allowTurnoutLock(lock); 523 } 524 525 /** 526 * {@inheritDoc } 527 */ 528 @Override 529 public boolean isTurnoutLockAllowed(SignalMast destination) { 530 if (!destList.containsKey(destination)) { 531 return false; 532 } 533 return destList.get(destination).isTurnoutLockAllowed(); 534 } 535 536 /** 537 * {@inheritDoc } 538 */ 539 @Override 540 public void setTurnouts(Hashtable<NamedBeanHandle<Turnout>, Integer> turnouts, SignalMast destination) { 541 if (!destList.containsKey(destination)) { 542 return; 543 } 544 destList.get(destination).setTurnouts(turnouts); 545 } 546 547 /** 548 * {@inheritDoc } 549 */ 550 @Override 551 public void setAutoTurnouts(Hashtable<Turnout, Integer> turnouts, SignalMast destination) { 552 if (!destList.containsKey(destination)) { 553 return; 554 } 555 destList.get(destination).setAutoTurnouts(turnouts); 556 } 557 558 /** 559 * {@inheritDoc } 560 */ 561 @Override 562 public void setBlocks(Hashtable<Block, Integer> blocks, SignalMast destination) { 563 if (!destList.containsKey(destination)) { 564 return; 565 } 566 destList.get(destination).setBlocks(blocks); 567 } 568 569 /** 570 * {@inheritDoc } 571 */ 572 @Override 573 public void setAutoBlocks(LinkedHashMap<Block, Integer> blocks, SignalMast destination) { 574 if (!destList.containsKey(destination)) { 575 return; 576 } 577 destList.get(destination).setAutoBlocks(blocks); 578 } 579 580 /** 581 * {@inheritDoc } 582 */ 583 @Override 584 public void setMasts(Hashtable<SignalMast, String> masts, SignalMast destination) { 585 if (!destList.containsKey(destination)) { 586 return; 587 } 588 destList.get(destination).setMasts(masts); 589 } 590 591 /** 592 * {@inheritDoc } 593 */ 594 @Override 595 public void setAutoMasts(Hashtable<SignalMast, String> masts, SignalMast destination) { 596 if (!destList.containsKey(destination)) { 597 return; 598 } 599 destList.get(destination).setAutoMasts(masts, true); 600 } 601 602 /** 603 * {@inheritDoc } 604 */ 605 @Override 606 public void setSensors(Hashtable<NamedBeanHandle<Sensor>, Integer> sensors, SignalMast destination) { 607 if (!destList.containsKey(destination)) { 608 return; 609 } 610 destList.get(destination).setSensors(sensors); 611 } 612 613 /** 614 * {@inheritDoc } 615 */ 616 @Override 617 public void addSensor(String sensorName, int state, SignalMast destination) { 618 if (!destList.containsKey(destination)) { 619 return; 620 } 621 Sensor sen = InstanceManager.sensorManagerInstance().getSensor(sensorName); 622 if (sen != null) { 623 NamedBeanHandle<Sensor> namedSensor = InstanceManager.getDefault(NamedBeanHandleManager.class) 624 .getNamedBeanHandle(sensorName, sen); 625 destList.get(destination).addSensor(namedSensor, state); 626 } 627 } 628 629 /** 630 * {@inheritDoc } 631 */ 632 @Override 633 public void removeSensor(String sensorName, SignalMast destination) { 634 Sensor sen = InstanceManager.sensorManagerInstance().getSensor(sensorName); 635 removeSensor(sen, destination); 636 } 637 638 public void removeSensor(Sensor sen, SignalMast destination) { 639 if (!destList.containsKey(destination)) { 640 return; 641 } 642 if (sen != null) { 643 destList.get(destination).removeSensor(sen); 644 } 645 } 646 647 /** 648 * {@inheritDoc } 649 */ 650 @Override 651 public List<Block> getBlocks(SignalMast destination) { 652 if (!destList.containsKey(destination)) { 653 return new ArrayList<>(); 654 } 655 return destList.get(destination).getBlocks(); 656 } 657 658 /** 659 * {@inheritDoc } 660 */ 661 @Override 662 public List<Block> getAutoBlocks(SignalMast destination) { 663 if (!destList.containsKey(destination)) { 664 return new ArrayList<>(); 665 } 666 return destList.get(destination).getAutoBlocks(); 667 } 668 669 /** 670 * {@inheritDoc } 671 */ 672 @Override 673 public List<Block> getAutoBlocksBetweenMasts(SignalMast destination) { 674 if (!destList.containsKey(destination)) { 675 return new ArrayList<>(); 676 } 677 return destList.get(destination).getAutoBlocksBetweenMasts(); 678 } 679 680 /** 681 * {@inheritDoc } 682 */ 683 @Override 684 public List<Turnout> getTurnouts(SignalMast destination) { 685 if (!destList.containsKey(destination)) { 686 return new ArrayList<>(); 687 } 688 return destList.get(destination).getTurnouts(); 689 } 690 691 /** 692 * {@inheritDoc } 693 */ 694 @Override 695 public List<NamedBeanHandle<Turnout>> getNamedTurnouts(SignalMast destination) { 696 if (!destList.containsKey(destination)) { 697 return new ArrayList<>(); 698 } 699 return destList.get(destination).getNamedTurnouts(); 700 } 701 702 public void removeTurnout(Turnout turn, SignalMast destination) { 703 if (!destList.containsKey(destination)) { 704 return; 705 } 706 707 if (turn != null) { 708 destList.get(destination).removeTurnout(turn); 709 } 710 } 711 712 /** 713 * {@inheritDoc } 714 */ 715 @Override 716 public List<Turnout> getAutoTurnouts(SignalMast destination) { 717 if (!destList.containsKey(destination)) { 718 return new ArrayList<>(); 719 } 720 return destList.get(destination).getAutoTurnouts(); 721 } 722 723 /** 724 * {@inheritDoc } 725 */ 726 @Override 727 public List<Sensor> getSensors(SignalMast destination) { 728 if (!destList.containsKey(destination)) { 729 return new ArrayList<>(); 730 } 731 return destList.get(destination).getSensors(); 732 } 733 734 /** 735 * {@inheritDoc } 736 */ 737 @Override 738 public List<NamedBeanHandle<Sensor>> getNamedSensors(SignalMast destination) { 739 if (!destList.containsKey(destination)) { 740 return new ArrayList<>(); 741 } 742 return destList.get(destination).getNamedSensors(); 743 } 744 745 /** 746 * {@inheritDoc } 747 */ 748 @Override 749 public List<SignalMast> getSignalMasts(SignalMast destination) { 750 if (!destList.containsKey(destination)) { 751 return new ArrayList<>(); 752 } 753 return destList.get(destination).getSignalMasts(); 754 } 755 756 /** 757 * {@inheritDoc } 758 */ 759 @Override 760 public List<SignalMast> getAutoMasts(SignalMast destination) { 761 if (!destList.containsKey(destination)) { 762 return new ArrayList<>(); 763 } 764 return destList.get(destination).getAutoSignalMasts(); 765 } 766 767 /** 768 * {@inheritDoc } 769 */ 770 @Override 771 public void initialise() { 772 Enumeration<SignalMast> en = destList.keys(); 773 while (en.hasMoreElements()) { 774 destList.get(en.nextElement()).initialise(); 775 } 776 } 777 778 /** 779 * {@inheritDoc } 780 */ 781 @Override 782 public void initialise(SignalMast destination) { 783 if (disposing) { 784 return; 785 } 786 787 if (!destList.containsKey(destination)) { 788 return; 789 } 790 destList.get(destination).initialise(); 791 } 792 793 /** 794 * {@inheritDoc } 795 */ 796 @Override 797 public LinkedHashMap<Block, Integer> setupLayoutEditorTurnoutDetails(List<LayoutBlock> blks, SignalMast destination) { 798 if (disposing) { 799 return new LinkedHashMap<>(); 800 } 801 802 if (!destList.containsKey(destination)) { 803 return new LinkedHashMap<>(); 804 } 805 return destList.get(destination).setupLayoutEditorTurnoutDetails(blks); 806 } 807 808 /** 809 * {@inheritDoc } 810 */ 811 @Override 812 public void setupLayoutEditorDetails() { 813 if (disposing) { 814 return; 815 } 816 Enumeration<SignalMast> en = destList.keys(); 817 while (en.hasMoreElements()) { 818 try { 819 destList.get(en.nextElement()).setupLayoutEditorDetails(); 820 } catch (JmriException e) { 821 //Considered normal if no route is valid on a Layout Editor panel 822 } 823 } 824 } 825 826 /** 827 * Check if routes to the destination Signal Mast are clear. 828 * 829 * @return true if the path to the next signal is clear 830 */ 831 boolean checkStates() { 832 SignalMast oldActiveMast = destination; 833 if (destination != null) { 834 firePropertyChange(PROPERTY_STATE, oldActiveMast, null); 835 log.debug("Remove listener from destination"); 836 destination.removePropertyChangeListener(propertyDestinationMastListener); 837 if (destList.containsKey(destination)) { 838 destList.get(destination).clearTurnoutLock(); 839 } 840 } 841 842 Enumeration<SignalMast> en = destList.keys(); 843 log.debug("checkStates enumerates over {} mast(s)", destList.size()); 844 while (en.hasMoreElements()) { 845 SignalMast key = en.nextElement(); 846 log.debug(" Destination mast {}", key.getDisplayName()); 847 log.debug(" isEnabled: {}", (destList.get(key)).isEnabled()); 848 log.debug(" isActive: {}", destList.get(key).isActive()); 849 850 if ((destList.get(key)).isEnabled() && (destList.get(key).isActive())) { 851 destination = key; 852 log.debug(" Add listener to destination"); 853 destination.addPropertyChangeListener(propertyDestinationMastListener); 854 log.debug(" firePropertyChange: \"state\""); 855 firePropertyChange(PROPERTY_STATE, oldActiveMast, destination); 856 destList.get(key).lockTurnouts(); 857 return true; 858 } 859 } 860 return false; 861 } 862 863 /** 864 * {@inheritDoc } 865 */ 866 @Override 867 public boolean areBlocksIncluded(List<Block> blks) { 868 Enumeration<SignalMast> en = destList.keys(); 869 while (en.hasMoreElements()) { 870 SignalMast dm = en.nextElement(); 871 boolean included; 872 for (int i = 0; i < blks.size(); i++) { 873 included = destList.get(dm).isBlockIncluded(blks.get(i)); 874 if (included) { 875 return true; 876 } 877 included = destList.get(dm).isAutoBlockIncluded(blks.get(i)); 878 if (included) { 879 return true; 880 } 881 } 882 } 883 return false; 884 } 885 886 /** 887 * {@inheritDoc } 888 */ 889 @Override 890 public int getBlockState(Block block, SignalMast destination) { 891 if (!destList.containsKey(destination)) { 892 return -1; 893 } 894 return destList.get(destination).getBlockState(block); 895 } 896 897 /** 898 * {@inheritDoc } 899 */ 900 @Override 901 public boolean isBlockIncluded(Block block, SignalMast destination) { 902 if (!destList.containsKey(destination)) { 903 return false; 904 } 905 return destList.get(destination).isBlockIncluded(block); 906 } 907 908 /** 909 * {@inheritDoc } 910 */ 911 @Override 912 public boolean isTurnoutIncluded(Turnout turnout, SignalMast destination) { 913 if (!destList.containsKey(destination)) { 914 return false; 915 } 916 return destList.get(destination).isTurnoutIncluded(turnout); 917 } 918 919 /** 920 * {@inheritDoc } 921 */ 922 @Override 923 public boolean isSensorIncluded(Sensor sensor, SignalMast destination) { 924 if (!destList.containsKey(destination)) { 925 return false; 926 } 927 return destList.get(destination).isSensorIncluded(sensor); 928 } 929 930 /** 931 * {@inheritDoc } 932 */ 933 @Override 934 public boolean isSignalMastIncluded(SignalMast signal, SignalMast destination) { 935 if (!destList.containsKey(destination)) { 936 return false; 937 } 938 return destList.get(destination).isSignalMastIncluded(signal); 939 } 940 941 /** 942 * {@inheritDoc } 943 */ 944 @Override 945 public int getAutoBlockState(Block block, SignalMast destination) { 946 if (!destList.containsKey(destination)) { 947 return -1; 948 } 949 return destList.get(destination).getAutoBlockState(block); 950 } 951 952 /** 953 * {@inheritDoc } 954 */ 955 @Override 956 public int getSensorState(Sensor sensor, SignalMast destination) { 957 if (!destList.containsKey(destination)) { 958 return -1; 959 } 960 return destList.get(destination).getSensorState(sensor); 961 } 962 963 /** 964 * {@inheritDoc } 965 */ 966 @Override 967 public int getTurnoutState(Turnout turnout, SignalMast destination) { 968 if (!destList.containsKey(destination)) { 969 return -1; 970 } 971 return destList.get(destination).getTurnoutState(turnout); 972 } 973 974 /** 975 * {@inheritDoc } 976 */ 977 @Override 978 public int getAutoTurnoutState(Turnout turnout, SignalMast destination) { 979 if (!destList.containsKey(destination)) { 980 return -1; 981 } 982 return destList.get(destination).getAutoTurnoutState(turnout); 983 } 984 985 /** 986 * {@inheritDoc } 987 */ 988 @Override 989 public String getSignalMastState(SignalMast mast, SignalMast destination) { 990 if (!destList.containsKey(destination)) { 991 return null; 992 } 993 return destList.get(destination).getSignalMastState(mast); 994 } 995 996 /** 997 * {@inheritDoc } 998 */ 999 @Override 1000 public String getAutoSignalMastState(SignalMast mast, SignalMast destination) { 1001 if (!destList.containsKey(destination)) { 1002 return null; 1003 } 1004 return destList.get(destination).getAutoSignalMastState(mast); 1005 } 1006 1007 /** 1008 * {@inheritDoc } 1009 */ 1010 @Override 1011 public float getMaximumSpeed(SignalMast destination) { 1012 if (!destList.containsKey(destination)) { 1013 return -1; 1014 } 1015 return destList.get(destination).getMinimumSpeed(); 1016 } 1017 1018 volatile boolean inWait = false; 1019 1020 /** 1021 * Before going active or checking that we can go active, wait 500ms 1022 * for things to settle down to help prevent a race condition. 1023 */ 1024 synchronized void setSignalAppearance() { 1025 log.debug("setMastAppearance (Aspect) called for {}", source.getDisplayName()); 1026 if (inWait) { 1027 log.debug("setMastAppearance (Aspect) called with inWait set, returning"); 1028 return; 1029 } 1030 inWait = true; 1031 1032 // The next line forces a single initialization of InstanceManager.getDefault(SignalSpeedMap.class) 1033 // before launching parallel threads 1034 InstanceManager.getDefault(SignalSpeedMap.class); 1035 1036 // The next line forces a single initialization of InstanceManager.getDefault(SignalMastLogicManager.class) 1037 // before launching delay 1038 int tempDelay = InstanceManager.getDefault(SignalMastLogicManager.class).getSignalLogicDelay() / 2; 1039 log.debug("SignalMastLogicManager started (delay)"); 1040 ThreadingUtil.runOnLayoutDelayed( 1041 () -> { 1042 setMastAppearance(); 1043 inWait = false; 1044 }, 1045 tempDelay 1046 ); 1047 1048 } 1049 1050 /** 1051 * Evaluate the destination signal mast Aspect and set ours accordingly. 1052 */ 1053 void setMastAppearance() { 1054 log.debug("Set source Signal Mast Aspect"); 1055 if (getSourceMast().getHeld()) { 1056 log.debug("Signal is at a Held state so will set to the aspect defined for Held or Danger"); 1057 1058 String heldAspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD); 1059 if (heldAspect != null) { 1060 log.debug(" Setting to HELD value of {}", heldAspect); 1061 ThreadingUtil.runOnLayout(() -> { 1062 getSourceMast().setAspect(heldAspect); 1063 }); 1064 } else { 1065 String dangerAspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER); 1066 log.debug(" Setting to DANGER value of {}", dangerAspect); 1067 ThreadingUtil.runOnLayout(() -> { 1068 getSourceMast().setAspect(dangerAspect); 1069 }); 1070 } 1071 return; 1072 } 1073 if (!checkStates()) { 1074 log.debug("Advanced routes not clear, set Stop aspect"); 1075 getSourceMast().setAspect(stopAspect); 1076 return; 1077 } 1078 String[] advancedAspect; 1079 if (destination.getHeld()) { 1080 if (destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD) != null) { 1081 advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD)); 1082 } else { 1083 advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER)); 1084 } 1085 } else { 1086 advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAspect()); 1087 } 1088 1089 log.debug("distant aspect is {}", destination.getAspect()); 1090 log.debug("advanced aspect is {}", advancedAspect != null ? advancedAspect : "<null>"); 1091 1092 if (advancedAspect != null) { 1093 String aspect = stopAspect; 1094 if (destList.get(destination).permissiveBlock) { 1095 if (!getSourceMast().isPermissiveSmlDisabled()) { 1096 //if a block is in a permissive state then we set the permissive appearance 1097 aspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE); 1098 } 1099 } else { 1100 for (String advancedAspect1 : advancedAspect) { 1101 if (!getSourceMast().isAspectDisabled(advancedAspect1)) { 1102 aspect = advancedAspect1; 1103 break; 1104 } 1105 } 1106 List<Integer> divergAspects = new ArrayList<>(); 1107 List<Integer> nonDivergAspects = new ArrayList<>(); 1108 List<Integer> eitherAspects = new ArrayList<>(); 1109 if (advancedAspect.length > 1) { 1110 float maxSigSpeed = -1; 1111 float maxPathSpeed = destList.get(destination).getMinimumSpeed(); 1112 boolean divergRoute = destList.get(destination).turnoutThrown; 1113 1114 log.debug("Diverging route? {}", divergRoute); 1115 boolean divergFlagsAvailable = false; 1116 //We split the aspects into two lists, one with diverging flag set, the other without. 1117 for (int i = 0; i < advancedAspect.length; i++) { 1118 String div = null; 1119 if (!getSourceMast().isAspectDisabled(advancedAspect[i])) { 1120 div = (String) getSourceMast().getSignalSystem().getProperty(advancedAspect[i], "route"); 1121 } 1122 if (div != null) { 1123 if (div.equals("Diverging")) { 1124 log.debug("Aspect {} added as Diverging Route", advancedAspect[i]); 1125 divergAspects.add(i); 1126 divergFlagsAvailable = true; 1127 log.debug("Using Diverging Flag"); 1128 } else if (div.equals("Either")) { 1129 log.debug("Aspect {} added as both Diverging and Normal Route", advancedAspect[i]); 1130 nonDivergAspects.add(i); 1131 divergAspects.add(i); 1132 divergFlagsAvailable = true; 1133 eitherAspects.add(i); 1134 log.debug("Using Diverging Flag"); 1135 } else { 1136 log.debug("Aspect {} added as Normal Route", advancedAspect[i]); 1137 nonDivergAspects.add(i); 1138 log.debug("Aspect {} added as Normal Route", advancedAspect[i]); 1139 } 1140 } else { 1141 nonDivergAspects.add(i); 1142 log.debug("Aspect {} added as Normal Route", advancedAspect[i]); 1143 } 1144 } 1145 if ((eitherAspects.equals(divergAspects)) && (divergAspects.size() < nonDivergAspects.size())) { 1146 //There are no unique diverging aspects 1147 log.debug("'Either' aspects equals divergAspects and is less than non-diverging aspects"); 1148 divergFlagsAvailable = false; 1149 } 1150 log.debug("path max speed : {}", maxPathSpeed); 1151 for (int i = 0; i < advancedAspect.length; i++) { 1152 if (!getSourceMast().isAspectDisabled(advancedAspect[i])) { 1153 String strSpeed = (String) getSourceMast().getSignalSystem().getProperty(advancedAspect[i], "speed"); 1154 log.debug("Aspect Speed = {} for aspect {}", strSpeed, advancedAspect[i]); 1155 /* if the diverg flags available is set and the diverg aspect 1156 array contains the entry then we will check this aspect. 1157 1158 If the diverg flag has not been set then we will check. 1159 */ 1160 log.debug("advanced aspect {}",advancedAspect[i]); 1161 if ((divergRoute && (divergFlagsAvailable) && (divergAspects.contains(i))) || ((divergRoute && !divergFlagsAvailable) || (!divergRoute)) && (nonDivergAspects.contains(i))) { 1162 log.debug("In list"); 1163 if ((strSpeed != null) && (!strSpeed.isEmpty())) { 1164 float speed = 0.0f; 1165 try { 1166 speed = Float.parseFloat(strSpeed); 1167 } catch (NumberFormatException nx) { 1168 // not a number, perhaps a name? 1169 try { 1170 speed = InstanceManager.getDefault(SignalSpeedMap.class).getSpeed(strSpeed); 1171 } catch (IllegalArgumentException ex) { 1172 // not a name either 1173 log.warn("Using speed = 0.0 because could not understand \"{}\"", strSpeed); 1174 } 1175 } 1176 //Integer state = Integer.parseInt(strSpeed); 1177 /* This pics out either the highest speed signal if there 1178 * is no block speed specified or the highest speed signal 1179 * that is under the minimum block speed. 1180 */ 1181 log.debug("{} signal state speed {} maxSigSpeed {} maxPathSpeed {}", destination.getDisplayName(), speed, maxSigSpeed, maxPathSpeed); 1182 if (maxPathSpeed == 0) { 1183 if (maxSigSpeed == -1) { 1184 log.debug("min speed on this route is equal to 0 so will set this as our max speed"); 1185 maxSigSpeed = speed; 1186 aspect = advancedAspect[i]; 1187 log.debug("Aspect to set is {}", aspect); 1188 } else if (speed > maxSigSpeed) { 1189 log.debug("new speed is faster than old will use this"); 1190 maxSigSpeed = speed; 1191 aspect = advancedAspect[i]; 1192 log.debug("Aspect to set is {}", aspect); 1193 } 1194 } else if ((speed > maxSigSpeed) && (maxSigSpeed < maxPathSpeed) && (speed <= maxPathSpeed)) { 1195 //Only set the speed to the lowest if the max speed is greater than the path speed 1196 //and the new speed is less than the last max speed 1197 log.debug("our minimum speed on this route is less than our state speed, we will set this as our max speed"); 1198 maxSigSpeed = speed; 1199 aspect = advancedAspect[i]; 1200 log.debug("Aspect to set is {}", aspect); 1201 } else if ((maxSigSpeed > maxPathSpeed) && (speed < maxSigSpeed)) { 1202 log.debug("our max signal speed is greater than our path speed on this route, our speed is less that the maxSigSpeed"); 1203 maxSigSpeed = speed; 1204 aspect = advancedAspect[i]; 1205 log.debug("Aspect to set is {}", aspect); 1206 1207 } else if (maxSigSpeed == -1) { 1208 log.debug("maxSigSpeed returned as -1"); 1209 maxSigSpeed = speed; 1210 aspect = advancedAspect[i]; 1211 log.debug("Aspect to set is {}", aspect); 1212 } 1213 } 1214 } 1215 } else { 1216 log.debug("Aspect has been disabled {}", advancedAspect[i]); 1217 } 1218 } 1219 } 1220 } 1221 if ((aspect != null) && (!aspect.isEmpty())) { 1222 log.debug("setMastAppearance setting aspect \"{}\"", aspect); 1223 String aspectSet = aspect; // for lambda 1224 try { 1225 ThreadingUtil.runOnLayout(() -> { 1226 getSourceMast().setAspect(aspectSet); 1227 }); 1228 } catch (Exception ex) { 1229 log.error("Exception while setting Signal Logic", ex); 1230 } 1231 return; 1232 } 1233 } 1234 log.debug("Aspect returned is not valid, setting stop"); 1235 ThreadingUtil.runOnLayout(() -> { 1236 getSourceMast().setAspect(stopAspect); 1237 }); 1238 } 1239 1240 /** 1241 * {@inheritDoc } 1242 */ 1243 @Override 1244 public void setConflictingLogic(SignalMast sm, LevelXing lx) { 1245 if (sm == null) { 1246 return; 1247 } 1248 log.debug("setConflicting logic mast {}", sm.getDisplayName()); 1249 if (sm == source) { 1250 log.debug("source is us so exit"); 1251 return; 1252 } 1253 Enumeration<SignalMast> en = destList.keys(); 1254 while (en.hasMoreElements()) { 1255 SignalMast dm = en.nextElement(); 1256 if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockAC())) { 1257 destList.get(dm).addAutoSignalMast(sm); 1258 } else if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockBD())) { 1259 destList.get(dm).addAutoSignalMast(sm); 1260 } else if (destList.get(dm).isAutoBlockIncluded(lx.getLayoutBlockAC())) { 1261 destList.get(dm).addAutoSignalMast(sm); 1262 } else if (destList.get(dm).isAutoBlockIncluded(lx.getLayoutBlockBD())) { 1263 destList.get(dm).addAutoSignalMast(sm); 1264 } else { 1265 log.debug("Block not found"); 1266 } 1267 } 1268 } 1269 1270 /** 1271 * {@inheritDoc } 1272 */ 1273 @Override 1274 public void removeConflictingLogic(SignalMast sm, LevelXing lx) { 1275 if (sm == source) { 1276 return; 1277 } 1278 Enumeration<SignalMast> en = destList.keys(); 1279 while (en.hasMoreElements()) { 1280 SignalMast dm = en.nextElement(); 1281 if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockAC())) { 1282 destList.get(dm).removeAutoSignalMast(sm); 1283 } else if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockBD())) { 1284 destList.get(dm).removeAutoSignalMast(sm); 1285 } 1286 } 1287 } 1288 1289 /** 1290 * Class to store SML properties for a destination mast paired with this 1291 * source mast. 1292 */ 1293 private class DestinationMast { 1294 1295 LayoutBlock destinationBlock = null; 1296 LayoutBlock protectingBlock = null; //this is the block that the source signal is protecting 1297 1298 List<NamedBeanSetting> userSetTurnouts = new ArrayList<>(0); 1299 Hashtable<Turnout, Integer> autoTurnouts = new Hashtable<>(0); 1300 //Hashtable<Turnout, Boolean> turnoutThroats = new Hashtable<Turnout, Boolean>(0); 1301 //Hashtable<Turnout, Boolean> autoTurnoutThroats = new Hashtable<Turnout, Boolean>(0); 1302 1303 List<NamedBeanSetting> userSetMasts = new ArrayList<>(0); 1304 Hashtable<SignalMast, String> autoMasts = new Hashtable<>(0); 1305 List<NamedBeanSetting> userSetSensors = new ArrayList<>(0); 1306 List<NamedBeanSetting> userSetBlocks = new ArrayList<>(0); 1307 boolean turnoutThrown = false; 1308 boolean permissiveBlock = false; 1309 boolean disposed = false; 1310 1311 List<LevelXing> blockInXings = new ArrayList<>(); 1312 1313 //autoBlocks are for those automatically generated by the system. 1314 LinkedHashMap<Block, Integer> autoBlocks = new LinkedHashMap<>(0); 1315 1316 List<Block> xingAutoBlocks = new ArrayList<>(0); 1317 List<Block> dblCrossoverAutoBlocks = new ArrayList<>(0); 1318 SignalMast destinationSignalMast; 1319 boolean active = false; 1320 boolean destMastInit = false; 1321 1322 float minimumBlockSpeed = 0.0f; 1323 1324 boolean useLayoutEditor = false; 1325 boolean useLayoutEditorTurnouts = false; 1326 boolean useLayoutEditorBlocks = false; 1327 boolean lockTurnouts = false; 1328 1329 NamedBeanHandle<Section> associatedSection = null; 1330 1331 DestinationMast(SignalMast destination) { 1332 this.destinationSignalMast = destination; 1333 if (destination.getAspect() == null) { 1334 try { 1335 destination.setAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER)); 1336 } catch (Exception ex) { 1337 log.error("Error while creating Signal Logic", ex); 1338 } 1339 } 1340 } 1341 1342 void updateDestinationMast(SignalMast newMast) { 1343 destinationSignalMast = newMast; 1344 if (destinationSignalMast.getAspect() == null) { 1345 try { 1346 destinationSignalMast.setAspect(destinationSignalMast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER)); 1347 } catch (Exception ex) { 1348 log.error("Error while creating Signal Logic", ex); 1349 } 1350 } 1351 } 1352 1353 LayoutBlock getProtectingBlock() { 1354 return protectingBlock; 1355 } 1356 1357 String getDisplayName() { 1358 return destinationSignalMast.getDisplayName(); 1359 } 1360 1361 String comment; 1362 1363 String getComment() { 1364 return comment; 1365 } 1366 1367 void setComment(String comment) { 1368 String old = this.comment; 1369 this.comment = comment; 1370 firePropertyChange(PROPERTY_COMMENT, old, comment); 1371 } 1372 1373 boolean isActive() { 1374 if (disposed) { 1375 log.error("checkState called even though this has been disposed of"); 1376 return false; 1377 } 1378 return active; 1379 } 1380 1381 float getMinimumSpeed() { 1382 return minimumBlockSpeed; 1383 } 1384 1385 boolean enable = true; 1386 1387 void setEnabled() { 1388 enable = true; 1389 firePropertyChange(PROPERTY_ENABLED, false, this.destinationSignalMast); 1390 } 1391 1392 void setDisabled() { 1393 enable = false; 1394 firePropertyChange(PROPERTY_ENABLED, true, this.destinationSignalMast); 1395 } 1396 1397 boolean isEnabled() { 1398 return enable; 1399 } 1400 1401 int store = STOREALL; 1402 1403 void setStore(int store) { 1404 this.store = store; 1405 } 1406 1407 int getStoreState() { 1408 return store; 1409 } 1410 1411 void setAssociatedSection(Section section) { 1412 if (section != null && (!useLayoutEditor || !useLayoutEditorBlocks)) { 1413 log.warn("This Logic {} to {} is not using the Layout Editor or its Blocks, the associated Section will not be populated correctly", source.getDisplayName(), destinationSignalMast.getDisplayName()); 1414 } 1415 if (section == null) { 1416 associatedSection = null; 1417 return; 1418 } 1419 associatedSection = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(section.getDisplayName(), section); 1420 if (!autoBlocks.isEmpty()) { // associatedSection is guaranteed to exist 1421 createSectionDetails(); 1422 } 1423 } 1424 1425 Section getAssociatedSection() { 1426 if (associatedSection != null) { 1427 return associatedSection.getBean(); 1428 } 1429 return null; 1430 } 1431 1432 void createSectionDetails() { 1433 getAssociatedSection().removeAllBlocksFromSection(); 1434 getAutoBlocksBetweenMasts().forEach(key -> { 1435 getAssociatedSection().addBlock(key); 1436 }); 1437 String dir = Path.decodeDirection(getFacingBlock().getNeighbourDirection(getProtectingBlock())); 1438 EntryPoint ep = new EntryPoint(getProtectingBlock().getBlock(), getFacingBlock().getBlock(), dir); 1439 ep.setTypeForward(); 1440 getAssociatedSection().addToForwardList(ep); 1441 1442 LayoutBlock proDestLBlock = InstanceManager.getDefault(LayoutBlockManager.class).getProtectedBlockByNamedBean(destinationSignalMast, destinationBlock.getMaxConnectedPanel()); 1443 if (proDestLBlock != null) { 1444 log.debug("Add protecting Block {}", proDestLBlock.getDisplayName()); 1445 dir = Path.decodeDirection(proDestLBlock.getNeighbourDirection(destinationBlock)); 1446 ep = new EntryPoint(destinationBlock.getBlock(), proDestLBlock.getBlock(), dir); 1447 ep.setTypeReverse(); 1448 getAssociatedSection().addToReverseList(ep); 1449 } else { 1450 log.debug(" ### Protecting Block not found ### "); 1451 } 1452 } 1453 1454 boolean isTurnoutLockAllowed() { 1455 return lockTurnouts; 1456 } 1457 1458 void allowTurnoutLock(boolean lock) { 1459 if (lockTurnouts == lock) { 1460 return; 1461 } 1462 if (!lock) { 1463 clearTurnoutLock(); 1464 } 1465 lockTurnouts = lock; 1466 } 1467 1468 void setTurnouts(Hashtable<NamedBeanHandle<Turnout>, Integer> turnouts) { 1469 if (this.userSetTurnouts != null) { 1470 userSetTurnouts.forEach(nbh -> 1471 nbh.getBean().removePropertyChangeListener(propertyTurnoutListener)); 1472 } 1473 destMastInit = false; 1474 if (turnouts == null) { 1475 userSetTurnouts = new ArrayList<>(0); 1476 } else { 1477 userSetTurnouts = new ArrayList<>(); 1478 Enumeration<NamedBeanHandle<Turnout>> e = turnouts.keys(); 1479 while (e.hasMoreElements()) { 1480 NamedBeanHandle<Turnout> nbh = e.nextElement(); 1481 NamedBeanSetting nbs = new NamedBeanSetting(nbh, turnouts.get(nbh)); 1482 userSetTurnouts.add(nbs); 1483 } 1484 } 1485 firePropertyChange(PROPERTY_TURNOUTS, null, this.destinationSignalMast); 1486 } 1487 1488 void setAutoTurnouts(Hashtable<Turnout, Integer> turnouts) { 1489 log.debug("{} called setAutoTurnouts with {}", destinationSignalMast.getDisplayName(), 1490 (turnouts != null ? "" + turnouts.size() + " turnouts in hash table" : "null hash table reference")); 1491 if (this.autoTurnouts != null) { 1492 Enumeration<Turnout> keys = this.autoTurnouts.keys(); 1493 while (keys.hasMoreElements()) { 1494 Turnout key = keys.nextElement(); 1495 key.removePropertyChangeListener(propertyTurnoutListener); 1496 } 1497 //minimumBlockSpeed = 0; 1498 } 1499 destMastInit = false; 1500 if (turnouts == null) { 1501 this.autoTurnouts = new Hashtable<>(0); 1502 } else { 1503 this.autoTurnouts = new Hashtable<>(turnouts); 1504 } 1505 firePropertyChange(PROPERTY_AUTO_TURNOUTS, null, this.destinationSignalMast); 1506 } 1507 1508 void setBlocks(Hashtable<Block, Integer> blocks) { 1509 log.debug("{} Set blocks called", destinationSignalMast.getDisplayName()); 1510 if (this.userSetBlocks != null) { 1511 userSetBlocks.forEach( nbh -> 1512 nbh.getBean().removePropertyChangeListener(propertyBlockListener)); 1513 } 1514 destMastInit = false; 1515 1516 userSetBlocks = new ArrayList<>(0); 1517 if (blocks != null) { 1518 userSetBlocks = new ArrayList<>(); 1519 Enumeration<Block> e = blocks.keys(); 1520 while (e.hasMoreElements()) { 1521 Block blk = e.nextElement(); 1522 NamedBeanHandle<?> nbh = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(blk.getDisplayName(), blk); 1523 NamedBeanSetting nbs = new NamedBeanSetting(nbh, blocks.get(blk)); 1524 userSetBlocks.add(nbs); 1525 } 1526 } 1527 firePropertyChange(PROPERTY_BLOCKS, null, this.destinationSignalMast); 1528 } 1529 1530 public void setAutoBlocks(LinkedHashMap<Block, Integer> blocks) { 1531 if (log.isDebugEnabled()) { 1532 log.debug("{} called setAutoBlocks with {}", destinationSignalMast.getDisplayName(), 1533 (blocks != null ? "" + blocks.size() + " blocks in hash table" : "null hash table reference")); 1534 } 1535 if (this.autoBlocks != null) { 1536 autoBlocks.keySet().forEach( key -> 1537 key.removePropertyChangeListener(propertyBlockListener)); 1538 } 1539 destMastInit = false; 1540 if (blocks == null) { 1541 this.autoBlocks = new LinkedHashMap<>(0); 1542 1543 } else { 1544 this.autoBlocks = new LinkedHashMap<>(blocks); 1545 //We shall remove the facing block in the list. 1546 if (facingBlock != null && autoBlocks.containsKey(facingBlock.getBlock())) { 1547 autoBlocks.remove(facingBlock.getBlock()); 1548 } 1549 if (getAssociatedSection() != null) { 1550 createSectionDetails(); 1551 } 1552 } 1553 firePropertyChange(PROPERTY_AUTO_BLOCKS, null, this.destinationSignalMast); 1554 } 1555 1556 void setMasts(Hashtable<SignalMast, String> masts) { 1557 if (this.userSetMasts != null) { 1558 userSetMasts.forEach( nbh -> 1559 nbh.getBean().removePropertyChangeListener(propertySignalMastListener)); 1560 } 1561 1562 destMastInit = false; 1563 1564 if (masts == null) { 1565 userSetMasts = new ArrayList<>(0); 1566 } else { 1567 userSetMasts = new ArrayList<>(); 1568 Enumeration<SignalMast> e = masts.keys(); 1569 while (e.hasMoreElements()) { 1570 SignalMast mast = e.nextElement(); 1571 NamedBeanHandle<?> nbh = InstanceManager.getDefault(NamedBeanHandleManager.class) 1572 .getNamedBeanHandle(mast.getDisplayName(), mast); 1573 NamedBeanSetting nbs = new NamedBeanSetting(nbh, masts.get(mast)); 1574 userSetMasts.add(nbs); 1575 } 1576 } 1577 firePropertyChange(PROPERTY_MASTS, null, this.destinationSignalMast); 1578 } 1579 1580 /** 1581 * 1582 * @param newAutoMasts Hashtable of signal masts and set to Aspects 1583 * @param overwrite When true, replace an existing autoMasts list in 1584 * the SML 1585 */ 1586 void setAutoMasts(Hashtable<SignalMast, String> newAutoMasts, boolean overwrite) { 1587 log.debug("{} setAutoMast Called", destinationSignalMast.getDisplayName()); 1588 if (this.autoMasts != null) { 1589 Enumeration<SignalMast> keys = this.autoMasts.keys(); 1590 while (keys.hasMoreElements()) { 1591 SignalMast key = keys.nextElement(); 1592 key.removePropertyChangeListener(propertySignalMastListener); 1593 } 1594 //minimumBlockSpeed = 0; 1595 } 1596 destMastInit = false; 1597 if (overwrite) { 1598 if (newAutoMasts == null) { 1599 this.autoMasts = new Hashtable<>(0); 1600 } else { 1601 this.autoMasts = new Hashtable<>(newAutoMasts); 1602 } 1603 } else { 1604 if (newAutoMasts == null) { 1605 this.autoMasts = new Hashtable<>(0); 1606 } else { 1607 Enumeration<SignalMast> keys = newAutoMasts.keys(); 1608 while (keys.hasMoreElements()) { 1609 SignalMast key = keys.nextElement(); 1610 this.autoMasts.put(key, newAutoMasts.get(key)); 1611 } 1612 } 1613 } 1614 //kick off the process to add back in signal masts at crossings. 1615 for (int i = 0; i < blockInXings.size(); i++) { 1616 blockInXings.get(i).addSignalMastLogic(source); 1617 } 1618 1619 firePropertyChange(PROPERTY_AUTO_MASTS, null, this.destinationSignalMast); 1620 } 1621 1622 void setSensors(Hashtable<NamedBeanHandle<Sensor>, Integer> sensors) { 1623 if (this.userSetSensors != null) { 1624 userSetSensors.forEach(nbh -> 1625 nbh.getBean().removePropertyChangeListener(propertySensorListener)); 1626 } 1627 destMastInit = false; 1628 1629 if (sensors == null) { 1630 userSetSensors = new ArrayList<>(0); 1631 } else { 1632 userSetSensors = new ArrayList<>(); 1633 Enumeration<NamedBeanHandle<Sensor>> e = sensors.keys(); 1634 while (e.hasMoreElements()) { 1635 NamedBeanHandle<Sensor> nbh = e.nextElement(); 1636 NamedBeanSetting nbs = new NamedBeanSetting(nbh, sensors.get(nbh)); 1637 userSetSensors.add(nbs); 1638 } 1639 } 1640 firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast); 1641 } 1642 1643 void addSensor(NamedBeanHandle<Sensor> sen, int state) { 1644 for (NamedBeanSetting nbh : userSetSensors) { 1645 if (nbh.getBean().equals(sen.getBean())) { 1646 return; 1647 } 1648 } 1649 sen.getBean().addPropertyChangeListener(propertySensorListener); 1650 NamedBeanSetting nbs = new NamedBeanSetting(sen, state); 1651 userSetSensors.add(nbs); 1652 firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast); 1653 } 1654 1655// not used now, preserved for later use 1656// void removeSensor(NamedBeanHandle<Sensor> sen) { 1657// for (NamedBeanSetting nbh : userSetSensors) { 1658// if (nbh.getBean().equals(sen.getBean())) { 1659// sen.getBean().removePropertyChangeListener(propertySensorListener); 1660// userSetSensors.remove(nbh); 1661// firePropertyChange("sensors", null, this.destination); 1662// return; 1663// } 1664// } 1665// } 1666 1667 void removeSensor(Sensor sen) { 1668 for (NamedBeanSetting nbh : userSetSensors) { 1669 if (nbh.getBean().equals(sen)) { 1670 sen.removePropertyChangeListener(propertySensorListener); 1671 userSetSensors.remove(nbh); 1672 firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast); 1673 return; 1674 } 1675 } 1676 } 1677 1678 List<Block> getBlocks() { 1679 List<Block> out = new ArrayList<>(); 1680 userSetBlocks.forEach( nbh -> out.add((Block) nbh.getBean())); 1681 return out; 1682 } 1683 1684 List<Block> getAutoBlocks() { 1685 List<Block> out = new ArrayList<>(); 1686 Set<Block> blockKeys = autoBlocks.keySet(); 1687 blockKeys.forEach(key -> out.add(key)); 1688 return out; 1689 } 1690 1691 List<Block> getAutoBlocksBetweenMasts() { 1692 if (destList.get(destinationSignalMast).xingAutoBlocks.isEmpty() 1693 && destList.get(destinationSignalMast).dblCrossoverAutoBlocks.isEmpty()) { 1694 return getAutoBlocks(); 1695 } 1696 List<Block> returnList = getAutoBlocks(); 1697 for (Block blk : getAutoBlocks()) { 1698 if (xingAutoBlocks.contains(blk)) { 1699 returnList.remove(blk); 1700 } 1701 if (dblCrossoverAutoBlocks.contains(blk)) { 1702 returnList.remove(blk); 1703 } 1704 } 1705 1706 return returnList; 1707 } 1708 1709 List<Turnout> getTurnouts() { 1710 List<Turnout> out = new ArrayList<>(); 1711 userSetTurnouts.forEach( nbh -> out.add((Turnout) nbh.getBean())); 1712 return out; 1713 } 1714 1715 void removeTurnout(Turnout turn) { 1716 Iterator<NamedBeanSetting> nbh = userSetTurnouts.iterator(); 1717 while (nbh.hasNext()) { 1718 NamedBeanSetting i = nbh.next(); 1719 if (i.getBean().equals(turn)) { 1720 turn.removePropertyChangeListener(propertyTurnoutListener); 1721 nbh.remove(); 1722 firePropertyChange(PROPERTY_TURNOUTS, null, this.destinationSignalMast); 1723 } 1724 } 1725 } 1726 1727 @SuppressWarnings("unchecked") // (NamedBeanHandle<Turnout>) nbh.getNamedBean() is unchecked cast 1728 List<NamedBeanHandle<Turnout>> getNamedTurnouts() { 1729 List<NamedBeanHandle<Turnout>> out = new ArrayList<>(); 1730 userSetTurnouts.forEach(nbh -> 1731 out.add((NamedBeanHandle<Turnout>) nbh.getNamedBean())); 1732 return out; 1733 } 1734 1735 List<Turnout> getAutoTurnouts() { 1736 List<Turnout> out = new ArrayList<>(); 1737 Enumeration<Turnout> en = autoTurnouts.keys(); 1738 while (en.hasMoreElements()) { 1739 out.add(en.nextElement()); 1740 } 1741 return out; 1742 } 1743 1744 List<SignalMast> getSignalMasts() { 1745 List<SignalMast> out = new ArrayList<>(); 1746 userSetMasts.forEach( nbh -> out.add((SignalMast) nbh.getBean())); 1747 return out; 1748 } 1749 1750 List<SignalMast> getAutoSignalMasts() { 1751 List<SignalMast> out = new ArrayList<>(); 1752 Enumeration<SignalMast> en = autoMasts.keys(); 1753 while (en.hasMoreElements()) { 1754 out.add(en.nextElement()); 1755 } 1756 return out; 1757 } 1758 1759 List<Sensor> getSensors() { 1760 List<Sensor> out = new ArrayList<>(); 1761 userSetSensors.forEach( nbh -> out.add((Sensor) nbh.getBean())); 1762 return out; 1763 } 1764 1765 @SuppressWarnings("unchecked") // (NamedBeanHandle<Sensor>) nbh.getNamedBean() is unchecked cast 1766 List<NamedBeanHandle<Sensor>> getNamedSensors() { 1767 List<NamedBeanHandle<Sensor>> out = new ArrayList<>(); 1768 userSetSensors.forEach( nbh -> out.add((NamedBeanHandle<Sensor>) nbh.getNamedBean())); 1769 return out; 1770 } 1771 1772 boolean isBlockIncluded(Block block) { 1773 return userSetBlocks.stream().anyMatch(nbh -> (nbh.getBean().equals(block))); 1774 } 1775 1776 boolean isAutoBlockIncluded(LayoutBlock block) { 1777 if (block != null) { 1778 return autoBlocks.containsKey(block.getBlock()); 1779 } 1780 return false; 1781 } 1782 1783 boolean isAutoBlockIncluded(Block block) { 1784 return autoBlocks.containsKey(block); 1785 } 1786 1787 boolean isBlockIncluded(LayoutBlock block) { 1788 return userSetBlocks.stream().anyMatch(nbh -> (nbh.getBean().equals(block.getBlock()))); 1789 } 1790 1791 boolean isTurnoutIncluded(Turnout turnout) { 1792 return userSetTurnouts.stream().anyMatch(nbh -> (nbh.getBean().equals(turnout))); 1793 } 1794 1795 boolean isSensorIncluded(Sensor sensor) { 1796 return userSetSensors.stream().anyMatch(nbh -> (nbh.getBean().equals(sensor))); 1797 } 1798 1799 boolean isSignalMastIncluded(SignalMast signal) { 1800 return userSetMasts.stream().anyMatch(nbh -> (nbh.getBean().equals(signal))); 1801 } 1802 1803 int getAutoBlockState(Block block) { 1804 if (autoBlocks == null) { 1805 return -1; 1806 } 1807 return autoBlocks.get(block); 1808 } 1809 1810 int getBlockState(Block block) { 1811 if (userSetBlocks == null) { 1812 return -1; 1813 } 1814 for (NamedBeanSetting nbh : userSetBlocks) { 1815 if (nbh.getBean().equals(block)) { 1816 return nbh.getSetting(); 1817 } 1818 } 1819 return -1; 1820 } 1821 1822 int getSensorState(Sensor sensor) { 1823 if (userSetSensors == null) { 1824 return -1; 1825 } 1826 for (NamedBeanSetting nbh : userSetSensors) { 1827 if (nbh.getBean().equals(sensor)) { 1828 return nbh.getSetting(); 1829 } 1830 } 1831 return -1; 1832 } 1833 1834 int getTurnoutState(Turnout turnout) { 1835 if (userSetTurnouts == null) { 1836 return -1; 1837 } 1838 for (NamedBeanSetting nbh : userSetTurnouts) { 1839 if (nbh.getBean().equals(turnout)) { 1840 return nbh.getSetting(); 1841 } 1842 } 1843 return -1; 1844 } 1845 1846 int getAutoTurnoutState(Turnout turnout) { 1847 if (autoTurnouts == null) { 1848 return -1; 1849 } 1850 if (autoTurnouts.containsKey(turnout)) { 1851 return autoTurnouts.get(turnout); 1852 } 1853 return -1; 1854 } 1855 1856 String getSignalMastState(SignalMast mast) { 1857 if (userSetMasts == null) { 1858 return null; 1859 } 1860 for (NamedBeanSetting nbh : userSetMasts) { 1861 if (nbh.getBean().equals(mast)) { 1862 return nbh.getStringSetting(); 1863 } 1864 } 1865 return null; 1866 } 1867 1868 String getAutoSignalMastState(SignalMast mast) { 1869 if (autoMasts == null) { 1870 return null; 1871 } 1872 return autoMasts.get(mast); 1873 } 1874 1875 // the following 2 methods are not supplied in the implementation 1876 boolean inWait = false; 1877 1878 /* 1879 * Before going active or checking that we can go active, wait 1880 * for things to settle down to help prevent a race condition. 1881 */ 1882 void checkState() { 1883 if (disposed) { 1884 log.error("checkState called even though this has been disposed of {}", 1885 getSourceMast().getDisplayName()); 1886 return; 1887 } 1888 1889 if (!enable) { 1890 return; 1891 } 1892 if (inWait) { 1893 return; 1894 } 1895 1896 log.debug("check Signal Dest State called"); 1897 inWait = true; 1898 1899 // The next line forces a single initialization of InstanceManager.getDefault(SignalMastLogicManager.class) 1900 // before launching parallel threads 1901 int tempDelay = InstanceManager.getDefault(SignalMastLogicManager.class).getSignalLogicDelay(); 1902 1903 ThreadingUtil.runOnLayoutDelayed( 1904 () -> { 1905 checkStateDetails(); 1906 inWait = false; 1907 }, tempDelay 1908 ); 1909 } 1910 1911 /** 1912 * Check the details of this source-destination signal mast logic pair. 1913 * Steps through every sensor, turnout etc. before setting the SML 1914 * Aspect on the source mast via { 1915 * 1916 * @see #setSignalAppearance } and { 1917 * @see #setMastAppearance } 1918 */ 1919 private void checkStateDetails() { 1920 turnoutThrown = false; 1921 permissiveBlock = false; 1922 if (disposed) { 1923 log.error("checkStateDetails called even though this has been disposed of {} {}", 1924 getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName()); 1925 return; 1926 } 1927 if (!enable) { 1928 return; 1929 } 1930 log.debug("From {} to {} internal check state", getSourceMast().getDisplayName(), 1931 destinationSignalMast.getDisplayName()); 1932 active = false; 1933 if ((useLayoutEditor) && (autoTurnouts.isEmpty()) && (autoBlocks.isEmpty())) { 1934 return; 1935 } 1936 boolean state = true; 1937 Enumeration<Turnout> keys = autoTurnouts.keys(); 1938 while (keys.hasMoreElements()) { 1939 Turnout key = keys.nextElement(); 1940 if (key.getKnownState() != autoTurnouts.get(key)) { 1941 if (key.getState() != autoTurnouts.get(key)) { 1942 if (isTurnoutIncluded(key)) { 1943 if (key.getState() != getTurnoutState(key)) { 1944 state = false; 1945 } else if (key.getState() == Turnout.THROWN) { 1946 turnoutThrown = true; 1947 } 1948 } else { 1949 state = false; 1950 } 1951 } 1952 } else if (key.getState() == Turnout.THROWN) { 1953 turnoutThrown = true; 1954 } 1955 } 1956 1957 for (NamedBeanSetting nbh : userSetTurnouts) { 1958 Turnout key = (Turnout) nbh.getBean(); 1959 if (key.getKnownState() != nbh.getSetting()) { 1960 state = false; 1961 } else if (key.getState() == Turnout.THROWN) { 1962 turnoutThrown = true; 1963 } 1964 } 1965 1966 Enumeration<SignalMast> mastKeys = autoMasts.keys(); 1967 while (mastKeys.hasMoreElements()) { 1968 SignalMast key = mastKeys.nextElement(); 1969 String aspect = key.getAspect(); 1970 log.debug("key {} {} {}", key.getDisplayName(), aspect, autoMasts.get(key)); 1971 if ((aspect != null) && (!aspect.equals(autoMasts.get(key)))) { 1972 if (isSignalMastIncluded(key)) { 1973 //Basically if we have a blank aspect, we don't care about the state of the signalmast 1974 if (!getSignalMastState(key).isEmpty()) { 1975 if (!aspect.equals(getSignalMastState(key))) { 1976 state = false; 1977 } 1978 } 1979 } else { 1980 state = false; 1981 } 1982 } 1983 } 1984 for (NamedBeanSetting nbh : userSetMasts) { 1985 SignalMast key = (SignalMast) nbh.getBean(); 1986 String aspect = key.getAspect(); 1987 if ((aspect == null) || (!aspect.equals(nbh.getStringSetting()))) { 1988 state = false; 1989 } 1990 } 1991 1992 for (NamedBeanSetting nbh : userSetSensors) { 1993 Sensor key = (Sensor) nbh.getBean(); 1994 if (key.getKnownState() != nbh.getSetting()) { 1995 state = false; 1996 } 1997 } 1998 1999 for (Map.Entry<Block, Integer> entry : this.autoBlocks.entrySet()) { 2000 log.debug(" entry {} {} {}", entry.getKey().getDisplayName(), 2001 entry.getKey().getState(), entry.getValue()); 2002 if (entry.getKey().getState() != autoBlocks.get(entry.getKey())) { 2003 if (isBlockIncluded(entry.getKey())) { 2004 if (getBlockState(entry.getKey()) != 0x03) { 2005 if (entry.getKey().getState() != getBlockState(entry.getKey())) { 2006 if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) { 2007 permissiveBlock = true; 2008 } else { 2009 state = false; 2010 } 2011 } 2012 } 2013 } else { 2014 if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) { 2015 permissiveBlock = true; 2016 } else if (entry.getKey().getState() == Block.UNDETECTED) { 2017 log.debug("Block {} is UNDETECTED so treat as unoccupied", entry.getKey().getDisplayName()); 2018 } else { 2019 state = false; 2020 } 2021 } 2022 } 2023 } 2024 2025 for (NamedBeanSetting nbh : userSetBlocks) { 2026 Block key = (Block) nbh.getBean(); 2027 if (nbh.getSetting() != 0x03) { 2028 if (key.getState() != nbh.getSetting()) { 2029 if (key.getState() == Block.OCCUPIED && key.getPermissiveWorking()) { 2030 permissiveBlock = true; 2031 } else { 2032 state = false; 2033 } 2034 } 2035 } 2036 } 2037 if (permissiveBlock 2038 /*If a block has been found to be permissive, but the source signalmast 2039 does not support a call-on/permissive aspect then the route can not be set*/ 2040 && getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE) == null) { 2041 state = false; 2042 } 2043 2044 /*This check is purely for use with the dispatcher, it will check to see if any of the blocks are set to "useExtraColor" 2045 which is a means to determine if the block is in a section that is occupied and it not ours thus we can set the signal to danger.*/ 2046 if (state && getAssociatedSection() != null 2047 && InstanceManager.getNullableDefault(jmri.jmrit.dispatcher.DispatcherFrame.class) != null 2048 && InstanceManager.getNullableDefault(LayoutBlockManager.class) != null 2049 && getAssociatedSection().getState() != Section.FORWARD) { 2050 2051 LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class); 2052 for (Block key : autoBlocks.keySet()) { 2053 LayoutBlock lb = lbm.getLayoutBlock(key); 2054 if (lb != null && lb.getUseExtraColor()) { 2055 state = false; 2056 break; 2057 } 2058 } 2059 if (!state) { 2060 for (NamedBeanSetting nbh : userSetBlocks) { 2061 Block key = (Block) nbh.getBean(); 2062 LayoutBlock lb = lbm.getLayoutBlock(key); 2063 if (lb != null && lb.getUseExtraColor()) { 2064 state = false; 2065 break; 2066 } 2067 } 2068 } 2069 } 2070 2071 if (!state) { 2072 turnoutThrown = false; 2073 permissiveBlock = false; 2074 } 2075 2076 active = state; 2077 ThreadingUtil.runOnLayout(() -> { 2078 setSignalAppearance(); 2079 }); 2080 } 2081 2082 /** 2083 * Set up this source-destination signal mast logic pair. Steps through 2084 * every list defined on the source mast. 2085 */ 2086 void initialise() { 2087 if ((destMastInit) || (disposed)) { 2088 return; 2089 } 2090 2091 active = false; 2092 turnoutThrown = false; 2093 permissiveBlock = false; 2094 boolean routeclear = true; 2095 if ((useLayoutEditor) && (autoTurnouts.isEmpty()) && (autoBlocks.isEmpty()) && (autoMasts.isEmpty())) { 2096 return; 2097 } 2098 2099 calculateSpeed(); 2100 2101 Enumeration<Turnout> keys = autoTurnouts.keys(); 2102 while (keys.hasMoreElements()) { 2103 Turnout key = keys.nextElement(); 2104 key.addPropertyChangeListener(propertyTurnoutListener); 2105 2106 if (key.getKnownState() != autoTurnouts.get(key)) { 2107 if (key.getState() != autoTurnouts.get(key)) { 2108 if (isTurnoutIncluded(key)) { 2109 if (key.getState() != getTurnoutState(key)) { 2110 routeclear = false; 2111 } else if (key.getState() == Turnout.THROWN) { 2112 turnoutThrown = true; 2113 } 2114 } else { 2115 routeclear = false; 2116 } 2117 } 2118 } else if (key.getState() == Turnout.THROWN) { 2119 turnoutThrown = true; 2120 } 2121 } 2122 2123 for (NamedBeanSetting nbh : userSetTurnouts) { 2124 Turnout key = (Turnout) nbh.getBean(); 2125 key.addPropertyChangeListener(propertyTurnoutListener, nbh.getBeanName(), 2126 "Signal Mast Logic:" + source.getDisplayName() + " to " + destinationSignalMast.getDisplayName()); 2127 if (key.getKnownState() != nbh.getSetting()) { 2128 routeclear = false; 2129 } else if (key.getState() == Turnout.THROWN) { 2130 turnoutThrown = true; 2131 } 2132 } 2133 2134 Enumeration<SignalMast> mastKeys = autoMasts.keys(); 2135 while (mastKeys.hasMoreElements()) { 2136 SignalMast key = mastKeys.nextElement(); 2137 log.debug("{} auto mast add list {}", destinationSignalMast.getDisplayName(), key.getDisplayName()); 2138 key.addPropertyChangeListener(propertySignalMastListener); 2139 String aspect = key.getAspect(); 2140 if ( aspect != null && !aspect.equals(autoMasts.get(key))) { 2141 if (isSignalMastIncluded(key)) { 2142 if (aspect.equals(getSignalMastState(key))) { 2143 routeclear = false; 2144 } 2145 } else { 2146 routeclear = false; 2147 } 2148 } 2149 } 2150 2151 for (NamedBeanSetting nbh : userSetMasts) { 2152 SignalMast key = (SignalMast) nbh.getBean(); 2153 key.addPropertyChangeListener(propertySignalMastListener); 2154 String aspect = key.getAspect(); 2155 log.debug("mast '{}' key aspect '{}'", destinationSignalMast.getDisplayName(), aspect); 2156 if ((aspect == null) || (!aspect.equals(nbh.getStringSetting()))) { 2157 routeclear = false; 2158 } 2159 } 2160 for (NamedBeanSetting nbh : userSetSensors) { 2161 Sensor sensor = (Sensor) nbh.getBean(); 2162 sensor.addPropertyChangeListener(propertySensorListener, nbh.getBeanName(), 2163 "Signal Mast Logic:" + source.getDisplayName() + " to " + destinationSignalMast.getDisplayName()); 2164 if (sensor.getKnownState() != nbh.getSetting()) { 2165 routeclear = false; 2166 } 2167 } 2168 2169 for (Map.Entry<Block, Integer> entry : this.autoBlocks.entrySet()) { 2170 log.debug("{} auto block add list {}", destinationSignalMast.getDisplayName(), 2171 entry.getKey().getDisplayName()); 2172 entry.getKey().addPropertyChangeListener(propertyBlockListener); 2173 if (entry.getKey().getState() != entry.getValue()) { 2174 if (isBlockIncluded(entry.getKey())) { 2175 if (entry.getKey().getState() != getBlockState(entry.getKey())) { 2176 if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) { 2177 permissiveBlock = true; 2178 } else { 2179 routeclear = false; 2180 } 2181 } 2182 } else { 2183 if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) { 2184 permissiveBlock = true; 2185 } else if (entry.getKey().getState() == Block.UNDETECTED) { 2186 log.debug("Block {} is UNDETECTED so treat as unoccupied", entry.getKey().getDisplayName()); 2187 } else { 2188 routeclear = false; 2189 } 2190 } 2191 } 2192 } 2193 2194 for (NamedBeanSetting nbh : userSetBlocks) { 2195 Block key = (Block) nbh.getBean(); 2196 key.addPropertyChangeListener(propertyBlockListener); 2197 if (key.getState() != getBlockState(key)) { 2198 if (key.getState() == Block.OCCUPIED && key.getPermissiveWorking()) { 2199 permissiveBlock = true; 2200 } else { 2201 routeclear = false; 2202 } 2203 } 2204 } 2205 if ( permissiveBlock 2206 /* If a block has been found to be permissive, but the source signalmast 2207 does not support a call-on/permissive aspect then the route can not be set */ 2208 && getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE) == null) { 2209 routeclear = false; 2210 } 2211 if (routeclear) { 2212 active = true; 2213 setSignalAppearance(); 2214 } else { 2215 permissiveBlock = false; 2216 turnoutThrown = false; 2217 } 2218 destMastInit = true; 2219 } 2220 2221 void useLayoutEditor(boolean boo) throws JmriException { 2222 log.debug("{} called useLayoutEditor({}), is {}", 2223 destinationSignalMast.getDisplayName(), boo, useLayoutEditor); 2224 if (useLayoutEditor == boo) { 2225 return; 2226 } 2227 useLayoutEditor = boo; 2228 if ((boo) && (InstanceManager.getDefault(LayoutBlockManager.class).routingStablised())) { 2229 // JmriException considered normal if there is no valid path using the layout editor. 2230 setupLayoutEditorDetails(); 2231 } else { 2232 destinationBlock = null; 2233 facingBlock = null; 2234 protectingBlock = null; 2235 setAutoBlocks(null); 2236 setAutoTurnouts(null); 2237 } 2238 } 2239 2240 void useLayoutEditorDetails(boolean turnouts, boolean blocks) throws JmriException { 2241 log.debug("{} use layout editor details called {}", 2242 destinationSignalMast.getDisplayName(), useLayoutEditor); 2243 useLayoutEditorTurnouts = turnouts; 2244 useLayoutEditorBlocks = blocks; 2245 if ((useLayoutEditor) && (InstanceManager.getDefault(LayoutBlockManager.class).routingStablised())) { 2246 // JmriException considered normal if there is no valid path using the Layout Editor. 2247 setupLayoutEditorDetails(); 2248 } 2249 } 2250 2251 void setupLayoutEditorDetails() throws JmriException { 2252 log.debug("setupLayoutEditorDetails: useLayoutEditor={} disposed={}", useLayoutEditor, disposed); 2253 if ((!useLayoutEditor) || (disposed)) { 2254 return; 2255 } 2256 LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class); 2257 if ( destinationBlock != null) { 2258 log.debug("{} Set use layout editor", destinationSignalMast.getDisplayName()); 2259 } 2260 Set<LayoutEditor> layout = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class); 2261 List<LayoutBlock> protectingBlocks = new ArrayList<>(); 2262 // We don't care which Layout Editor panel the signal mast is on, just so long as 2263 // the routing is done via layout blocks. 2264 remoteProtectingBlock = null; 2265 for (int i = 0; i < layout.size(); i++) { 2266 log.debug("{} Layout name {}", destinationSignalMast.getDisplayName(), editor ); 2267 if (facingBlock == null) { 2268 facingBlock = lbm.getFacingBlockByNamedBean(getSourceMast(), editor); 2269 } 2270 if (protectingBlock == null && protectingBlocks.isEmpty()) { 2271 //This is wrong 2272 protectingBlocks = lbm.getProtectingBlocksByNamedBean(getSourceMast(), editor); 2273 } 2274 if (destinationBlock == null) { 2275 destinationBlock = lbm.getFacingBlockByNamedBean(destinationSignalMast, editor); 2276 } 2277 if (remoteProtectingBlock == null) { 2278 remoteProtectingBlock = lbm.getProtectedBlockByNamedBean(destinationSignalMast, editor); 2279 } 2280 } 2281 // At this point, if we are not using the Layout Editor turnout or block 2282 // details then there is no point in trying to gather them. 2283 if ((!useLayoutEditorTurnouts) && (!useLayoutEditorBlocks)) { 2284 return; 2285 } 2286 if (facingBlock == null) { 2287 log.error("No facing block found for source mast {}", getSourceMast().getDisplayName()); 2288 throw new JmriException("No facing block found for source mast " + getSourceMast().getDisplayName()); 2289 } 2290 if (destinationBlock == null) { 2291 log.error("No facing block found for destination mast {}", destinationSignalMast.getDisplayName()); 2292 throw new JmriException("No facing block found for destination mast " + destinationSignalMast.getDisplayName()); 2293 } 2294 List<LayoutBlock> lblks = new ArrayList<>(); 2295 if (protectingBlock == null) { 2296 log.debug("protecting block is null"); 2297 String pBlkNames = ""; 2298 StringBuffer lBlksNamesBuf = new StringBuffer(); 2299 for (LayoutBlock pBlk : protectingBlocks) { 2300 log.debug("checking layoutBlock {}", pBlk.getDisplayName()); 2301 pBlkNames = pBlkNames + pBlk.getDisplayName() + " (" + lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock, pBlk, destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST) + "), "; 2302 if (lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock, pBlk, destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST)) { 2303 try { 2304 lblks = lbm.getLayoutBlockConnectivityTools().getLayoutBlocks(facingBlock, destinationBlock, pBlk, true, LayoutBlockConnectivityTools.Routing.MASTTOMAST); 2305 protectingBlock = pBlk; 2306 log.debug("building path names..."); 2307 for (LayoutBlock lBlk : lblks) { 2308 lBlksNamesBuf.append(" "); 2309 lBlksNamesBuf.append(lBlk.getDisplayName()); 2310 } 2311 break; 2312 } catch (JmriException ee) { 2313 log.debug("path not found this time"); 2314 } 2315 } 2316 } 2317 String lBlksNames = new String(lBlksNamesBuf); 2318 2319 if (protectingBlock == null) { 2320 throw new JmriException("Path not valid, protecting block is null. Protecting block: " + pBlkNames 2321 + " not connected to " + facingBlock.getDisplayName() + ". Layout block names: " + lBlksNames); 2322 } 2323 } 2324 if (!lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock,protectingBlock, 2325 destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST)) { 2326 throw new JmriException("Path not valid, destination check failed."); 2327 } 2328 if (log.isDebugEnabled()) { 2329 log.debug("{} face {}", destinationSignalMast.getDisplayName(), facingBlock); 2330 log.debug("{} prot {}", destinationSignalMast.getDisplayName(), protectingBlock); 2331 log.debug("{} dest {}", destinationSignalMast.getDisplayName(), destinationBlock); 2332 } 2333 2334 if (destinationBlock != null && protectingBlock != null && facingBlock != null) { 2335 setAutoMasts(null, true); 2336 if (log.isDebugEnabled()) { 2337 log.debug("{} face {}", destinationSignalMast.getDisplayName(), facingBlock.getDisplayName()); 2338 log.debug("{} prot {}", destinationSignalMast.getDisplayName(), protectingBlock.getDisplayName()); 2339 log.debug("{} dest {}", destinationSignalMast.getDisplayName(), destinationBlock.getDisplayName()); 2340 } 2341 2342 try { 2343 lblks = lbm.getLayoutBlockConnectivityTools().getLayoutBlocks( 2344 facingBlock, destinationBlock, protectingBlock, 2345 true, LayoutBlockConnectivityTools.Routing.MASTTOMAST); 2346 } catch (JmriException ee) { 2347 log.error("No blocks found by the layout editor for pair {}-{}", 2348 source.getDisplayName(), destinationSignalMast.getDisplayName()); 2349 } 2350 LinkedHashMap<Block, Integer> block = setupLayoutEditorTurnoutDetails(lblks); 2351 2352 for (int i = 0; i < blockInXings.size(); i++) { 2353 blockInXings.get(i).removeSignalMastLogic(source); 2354 } 2355 blockInXings = new ArrayList<>(0); 2356 xingAutoBlocks = new ArrayList<>(0); 2357 for (LayoutEditor lay : layout) { 2358 for (LevelXing levelXing : lay.getLevelXings()) { 2359 //Looking for a crossing that both layout blocks defined and they are individual. 2360 if ((levelXing.getLayoutBlockAC() != null) 2361 && (levelXing.getLayoutBlockBD() != null) 2362 && (levelXing.getLayoutBlockAC() != levelXing.getLayoutBlockBD())) { 2363 if (lblks.contains(levelXing.getLayoutBlockAC()) && 2364 levelXing.getLayoutBlockAC() != facingBlock) { // Don't include the facing xing blocks 2365 block.put(levelXing.getLayoutBlockBD().getBlock(), Block.UNOCCUPIED); 2366 xingAutoBlocks.add(levelXing.getLayoutBlockBD().getBlock()); 2367 blockInXings.add(levelXing); 2368 } else if (lblks.contains(levelXing.getLayoutBlockBD()) && 2369 levelXing.getLayoutBlockBD() != facingBlock) { // Don't include the facing xing blocks 2370 block.put(levelXing.getLayoutBlockAC().getBlock(), Block.UNOCCUPIED); 2371 xingAutoBlocks.add(levelXing.getLayoutBlockAC().getBlock()); 2372 blockInXings.add(levelXing); 2373 } 2374 } 2375 } 2376 } 2377 if (useLayoutEditorBlocks) { 2378 setAutoBlocks(block); 2379 } else { 2380 setAutoBlocks(null); 2381 } 2382 if (!useLayoutEditorTurnouts) { 2383 setAutoTurnouts(null); 2384 } 2385 2386 setupAutoSignalMast(null, false); 2387 } 2388 initialise(); 2389 } 2390 2391 /** 2392 * From a list of Layout Blocks, search for included Turnouts and their 2393 * Set To state. 2394 * 2395 * @param lblks List of Layout Blocks 2396 * @return a list of block - turnout state pairs 2397 */ 2398 LinkedHashMap<Block, Integer> setupLayoutEditorTurnoutDetails(List<LayoutBlock> lblks) { 2399 ConnectivityUtil connection; 2400 List<LayoutTrackExpectedState<LayoutTurnout>> turnoutList; 2401 Hashtable<Turnout, Integer> turnoutSettings = new Hashtable<>(); 2402 LinkedHashMap<Block, Integer> block = new LinkedHashMap<>(); 2403 for (int i = 0; i < lblks.size(); i++) { 2404 log.debug("layoutblock {}",lblks.get(i).getDisplayName()); 2405 block.put(lblks.get(i).getBlock(), Block.UNOCCUPIED); 2406 if ((i > 0)) { 2407 int nxtBlk = i + 1; 2408 int preBlk = i - 1; 2409 if (i == lblks.size() - 1) { 2410 nxtBlk = i; 2411 } 2412 //We use the best connectivity for the current block. 2413 connection = new ConnectivityUtil(lblks.get(i).getMaxConnectedPanel()); 2414 if (i == lblks.size() - 1 && remoteProtectingBlock != null) { 2415 turnoutList = connection.getTurnoutList(lblks.get(i) 2416 .getBlock(), lblks.get(preBlk).getBlock(), remoteProtectingBlock.getBlock()); 2417 }else{ 2418 turnoutList = connection.getTurnoutList(lblks.get(i) 2419 .getBlock(), lblks.get(preBlk).getBlock(), lblks.get(nxtBlk).getBlock()); 2420 } 2421 for (int x = 0; x < turnoutList.size(); x++) { 2422 LayoutTurnout lt = turnoutList.get(x).getObject(); 2423 if (lt instanceof LayoutSlip) { 2424 LayoutSlip ls = (LayoutSlip) lt; 2425 int slipState = turnoutList.get(x).getExpectedState(); 2426 int taState = ls.getTurnoutState(slipState); 2427 Turnout tTemp = ls.getTurnout(); 2428 if (tTemp == null ) { 2429 log.error("Unexpected null Turnout in {}, skipped", ls); 2430 continue; // skip this one in loop, what else can you do? 2431 } 2432 turnoutSettings.put(ls.getTurnout(), taState); 2433 int tbState = ls.getTurnoutBState(slipState); 2434 turnoutSettings.put(ls.getTurnoutB(), tbState); 2435 } else if ( lt != null ) { 2436 String t = lt.getTurnoutName(); 2437 // temporary = why is this looking up the Turnout instead of using getTurnout()? 2438 Turnout turnout = InstanceManager.turnoutManagerInstance().getTurnout(t); 2439 if (log.isDebugEnabled()) { 2440 if ( (lt.getTurnoutType() == LayoutTurnout.TurnoutType.RH_TURNOUT || 2441 lt.getTurnoutType() == LayoutTurnout.TurnoutType.LH_TURNOUT || 2442 lt.getTurnoutType() == LayoutTurnout.TurnoutType.WYE_TURNOUT) 2443 && (!lt.getBlockName().isEmpty())) { 2444 log.debug("turnout in list is straight left/right wye"); 2445 log.debug("turnout block Name {}", lt.getBlockName()); 2446 log.debug("current {} - pre {}", lblks.get(i).getBlock().getDisplayName(), lblks.get(preBlk).getBlock().getDisplayName()); 2447 log.debug("A {}", lt.getConnectA()); 2448 log.debug("B {}", lt.getConnectB()); 2449 log.debug("C {}", lt.getConnectC()); 2450 log.debug("D {}", lt.getConnectD()); 2451 } 2452 } 2453 if (turnout != null ) { 2454 turnoutSettings.put(turnout, turnoutList.get(x).getExpectedState()); 2455 } 2456 Turnout tempT; 2457 if ((tempT = lt.getSecondTurnout()) != null) { 2458 turnoutSettings.put(tempT, turnoutList.get(x).getExpectedState()); 2459 } 2460 /* TODO: We could do with a more intelligent way to deal with double crossovers, other than 2461 just looking at the state of the other conflicting blocks, such as looking at Signalmasts 2462 that protect the other blocks and the settings of any other turnouts along the way. 2463 */ 2464 if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) { 2465 LayoutBlock tempLB; 2466 if (turnoutList.get(x).getExpectedState() == Turnout.THROWN) { 2467 if (lt.getLayoutBlock() == lblks.get(i) || lt.getLayoutBlockC() == lblks.get(i)) { 2468 // A or C, add B and D to remove list unless A=B or C=D 2469 if ((tempLB = lt.getLayoutBlockB()) != null) { 2470 if (!tempLB.equals(lt.getLayoutBlock())) { 2471 dblCrossoverAutoBlocks.add(tempLB.getBlock()); 2472 } 2473 block.put(tempLB.getBlock(), Block.UNOCCUPIED); 2474 } 2475 if ((tempLB = lt.getLayoutBlockD()) != null) { 2476 if (!tempLB.equals(lt.getLayoutBlockC())) { 2477 dblCrossoverAutoBlocks.add(tempLB.getBlock()); 2478 } 2479 block.put(tempLB.getBlock(), Block.UNOCCUPIED); 2480 } 2481 } else if (lt.getLayoutBlockB() == lblks.get(i) || lt.getLayoutBlockD() == lblks.get(i)) { 2482 // B or D, add A and C to remove list unless A=B or C=D 2483 if ((tempLB = lt.getLayoutBlock()) != null) { 2484 if (!tempLB.equals(lt.getLayoutBlockB())) { 2485 dblCrossoverAutoBlocks.add(tempLB.getBlock()); 2486 } 2487 block.put(tempLB.getBlock(), Block.UNOCCUPIED); 2488 } 2489 if ((tempLB = lt.getLayoutBlockC()) != null) { 2490 if (!tempLB.equals(lt.getLayoutBlockD())) { 2491 dblCrossoverAutoBlocks.add(tempLB.getBlock()); 2492 } 2493 block.put(tempLB.getBlock(), Block.UNOCCUPIED); 2494 } 2495 } 2496 } 2497 } 2498 } 2499 } 2500 } 2501 } 2502 if (useLayoutEditorTurnouts) { 2503 setAutoTurnouts(turnoutSettings); 2504 } 2505 return block; 2506 } 2507 2508 /** 2509 * Generate auto signalmast for a given SML. Looks through all the other 2510 * logics to see if there are any blocks that are in common and thus 2511 * will add the other signal mast protecting that block. 2512 * 2513 * @param sml The Signal Mast Logic for which to set up 2514 * autoSignalMasts 2515 * @param overwrite When true, replace an existing autoMasts list in the 2516 * SML 2517 */ 2518 void setupAutoSignalMast(SignalMastLogic sml, boolean overwrite) { 2519 if (!allowAutoSignalMastGeneration) { 2520 return; 2521 } 2522 List<SignalMastLogic> smlList = InstanceManager.getDefault(SignalMastLogicManager.class) 2523 .getLogicsByDestination(destinationSignalMast); 2524 List<Block> allBlock = new ArrayList<>(); 2525 2526 userSetBlocks.forEach(nbh -> allBlock.add((Block) nbh.getBean())); 2527 2528 Set<Block> blockKeys = autoBlocks.keySet(); 2529 blockKeys.stream().filter(key -> (!allBlock.contains(key))).forEachOrdered(key -> 2530 allBlock.add(key)); 2531 Hashtable<SignalMast, String> masts; 2532 if (sml != null) { 2533 masts = autoMasts; 2534 if (sml.areBlocksIncluded(allBlock)) { 2535 SignalMast mast = sml.getSourceMast(); 2536 String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER); 2537 masts.put(mast, danger); 2538 } else { 2539 //No change so will leave. 2540 return; 2541 } 2542 } else { 2543 masts = new Hashtable<>(); 2544 for (int i = 0; i < smlList.size(); i++) { 2545 if (smlList.get(i).areBlocksIncluded(allBlock)) { 2546 SignalMast mast = smlList.get(i).getSourceMast(); 2547 String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER); 2548 masts.put(mast, danger); 2549 } 2550 } 2551 } 2552 setAutoMasts(masts, overwrite); 2553 } 2554 2555 /** 2556 * Add a certain Signal Mast to the list of AutoSignalMasts for this 2557 * SML. 2558 * 2559 * @param mast The Signal Mast to be added 2560 */ 2561 void addAutoSignalMast(SignalMast mast) { 2562 log.debug("{} add mast to auto list {}", destinationSignalMast.getDisplayName(), mast); 2563 String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER); 2564 if (danger == null) { 2565 log.error("Can not add SignalMast {} to logic for {} to {} " 2566 + "as it does not have a Danger appearance configured", 2567 mast.getDisplayName(), source.getDisplayName(), destinationSignalMast.getDisplayName()); 2568 return; 2569 } 2570 this.autoMasts.put(mast, danger); 2571 if (destMastInit) { 2572 mast.addPropertyChangeListener(propertySignalMastListener); 2573 } 2574 firePropertyChange(PROPERTY_AUTO_MASTS, null, this.destinationSignalMast); 2575 } 2576 2577 /** 2578 * Remove a certain Signal Mast from the list of AutoSignalMasts for 2579 * this SML. 2580 * 2581 * @param mast The Signal Mast to be removed 2582 */ 2583 void removeAutoSignalMast(SignalMast mast) { 2584 this.autoMasts.remove(mast); 2585 if (destMastInit) { 2586 mast.removePropertyChangeListener(propertySignalMastListener); 2587 } 2588 firePropertyChange(PROPERTY_AUTO_MASTS, this.destinationSignalMast, null); 2589 } 2590 2591 boolean useLayoutEditor() { 2592 return useLayoutEditor; 2593 } 2594 2595 boolean useLayoutEditorBlocks() { 2596 return useLayoutEditorBlocks; 2597 } 2598 2599 boolean useLayoutEditorTurnouts() { 2600 return useLayoutEditorTurnouts; 2601 } 2602 2603 boolean allowAutoSignalMastGeneration = false; 2604 2605 boolean allowAutoSignalMastGen() { 2606 return allowAutoSignalMastGeneration; 2607 } 2608 2609 void allowAutoSignalMastGen(boolean gen) { 2610 if (allowAutoSignalMastGeneration == gen) { 2611 return; 2612 } 2613 allowAutoSignalMastGeneration = gen; 2614 } 2615 2616 /** 2617 * Remove references from this Destination Mast and clear lists, so that 2618 * it can eventually be garbage-collected. 2619 * <p> 2620 * Note: This does not stop any delayed operations that might be queued. 2621 */ 2622 void dispose() { 2623 disposed = true; 2624 clearTurnoutLock(); 2625 destinationSignalMast.removePropertyChangeListener(propertyDestinationMastListener); 2626 setBlocks(null); 2627 setAutoBlocks(null); 2628 setTurnouts(null); 2629 setAutoTurnouts(null); 2630 setSensors(null); 2631 setMasts(null); 2632 setAutoMasts(null, true); 2633 } 2634 2635 void lockTurnouts() { 2636 // We do not allow the turnouts to be locked if we are disposing the logic, 2637 // if the logic is not active, or if we do not allow the turnouts to be locked. 2638 if ((disposed) || (!lockTurnouts) || (!active)) { 2639 return; 2640 } 2641 2642 userSetTurnouts.stream().map(nbh -> (Turnout) nbh.getBean()).forEachOrdered(key -> { 2643 key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, true); 2644 }); 2645 Enumeration<Turnout> keys = autoTurnouts.keys(); 2646 while (keys.hasMoreElements()) { 2647 Turnout key = keys.nextElement(); 2648 key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, true); 2649 } 2650 } 2651 2652 void clearTurnoutLock() { 2653 // We do not allow the turnout lock to be cleared if we are not active, 2654 // and the lock flag has not been set. 2655 if ((!lockTurnouts) && (!active)) { 2656 return; 2657 } 2658 2659 Enumeration<Turnout> keys = autoTurnouts.keys(); 2660 while (keys.hasMoreElements()) { 2661 Turnout key = keys.nextElement(); 2662 key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, false); 2663 } 2664 2665 userSetTurnouts.stream().map(nbh -> (Turnout) nbh.getBean()).forEachOrdered(key -> 2666 key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, false)); 2667 } 2668 2669 protected void calculateSpeed() { 2670 log.debug("{} calculate the speed setting for this logic ie what the signalmast will display", destinationSignalMast.getDisplayName()); 2671 minimumBlockSpeed = 0.0f; 2672 Enumeration<Turnout> keys = autoTurnouts.keys(); 2673 while (keys.hasMoreElements()) { 2674 Turnout key = keys.nextElement(); 2675 log.debug("{} turnout {}", destinationSignalMast.getDisplayName(), key.getDisplayName()); 2676 if (!isTurnoutIncluded(key)) { 2677 if (autoTurnouts.get(key) == Turnout.CLOSED) { 2678 if (((key.getStraightLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getStraightLimit() != -1)) { 2679 minimumBlockSpeed = key.getStraightLimit(); 2680 log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed); 2681 } 2682 } else { 2683 if (((key.getDivergingLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getDivergingLimit() != -1)) { 2684 minimumBlockSpeed = key.getDivergingLimit(); 2685 log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed); 2686 } 2687 } 2688 } 2689 } 2690 2691 userSetTurnouts.forEach(nbh -> { 2692 Turnout key = (Turnout) nbh.getBean(); 2693 if (nbh.getSetting() == Turnout.CLOSED) { 2694 if (((key.getStraightLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getStraightLimit() != -1)) { 2695 minimumBlockSpeed = key.getStraightLimit(); 2696 log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed); 2697 } 2698 } else if (nbh.getSetting() == Turnout.THROWN) { 2699 if (((key.getDivergingLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getDivergingLimit() != -1)) { 2700 minimumBlockSpeed = key.getDivergingLimit(); 2701 log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed); 2702 } 2703 } 2704 }); 2705 2706 Set<Block> autoBlockKeys = autoBlocks.keySet(); 2707 for (Block key : autoBlockKeys) { 2708 log.debug("{} auto block add list {}", destinationSignalMast.getDisplayName(), key.getDisplayName()); 2709 if (!isBlockIncluded(key)) { 2710 if (((key.getSpeedLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getSpeedLimit() != -1)) { 2711 minimumBlockSpeed = key.getSpeedLimit(); 2712 log.debug("{} block {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed); 2713 } 2714 } 2715 } 2716 for (NamedBeanSetting nbh : userSetBlocks) { 2717 Block key = (Block) nbh.getBean(); 2718 if (((key.getSpeedLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getSpeedLimit() != -1)) { 2719 log.debug("{} block {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed); 2720 minimumBlockSpeed = key.getSpeedLimit(); 2721 } 2722 } 2723 /*if(minimumBlockSpeed==-0.1f) 2724 minimumBlockSpeed==0.0f;*/ 2725 } 2726 2727 protected PropertyChangeListener propertySensorListener = new PropertyChangeListener() { 2728 @Override 2729 public void propertyChange(PropertyChangeEvent e) { 2730 Sensor sen = (Sensor) e.getSource(); 2731 log.debug("{} to {} destination sensor {} trigger {}",source.getDisplayName(), destinationSignalMast.getDisplayName(), sen.getDisplayName(), e.getPropertyName()); 2732 if ( Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())) { 2733 int now = ((Integer) e.getNewValue()); 2734 log.debug("current value {} value we want {}", now, getSensorState(sen)); 2735 if (isSensorIncluded(sen) && getSensorState(sen) != now) { 2736 log.debug("Sensor {} caused the signalmast to be set to danger", sen.getDisplayName()); 2737 //getSourceMast().setAspect(stopAspect); 2738 if (active == true) { 2739 active = false; 2740 setSignalAppearance(); 2741 } 2742 } else if (getSensorState(sen) == now) { 2743 log.debug("{} sensor {} triggers a calculation of change", destinationSignalMast.getDisplayName(), sen.getDisplayName()); 2744 checkState(); 2745 } 2746 } 2747 } 2748 }; 2749 2750 protected PropertyChangeListener propertyTurnoutListener = new PropertyChangeListener() { 2751 @Override 2752 public void propertyChange(PropertyChangeEvent e) { 2753 Turnout turn = (Turnout) e.getSource(); 2754 // log.debug(destination.getDisplayName() + " destination sensor "+ sen.getDisplayName() + "trigger"); 2755 if ( Turnout.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())) { 2756 //Need to check this against the manual list vs auto list 2757 //The manual list should over-ride the auto list 2758 int now = ((Integer) e.getNewValue()); 2759 if (isTurnoutIncluded(turn)) { 2760 if (getTurnoutState(turn) != now) { 2761 log.debug("Turnout {} caused the signalmast to be set", turn.getDisplayName()); 2762 log.debug("From {} to {} Turnout {} caused the signalmast to be set to danger", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName()); 2763 if (active == true) { 2764 active = false; 2765 setSignalAppearance(); 2766 } 2767 } else { 2768 log.debug("{} turnout {} triggers a calculation of change", destinationSignalMast.getDisplayName(), turn.getDisplayName()); 2769 checkState(); 2770 } 2771 } else if (autoTurnouts.containsKey(turn)) { 2772 if (getAutoTurnoutState(turn) != now) { 2773 log.debug("Turnout {} auto caused the signalmast to be set", turn.getDisplayName()); 2774 log.debug("From {} to {} Auto Turnout {} auto caused the signalmast to be set to danger", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName()); 2775 if (active == true) { 2776 active = false; 2777 setSignalAppearance(); 2778 } 2779 } else { 2780 log.debug("From {} to {} turnout {} triggers a calculation of change", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName()); 2781 checkState(); 2782 } 2783 } 2784 2785 } else if ( Turnout.PROPERTY_TURNOUT_STRAIGHT_SPEED.equals(e.getPropertyName()) 2786 || Turnout.PROPERTY_TURNOUT_DIVERGING_SPEED.equals(e.getPropertyName())) { 2787 calculateSpeed(); 2788 } 2789 } 2790 }; 2791 2792 protected PropertyChangeListener propertyBlockListener = new PropertyChangeListener() { 2793 @Override 2794 public void propertyChange(PropertyChangeEvent e) { 2795 Block block = (Block) e.getSource(); 2796 log.debug("{} destination block {} trigger {} {}", destinationSignalMast.getDisplayName(), block.getDisplayName(), e.getPropertyName(), e.getNewValue()); 2797 if ( Block.PROPERTY_STATE.equals(e.getPropertyName()) || Block.PROPERTY_ALLOCATED.equals(e.getPropertyName())) { 2798 // TODO: what is this? 2799 log.debug("Included in user entered block {}", Boolean.toString(isBlockIncluded(block))); 2800 log.debug("Included in AutoGenerated Block {}", Boolean.toString(autoBlocks.containsKey(block))); 2801 if (isBlockIncluded(block)) { 2802 log.debug("{} in manual block", destinationSignalMast.getDisplayName()); 2803 log.debug(" state: {} {}", getBlockState(block), block.getState()); 2804 checkState(); 2805 } else if (autoBlocks.containsKey(block)) { 2806 log.debug("{} in auto block", destinationSignalMast.getDisplayName()); 2807 log.debug(" states: {} {}", getAutoBlockState(block), block.getState()); 2808 checkState(); 2809 } else { 2810 log.debug("{} Not found", destinationSignalMast.getDisplayName()); 2811 } 2812 } else if ( e.getPropertyName().equals("BlockSpeedChange")) { 2813 calculateSpeed(); 2814 } 2815 } 2816 }; 2817 2818 protected PropertyChangeListener propertySignalMastListener = new PropertyChangeListener() { 2819 @Override 2820 public void propertyChange(PropertyChangeEvent e) { 2821 2822 SignalMast mast = (SignalMast) e.getSource(); 2823 log.debug("{} signalmast change {} {}", destinationSignalMast.getDisplayName(), mast.getDisplayName(), e.getPropertyName()); 2824 // log.debug(destination.getDisplayName() + " destination sensor "+ sen.getDisplayName() + "trigger"); 2825 if ( SignalMast.PROPERTY_ASPECT.equals(e.getPropertyName())) { 2826 2827 String now = ((String) e.getNewValue()); 2828 log.debug("{} match property {}", destinationSignalMast.getDisplayName(), now); 2829 if (isSignalMastIncluded(mast)) { 2830 if (!now.equals(getSignalMastState(mast))) { 2831 log.debug("{} in mast list SignalMast {} caused the signalmast to be set", destinationSignalMast.getDisplayName(), mast.getDisplayName()); 2832 log.debug("SignalMast {} caused the signalmast to be set", mast.getDisplayName()); 2833 if (active) { 2834 active = false; 2835 setSignalAppearance(); 2836 } 2837 } else { 2838 log.debug("{} in mast list signalmast change", destinationSignalMast.getDisplayName()); 2839 checkState(); 2840 } 2841 } else if (autoMasts.containsKey(mast)) { 2842 if (!now.equals(getAutoSignalMastState(mast))) { 2843 log.debug("SignalMast {} caused the signalmast to be set", mast.getDisplayName()); 2844 log.debug("{} in auto mast list SignalMast {} caused the signalmast to be set", destinationSignalMast.getDisplayName(), mast.getDisplayName()); 2845 if (active) { 2846 active = false; 2847 setSignalAppearance(); 2848 } 2849 } else { 2850 log.debug("{} in auto mast list signalmast change", destinationSignalMast.getDisplayName()); 2851 checkState(); 2852 } 2853 } 2854 } 2855 } 2856 }; 2857 2858 private class NamedBeanSetting { 2859 2860 NamedBeanHandle<?> namedBean; 2861 int setting = 0; 2862 String strSetting = null; 2863 2864 NamedBeanSetting(NamedBeanHandle<?> namedBean, int setting) { 2865 this.namedBean = namedBean; 2866 this.setting = setting; 2867 } 2868 2869 NamedBeanSetting(NamedBeanHandle<?> namedBean, String setting) { 2870 this.namedBean = namedBean; 2871 strSetting = setting; 2872 } 2873 2874 NamedBean getBean() { 2875 return namedBean.getBean(); 2876 } 2877 2878 NamedBeanHandle<?> getNamedBean() { 2879 return namedBean; 2880 } 2881 2882 int getSetting() { 2883 return setting; 2884 } 2885 2886 String getStringSetting() { 2887 return strSetting; 2888 } 2889 2890 String getBeanName() { 2891 return namedBean.getName(); 2892 } 2893 } 2894 } 2895 2896 /** 2897 * The listener on the destination Signal Mast. 2898 */ 2899 private PropertyChangeListener propertyDestinationMastListener = new PropertyChangeListener() { 2900 @Override 2901 public void propertyChange(PropertyChangeEvent e) { 2902 SignalMast mast = (SignalMast) e.getSource(); 2903 if (mast == destination) { 2904 log.debug("destination mast change {}", mast.getDisplayName()); 2905 setSignalAppearance(); 2906 } 2907 } 2908 }; 2909 2910 /** 2911 * The listener on the source Signal Mast. 2912 */ 2913 private PropertyChangeListener propertySourceMastListener = new PropertyChangeListener() { 2914 @Override 2915 public void propertyChange(PropertyChangeEvent e) { 2916 SignalMast mast = (SignalMast) e.getSource(); 2917 if ((mast == source) && ( SignalMast.PROPERTY_HELD.equals(e.getPropertyName()))) { 2918 log.debug("source mast change {} {}", mast.getDisplayName(), e.getPropertyName()); 2919 setSignalAppearance(); 2920 } 2921 } 2922 }; 2923 2924 //@todo need to think how we deal with auto generated lists based upon the layout editor. 2925 @Override 2926 public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { 2927 NamedBean nb = (NamedBean) evt.getOldValue(); 2928 if (Manager.PROPERTY_CAN_DELETE.equals(evt.getPropertyName())) { 2929 boolean found = false; 2930 StringBuilder message = new StringBuilder(); 2931 if (nb instanceof SignalMast) { 2932 if (nb.equals(source)) { 2933 message.append("Has SignalMast Logic attached which will be <b>Deleted</b> to <ul>"); 2934 for (SignalMast sm : getDestinationList()) { 2935 message.append("<li>"); 2936 message.append(sm.getDisplayName()); 2937 message.append("</li>"); 2938 } 2939 message.append("</ul>"); 2940 throw new PropertyVetoException(message.toString(), evt); 2941 2942 } else if (isDestinationValid((SignalMast) nb)) { 2943 throw new PropertyVetoException("Is the end point mast for logic attached to signal mast " + source.getDisplayName() + " which will be <b>Deleted</b> ", evt); 2944 } 2945 for (SignalMast sm : getDestinationList()) { 2946 if (isSignalMastIncluded((SignalMast) nb, sm)) { 2947 message.append("<li>"); 2948 message.append("Used in conflicting logic of ").append(source.getDisplayName()) 2949 .append(" & ").append(sm.getDisplayName()).append("</li>"); 2950 } 2951 } 2952 } 2953 if (nb instanceof Turnout) { 2954 for (SignalMast sm : getDestinationList()) { 2955 if (isTurnoutIncluded((Turnout) nb, sm)) { 2956 message.append("<li>Is in logic between Signal Masts ").append(source.getDisplayName()) 2957 .append(" ").append(sm.getDisplayName()).append("</li>"); 2958 found = true; 2959 } 2960 } 2961 } 2962 if (nb instanceof Sensor) { 2963 for (SignalMast sm : getDestinationList()) { 2964 if (isSensorIncluded((Sensor) nb, sm)) { 2965 message.append("<li>"); 2966 message.append("Is in logic between Signal Masts ").append(source.getDisplayName()) 2967 .append(" ").append(sm.getDisplayName()).append("</li>"); 2968 found = true; 2969 } 2970 } 2971 } 2972 if (found) { 2973 throw new PropertyVetoException(message.toString(), evt); 2974 } 2975 } else if (Manager.PROPERTY_DO_DELETE.equals(evt.getPropertyName())) { 2976 if (nb instanceof SignalMast) { 2977 if (nb.equals(source)) { 2978 dispose(); 2979 } 2980 if (isDestinationValid((SignalMast) nb)) { 2981 removeDestination((SignalMast) nb); 2982 } 2983 for (SignalMast sm : getDestinationList()) { 2984 if (isSignalMastIncluded((SignalMast) nb, sm)) { 2985 log.warn("Unhandled condition: signal mast included during DoDelete"); 2986 // @todo need to deal with this situation 2987 } 2988 } 2989 } 2990 if (nb instanceof Turnout) { 2991 Turnout t = (Turnout) nb; 2992 getDestinationList().stream().filter(sm -> (isTurnoutIncluded(t, sm))).forEachOrdered(sm -> { 2993 removeTurnout(t, sm); 2994 }); 2995 } 2996 if (nb instanceof Sensor) { 2997 Sensor s = (Sensor) nb; 2998 getDestinationList().stream().filter(sm -> (isSensorIncluded(s, sm))).forEachOrdered(sm -> { 2999 removeSensor(s, sm); 3000 }); 3001 } 3002 } 3003 } 3004 3005 /** 3006 * Note: This does not stop any delayed operations that might be queued. 3007 */ 3008 @Override 3009 public void dispose() { 3010 disposing = true; 3011 getSourceMast().removePropertyChangeListener(propertySourceMastListener); 3012 Enumeration<SignalMast> en = destList.keys(); 3013 while (en.hasMoreElements()) { 3014 SignalMast dm = en.nextElement(); 3015 destList.get(dm).dispose(); 3016 } 3017 super.dispose(); // release any prop change listeners 3018 } 3019 3020 /** 3021 * {@inheritDoc } 3022 */ 3023 @Override 3024 public String getBeanType() { 3025 return Bundle.getMessage("BeanNameSignalMastLogic"); 3026 } 3027 3028 /** 3029 * No valid integer state, always return a constant. 3030 * 3031 * @return Always zero 3032 */ 3033 @Override 3034 public int getState() { 3035 return 0; 3036 } 3037 3038 @Override 3039 public void setState(int i) { 3040 } 3041 3042 /** 3043 * {@inheritDoc } 3044 */ 3045 @Override 3046 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 3047 List<NamedBeanUsageReport> report = new ArrayList<>(); 3048 if (bean != null) { 3049 if (bean.equals(getSourceMast())) { 3050 report.add(new NamedBeanUsageReport("SMLSourceMast")); // NOI18N 3051 } 3052 getDestinationList().forEach((dest) -> { 3053 if (bean.equals(dest)) { 3054 report.add(new NamedBeanUsageReport("SMLDestinationMast")); // NOI18N 3055 } 3056 getAutoBlocks(dest).forEach((block) -> { 3057 if (bean.equals(block)) { 3058 report.add(new NamedBeanUsageReport("SMLBlockAuto", dest)); // NOI18N 3059 } 3060 }); 3061 getBlocks(dest).forEach((block) -> { 3062 if (bean.equals(block)) { 3063 report.add(new NamedBeanUsageReport("SMLBlockUser", dest)); // NOI18N 3064 } 3065 }); 3066 getAutoTurnouts(dest).forEach((turnout) -> { 3067 if (bean.equals(turnout)) { 3068 report.add(new NamedBeanUsageReport("SMLTurnoutAuto", dest)); // NOI18N 3069 } 3070 }); 3071 getTurnouts(dest).forEach((turnout) -> { 3072 if (bean.equals(turnout)) { 3073 report.add(new NamedBeanUsageReport("SMLTurnoutUser", dest)); // NOI18N 3074 } 3075 }); 3076 getSensors(dest).forEach((sensor) -> { 3077 if (bean.equals(sensor)) { 3078 report.add(new NamedBeanUsageReport("SMLSensor", dest)); // NOI18N 3079 } 3080 }); 3081 getAutoMasts(dest).forEach((mast) -> { 3082 if (bean.equals(mast)) { 3083 report.add(new NamedBeanUsageReport("SMLMastAuto", dest)); // NOI18N 3084 } 3085 }); 3086 getSignalMasts(dest).forEach((mast) -> { 3087 if (bean.equals(mast)) { 3088 report.add(new NamedBeanUsageReport("SMLMastUser", dest)); // NOI18N 3089 } 3090 }); 3091 }); 3092 } 3093 return report; 3094 } 3095 3096 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultSignalMastLogic.class); 3097 3098}