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