001package jmri.jmrit.entryexit; 002 003import java.awt.Color; 004import java.awt.event.MouseAdapter; 005import java.awt.event.MouseEvent; 006import java.beans.PropertyChangeEvent; 007import java.beans.PropertyChangeListener; 008import java.beans.PropertyVetoException; 009import java.util.*; 010import java.util.Map.Entry; 011 012import javax.annotation.CheckReturnValue; 013import javax.annotation.Nonnull; 014import javax.annotation.OverridingMethodsMustInvokeSuper; 015import javax.swing.JDialog; 016import javax.swing.JPanel; 017 018import jmri.*; 019import jmri.beans.VetoableChangeSupport; 020import jmri.jmrit.display.EditorManager; 021import jmri.jmrit.display.layoutEditor.LayoutBlock; 022import jmri.jmrit.display.layoutEditor.LayoutBlockConnectivityTools; 023import jmri.jmrit.display.layoutEditor.LayoutBlockManager; 024import jmri.jmrit.display.layoutEditor.LayoutEditor; 025import jmri.jmrix.internal.InternalSystemConnectionMemo; 026import jmri.util.swing.JmriJOptionPane; 027 028/** 029 * Implements an Entry Exit based method of setting turnouts, setting up signal 030 * logic and allocating blocks through a path based on the Layout Editor. 031 * <p> 032 * The route is based upon having a sensor assigned at a known location on the 033 * panel (set at the boundary of two different blocks) through to a sensor at a 034 * remote location on the same panel. Using the layout block routing, a path can 035 * then be set between the two sensors so long as one exists and no 036 * section of track is set occupied. If available an alternative route will be 037 * used when the direct path is occupied (blocked). 038 * <p> 039 * Initial implementation only handles the setting up of turnouts on a path. 040 * 041 * @author Kevin Dickerson Copyright (C) 2011 042 */ 043public class EntryExitPairs extends VetoableChangeSupport implements Manager<DestinationPoints>, jmri.InstanceManagerAutoDefault, 044 PropertyChangeListener { 045 046 public LayoutBlockConnectivityTools.Metric routingMethod = LayoutBlockConnectivityTools.Metric.METRIC; 047 048 public final static int NXBUTTONSELECTED = 0x08; 049 public final static int NXBUTTONACTIVE = Sensor.ACTIVE; 050 public final static int NXBUTTONINACTIVE = Sensor.INACTIVE; 051 private final SystemConnectionMemo memo; 052 private final Map<String, Boolean> silencedProperties = new HashMap<>(); 053 054 private int settingTimer = 2000; 055 056 public int getSettingTimer() { 057 return settingTimer; 058 } 059 060 public void setSettingTimer(int i) { 061 settingTimer = i; 062 } 063 064 private Color settingRouteColor = null; 065 066 public boolean useDifferentColorWhenSetting() { 067 return (settingRouteColor != null); 068 } 069 070 public Color getSettingRouteColor() { 071 return settingRouteColor; 072 } 073 074 public void setSettingRouteColor(Color col) { 075 settingRouteColor = col; 076 } 077 078 /** 079 * Constant value to represent that the entryExit will only set up the 080 * turnouts between two different points. 081 */ 082 public final static int SETUPTURNOUTSONLY = 0x00; 083 084 /** 085 * Constant value to represent that the entryExit will set up the turnouts 086 * between two different points and configure the Signal Mast Logic to use 087 * the correct blocks. 088 */ 089 public final static int SETUPSIGNALMASTLOGIC = 0x01; 090 091 /** 092 * Constant value to represent that the entryExit will do full interlocking. 093 * It will set the turnouts and "reserve" the blocks. 094 */ 095 public final static int FULLINTERLOCK = 0x02; 096 097 boolean allocateToDispatcher = false; 098 boolean absSignalMode = false; 099 100 public final static int PROMPTUSER = 0x00; 101 public final static int AUTOCLEAR = 0x01; 102 public final static int AUTOCANCEL = 0x02; 103 public final static int AUTOSTACK = 0x03; 104 105 public final static int OVERLAP_CANCEL = 0x01; 106 public final static int OVERLAP_STACK = 0x02; 107 108 int routeClearOption = PROMPTUSER; 109 int routeOverlapOption = PROMPTUSER; 110 String memoryOption = ""; // Optional memory variable to receive allocation messages 111 int memoryClearDelay = 0; // Delay before clearing memory, 0 for clearing disabled 112 113 static JPanel glassPane = new JPanel(); 114 115 /** 116 * Delay between issuing Turnout commands 117 */ 118 public int turnoutSetDelay = 0; 119 120 /** 121 * Constructor for creating an EntryExitPairs object and create a transparent JPanel for it. 122 */ 123 public EntryExitPairs() { 124 memo = InstanceManager.getDefault(InternalSystemConnectionMemo.class); 125 InstanceManager.getOptionalDefault(ConfigureManager.class).ifPresent(cm -> cm.registerUser(this)); 126 InstanceManager.getDefault(LayoutBlockManager.class).addPropertyChangeListener(propertyBlockManagerListener); 127 128 glassPane.setOpaque(false); 129 glassPane.setLayout(null); 130 glassPane.addMouseListener(new MouseAdapter() { 131 @Override 132 public void mousePressed(MouseEvent e) { 133 e.consume(); 134 } 135 }); 136 } 137 138 public void setDispatcherIntegration(boolean boo) { 139 allocateToDispatcher = boo; 140 } 141 142 public boolean getDispatcherIntegration() { 143 return allocateToDispatcher; 144 } 145 146 public void setAbsSignalMode(boolean absMode) { 147 absSignalMode = absMode; 148 } 149 150 public boolean isAbsSignalMode() { 151 return absSignalMode; 152 } 153 154 /** 155 * Get the transparent JPanel for this EntryExitPairs. 156 * @return JPanel overlay 157 */ 158 public JPanel getGlassPane() { 159 return glassPane; 160 } 161 162 HashMap<PointDetails, Source> nxpair = new HashMap<>(); 163 164 public void addNXSourcePoint(LayoutBlock facing, List<LayoutBlock> protecting, NamedBean loc, LayoutEditor panel) { 165 PointDetails point = providePoint(facing, protecting, panel); 166 point.setRefObject(loc); 167 } 168 169 public void addNXSourcePoint(NamedBean source) { 170 PointDetails point = null; 171 for (LayoutEditor editor : InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class)) { 172 point = providePoint(source, editor); 173 } 174 if (point == null) { 175 log.error("Unable to find a location on any panel for item {}", source.getDisplayName()); // NOI18N 176 } 177 } 178 179 public void addNXSourcePoint(NamedBean source, LayoutEditor panel) { 180 if (source == null) { 181 log.error("source bean supplied is null"); // NOI18N 182 return; 183 } 184 if (panel == null) { 185 log.error("panel supplied is null"); // NOI18N 186 return; 187 } 188 PointDetails point; 189 point = providePoint(source, panel); 190 if (point == null) { 191 log.error("Unable to find a location on the panel {} for item {}", panel.getLayoutName(), source.getDisplayName()); // NOI18N 192 } 193 } 194 195 public Object getEndPointLocation(NamedBean source, LayoutEditor panel) { 196 if (source == null) { 197 log.error("Source bean past is null"); // NOI18N 198 return null; 199 } 200 if (panel == null) { 201 log.error("panel passed is null"); // NOI18N 202 return null; 203 } 204 PointDetails sourcePoint = getPointDetails(source, panel); 205 if (sourcePoint == null) { 206 log.error("Point is not located"); // NOI18N 207 return null; 208 } 209 return sourcePoint.getRefLocation(); 210 } 211 212 /** {@inheritDoc} */ 213 @Override 214 public int getXMLOrder() { 215 return ENTRYEXIT; 216 } 217 218 /** {@inheritDoc} */ 219 @Override 220 public DestinationPoints getBySystemName(String systemName) { 221 for (Source e : nxpair.values()) { 222 DestinationPoints pd = e.getByUniqueId(systemName); 223 if (pd != null) { 224 return pd; 225 } 226 } 227 return null; 228 } 229 230 /** {@inheritDoc} */ 231 @Override 232 public DestinationPoints getByUserName(@Nonnull String userName) { 233 for (Source e : nxpair.values()) { 234 DestinationPoints pd = e.getByUserName(userName); 235 if (pd != null) { 236 return pd; 237 } 238 } 239 return null; 240 } 241 242 /** {@inheritDoc} */ 243 @Override 244 public DestinationPoints getNamedBean(@Nonnull String name) { 245 DestinationPoints b = getByUserName(name); 246 if (b != null) { 247 return b; 248 } 249 return getBySystemName(name); 250 } 251 252 /** {@inheritDoc} */ 253 @Nonnull 254 @Override 255 public SystemConnectionMemo getMemo() { 256 return memo; 257 } 258 259 /** {@inheritDoc} */ 260 @Override 261 @Nonnull 262 public String getSystemPrefix() { 263 return memo.getSystemPrefix(); 264 } 265 266 /** {@inheritDoc} */ 267 @Override 268 public char typeLetter() { 269 throw new UnsupportedOperationException("Not supported yet."); // NOI18N 270 } 271 272 /** {@inheritDoc} */ 273 @Override 274 @Nonnull 275 public String makeSystemName(@Nonnull String s) { 276 throw new UnsupportedOperationException("Not supported yet."); // NOI18N 277 } 278 279 /** {@inheritDoc} */ 280 @Override 281 @CheckReturnValue 282 public int getObjectCount() { 283 return getNamedBeanSet().size(); 284 } 285 286 /** 287 * Implemented to support the Conditional combo box name list 288 * @since 4.9.3 289 * @return a list of Destination Point beans 290 */ 291 @Override 292 @Nonnull 293 public SortedSet<DestinationPoints> getNamedBeanSet() { 294 TreeSet<DestinationPoints> beanList = new TreeSet<>(new jmri.util.NamedBeanComparator<>()); 295 for (Source e : nxpair.values()) { 296 List<String> uidList = e.getDestinationUniqueId(); 297 for (String uid : uidList) { 298 beanList.add(e.getByUniqueId(uid)); 299 } 300 } 301 return beanList; 302 } 303 304 /** {@inheritDoc} */ 305 @Override 306 public void register(@Nonnull DestinationPoints n) { 307 throw new UnsupportedOperationException("Not supported yet."); // NOI18N 308 } 309 310 /** {@inheritDoc} */ 311 @Override 312 public void deregister(@Nonnull DestinationPoints n) { 313 throw new UnsupportedOperationException("Not supported yet."); // NOI18N 314 } 315 316 public void setClearDownOption(int i) { 317 routeClearOption = i; 318 } 319 320 public int getClearDownOption() { 321 return routeClearOption; 322 } 323 324 public void setOverlapOption(int i) { 325 routeOverlapOption = i; 326 } 327 328 public int getOverlapOption() { 329 return routeOverlapOption; 330 } 331 332 public void setMemoryOption(String memoryName) { 333 memoryOption = memoryName; 334 } 335 336 public String getMemoryOption() { 337 return memoryOption; 338 } 339 340 public void setMemoryClearDelay(int secs) { 341 memoryClearDelay = secs; 342 } 343 344 public int getMemoryClearDelay() { 345 return memoryClearDelay; 346 } 347 348 /** {@inheritDoc} */ 349 @Override 350 public void dispose() { 351 } 352 353 /** 354 * Generate the point details, given a known source and a 355 * Layout Editor panel. 356 * 357 * @param source Origin of movement 358 * @param panel A Layout Editor panel 359 * @return A PointDetails object 360 */ 361 public PointDetails providePoint(NamedBean source, LayoutEditor panel) { 362 PointDetails sourcePoint = getPointDetails(source, panel); 363 if (sourcePoint == null) { 364 LayoutBlock facing = InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class).getFacingBlockByNamedBean(source, null); 365 List<LayoutBlock> protecting = InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class).getProtectingBlocksByNamedBean(source, null); 366// log.info("facing = {}, protecting = {}", facing, protecting); 367 if (facing == null && protecting.size() == 0) { 368 log.error("Unable to find facing and protecting blocks"); // NOI18N 369 return null; 370 } 371 sourcePoint = providePoint(facing, protecting, panel); 372 sourcePoint.setRefObject(source); 373 } 374 return sourcePoint; 375 } 376 377 /** 378 * Return a list of all source (origin) points on a given 379 * Layout Editor panel. 380 * 381 * @param panel A Layout Editor panel 382 * @return A list of source objects 383 */ 384 public List<Object> getSourceList(LayoutEditor panel) { 385 List<Object> list = new ArrayList<>(); 386 387 for (Entry<PointDetails, Source> e : nxpair.entrySet()) { 388 Object obj = (e.getKey()).getRefObject(); 389 LayoutEditor pan = (e.getKey()).getPanel(); 390 if (pan == panel) { 391 if (!list.contains(obj)) { 392 list.add(obj); 393 } 394 } // end while 395 } 396 return list; 397 } 398 399 public Source getSourceForPoint(PointDetails pd) { 400 return nxpair.get(pd); 401 } 402 403 public int getNxPairNumbers(LayoutEditor panel) { 404 int total = 0; 405 for (Entry<PointDetails, Source> e : nxpair.entrySet()) { 406 PointDetails key = e.getKey(); 407 LayoutEditor pan = key.getPanel(); 408 if (pan == panel) { 409 total = total + e.getValue().getNumberOfDestinations(); 410 } // end while 411 } 412 413 return total; 414 } 415 416 /** 417 * Set a reversed route between two points. Special case to support a LogixNG action. 418 * @since 5.5.7 419 * @param nxPair The system or user name of the destination point. 420 */ 421 public void setReversedRoute(String nxPair) { 422 DestinationPoints dp = getNamedBean(nxPair); 423 if (dp != null) { 424 String destUUID = dp.getUniqueId(); 425 nxpair.forEach((pd, src) -> { 426 for (String srcUUID : src.getDestinationUniqueId()) { 427 if (destUUID.equals(srcUUID)) { 428 log.debug("Found the correct reverse route source: src = {}, dest = {}", 429 pd.getSensor().getDisplayName(), dp.getDestPoint().getSensor().getDisplayName()); 430 refCounter++; 431 routesToSet.add(new SourceToDest(src, dp, true, refCounter)); 432 processRoutesToSet(); 433 return; 434 } 435 } 436 }); 437 } 438 } 439 440 /** 441 * Set the route between the two points represented by the Destination Point name. 442 * 443 * @since 4.11.1 444 * @param nxPair The system or user name of the destination point. 445 */ 446 public void setSingleSegmentRoute(String nxPair) { 447 DestinationPoints dp = getNamedBean(nxPair); 448 if (dp != null) { 449 String destUUID = dp.getUniqueId(); 450 nxpair.forEach((pd, src) -> { 451 for (String srcUUID : src.getDestinationUniqueId()) { 452 if (destUUID.equals(srcUUID)) { 453 log.debug("Found the correct source: src = {}, dest = {}", 454 pd.getSensor().getDisplayName(), dp.getDestPoint().getSensor().getDisplayName()); 455 setMultiPointRoute(pd, dp.getDestPoint()); 456 return; 457 } 458 } 459 }); 460 } 461 } 462 463 public void setMultiPointRoute(PointDetails requestpd, LayoutEditor panel) { 464 for (PointDetails pd : pointDetails) { 465 if (pd != requestpd) { 466 if (pd.getNXState() == NXBUTTONSELECTED) { 467 setMultiPointRoute(pd, requestpd); 468 return; 469 } 470 } 471 } 472 } 473 474 private void setMultiPointRoute(PointDetails fromPd, PointDetails toPd) { 475 boolean cleardown = false; 476 if (fromPd.isRouteFromPointSet() && toPd.isRouteToPointSet()) { 477 cleardown = true; 478 } 479 for (LayoutBlock pro : fromPd.getProtecting()) { 480 try { 481 jmri.jmrit.display.layoutEditor.LayoutBlockManager lbm = InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class); 482 LayoutBlock toProt = null; 483 if (!toPd.getProtecting().isEmpty()) { 484 toProt = toPd.getProtecting().get(0); 485 } 486 boolean result = lbm.getLayoutBlockConnectivityTools().checkValidDest(fromPd.getFacing(), pro, toPd.getFacing(), toProt, LayoutBlockConnectivityTools.Routing.SENSORTOSENSOR); 487 if (result) { 488 List<LayoutBlock> blkList = lbm.getLayoutBlockConnectivityTools().getLayoutBlocks(fromPd.getFacing(), toPd.getFacing(), pro, cleardown, LayoutBlockConnectivityTools.Routing.NONE); 489 if (!blkList.isEmpty()) { 490 if (log.isDebugEnabled()) { 491 for (LayoutBlock blk : blkList) { 492 log.debug("blk = {}", blk.getDisplayName()); 493 } 494 } 495 List<jmri.NamedBean> beanList = lbm.getLayoutBlockConnectivityTools().getBeansInPath(blkList, null, jmri.Sensor.class); 496 PointDetails fromPoint = fromPd; 497 refCounter++; 498 if (!beanList.isEmpty()) { 499 if (log.isDebugEnabled()) { 500 for (NamedBean xnb : beanList) { 501 log.debug("xnb = {}", xnb.getDisplayName()); 502 } 503 } 504 for (int i = 1; i < beanList.size(); i++) { 505 NamedBean nb = beanList.get(i); 506 PointDetails cur = getPointDetails(nb, fromPd.getPanel()); 507 Source s = nxpair.get(fromPoint); 508 if (s != null) { 509 routesToSet.add(new SourceToDest(s, s.getDestForPoint(cur), false, refCounter)); 510 } 511 fromPoint = cur; 512 } 513 } 514 Source s = nxpair.get(fromPoint); 515 if (s != null) { 516 if (s.getDestForPoint(toPd) != null) { 517 routesToSet.add(new SourceToDest(s, s.getDestForPoint(toPd), false, refCounter)); 518 } 519 } 520 processRoutesToSet(); 521 return; 522 } 523 } 524 } catch (jmri.JmriException e) { 525 //Can be considered normal if route is blocked 526 } 527 } 528 fromPd.setNXButtonState(NXBUTTONINACTIVE); 529 toPd.setNXButtonState(NXBUTTONINACTIVE); 530 } 531 532 int refCounter = 0; 533 534 /** 535 * List holding SourceToDest sets of routes between two points. 536 */ 537 List<SourceToDest> routesToSet = new ArrayList<>(); 538 539 /** 540 * Class to store NX sets consisting of a source point, a destination point, 541 * a direction and a reference. 542 */ 543 static class SourceToDest { 544 545 Source s = null; 546 DestinationPoints dp = null; 547 boolean direction = false; 548 int ref = -1; 549 550 /** 551 * Constructor for a SourceToDest element. 552 * 553 * @param s a source point 554 * @param dp a destination point 555 * @param dir a direction 556 * @param ref Integer used as reference 557 */ 558 SourceToDest(Source s, DestinationPoints dp, boolean dir, int ref) { 559 this.s = s; 560 this.dp = dp; 561 this.direction = dir; 562 this.ref = ref; 563 } 564 } 565 566 int currentDealing = 0; 567 568 /** 569 * Activate each SourceToDest set in routesToSet 570 */ 571 synchronized void processRoutesToSet() { 572 if (log.isDebugEnabled()) { 573 for (SourceToDest sd : routesToSet) { 574 String dpName = (sd.dp == null) ? "- null -" : sd.dp.getDestPoint().getSensor().getDisplayName(); 575 log.debug("processRoutesToSet: {} -- {} -- {}", sd.s.getPoint().getSensor().getDisplayName(), dpName, sd.ref); 576 } 577 } 578 579 if (routesToSet.isEmpty()) { 580 return; 581 } 582 Source s = routesToSet.get(0).s; 583 DestinationPoints dp = routesToSet.get(0).dp; 584 boolean dir = routesToSet.get(0).direction; 585 currentDealing = routesToSet.get(0).ref; 586 routesToSet.remove(0); 587 588 dp.addPropertyChangeListener(propertyDestinationListener); 589 s.activeBean(dp, dir); 590 } 591 592 /** 593 * Remove remaining SourceToDest sets in routesToSet 594 */ 595 synchronized void removeRemainingRoute() { 596 List<SourceToDest> toRemove = new ArrayList<>(); 597 for (SourceToDest rts : routesToSet) { 598 if (rts.ref == currentDealing) { 599 toRemove.add(rts); 600 rts.dp.getDestPoint().setNXButtonState(NXBUTTONINACTIVE); 601 } 602 } 603 for (SourceToDest rts : toRemove) { 604 routesToSet.remove(rts); 605 } 606 } 607 608 protected PropertyChangeListener propertyDestinationListener = new PropertyChangeListener() { 609 @Override 610 public void propertyChange(PropertyChangeEvent e) { 611 ((DestinationPoints) e.getSource()).removePropertyChangeListener(this); 612 if (e.getPropertyName().equals("active")) { 613 processRoutesToSet(); 614 } else if (e.getPropertyName().equals("stacked") || e.getPropertyName().equals("failed") || e.getPropertyName().equals("noChange")) { // NOI18N 615 removeRemainingRoute(); 616 } 617 } 618 }; 619 620 List<Object> destinationList = new ArrayList<>(); 621 622 // Need to sort out the presentation of the name here rather than using the point ID. 623 // This is used for the creation and display of information in the table. 624 // The presentation of the name might have to be done at the table level. 625 public List<Object> getNxSource(LayoutEditor panel) { 626 List<Object> source = new ArrayList<>(); 627 destinationList = new ArrayList<>(); 628 629 for (Entry<PointDetails, Source> e : nxpair.entrySet()) { 630 PointDetails key = e.getKey(); 631 LayoutEditor pan = key.getPanel(); 632 if (pan == panel) { 633 List<PointDetails> dest = nxpair.get(key).getDestinationPoints(); 634 for (int i = 0; i < dest.size(); i++) { 635 destinationList.add(dest.get(i).getRefObject()); 636 source.add(key.getRefObject()); 637 } 638 } 639 } 640 return source; 641 } 642 643 public List<Object> getNxDestination() { 644 return destinationList; 645 } 646 647 public List<LayoutEditor> getSourcePanelList() { 648 List<LayoutEditor> list = new ArrayList<>(); 649 650 for (Entry<PointDetails, Source> e : nxpair.entrySet()) { 651 PointDetails key = e.getKey(); 652 LayoutEditor pan = key.getPanel(); 653 if (!list.contains(pan)) { 654 list.add(pan); 655 } 656 } 657 return list; 658 } 659 660 /** 661 * Return a point if it already exists, or create a new one if not. 662 */ 663 private PointDetails providePoint(LayoutBlock source, List<LayoutBlock> protecting, LayoutEditor panel) { 664 PointDetails sourcePoint = getPointDetails(source, protecting, panel); 665 if (sourcePoint == null) { 666 sourcePoint = new PointDetails(source, protecting); 667 sourcePoint.setPanel(panel); 668 } 669 return sourcePoint; 670 } 671 672 /** 673 * @since 4.17.4 674 */ 675 @Override 676 public void propertyChange(PropertyChangeEvent evt) { 677 firePropertyChange("active", evt.getOldValue(), evt.getNewValue()); 678 } 679 680 681 public void addNXDestination(NamedBean source, NamedBean destination, LayoutEditor panel) { 682 addNXDestination(source, destination, panel, null); 683 } 684 685 /** 686 * @since 4.17.4 687 * Register in Property Change Listener. 688 * @param source the source bean. 689 * @param destination the destination bean. 690 * @param panel the layout editor panel. 691 * @param id the points details id. 692 */ 693 public void addNXDestination(NamedBean source, NamedBean destination, LayoutEditor panel, String id) { 694 if (source == null) { 695 log.error("no source Object provided"); // NOI18N 696 return; 697 } 698 if (destination == null) { 699 log.error("no destination Object provided"); // NOI18N 700 return; 701 } 702 PointDetails sourcePoint = providePoint(source, panel); 703 if (sourcePoint == null) { 704 log.error("source point for {} not created addNXDes", source.getDisplayName()); // NOI18N 705 return; 706 } 707 708 sourcePoint.setPanel(panel); 709 sourcePoint.setRefObject(source); 710 PointDetails destPoint = providePoint(destination, panel); 711 if (destPoint != null) { 712 destPoint.setPanel(panel); 713 destPoint.setRefObject(destination); 714 destPoint.getSignal(); 715 if (!nxpair.containsKey(sourcePoint)) { 716 Source sp = new Source(sourcePoint); 717 nxpair.put(sourcePoint, sp); 718 sp.removePropertyChangeListener(this); 719 sp.addPropertyChangeListener(this); 720 } 721 nxpair.get(sourcePoint).addDestination(destPoint, id); 722 } 723 724 firePropertyChange("length", null, null); // NOI18N 725 } 726 727 public List<Object> getDestinationList(Object obj, LayoutEditor panel) { 728 List<Object> list = new ArrayList<>(); 729 if (nxpair.containsKey(getPointDetails(obj, panel))) { 730 List<PointDetails> from = nxpair.get(getPointDetails(obj, panel)).getDestinationPoints(); 731 for (int i = 0; i < from.size(); i++) { 732 list.add(from.get(i).getRefObject()); 733 } 734 } 735 return list; 736 } 737 738 public void removeNXSensor(Sensor sensor) { 739 log.info("panel maintenance has resulting in the request to remove a sensor: {}", sensor.getDisplayName()); 740 } 741 742 // ============ NX Pair Delete Methods ============ 743 // The request will be for all NX Pairs containing a sensor or 744 // a specific entry and exit sensor pair. 745 746 /** 747 * Entry point to delete all of the NX pairs for a specific sensor. 748 * 1) Build a list of affected NX pairs. 749 * 2) Check for Conditional references. 750 * 3) If no references, do the delete process with user approval. 751 * @since 4.11.2 752 * @param sensor The sensor whose pairs should be deleted. 753 * @return true if the delete was successful. False if prevented by 754 * Conditional/LogixNG references or user choice. 755 */ 756 public boolean deleteNxPair(NamedBean sensor) { 757 if (sensor == null) { 758 log.error("deleteNxPair: sensor is null"); // NOI18N 759 return false; 760 } 761 createDeletePairList(sensor); 762 if (checkNxPairs() && checkLogixNG()) { 763 // No Conditional or LogixNG references. 764 if (confirmDeletePairs()) { 765 deleteNxPairs(); 766 return true; 767 } 768 } 769 return false; 770 } 771 772 /** 773 * Entry point to delete a specific NX pair. 774 * 775 * @since 4.11.2 776 * @param entrySensor The sensor that acts as the entry point. 777 * @param exitSensor The sensor that acts as the exit point. 778 * @param panel The layout editor panel that contains the entry sensor. 779 * @return true if the delete was successful. False if there are Conditional/LogixNG references. 780 */ 781 public boolean deleteNxPair(NamedBean entrySensor, NamedBean exitSensor, LayoutEditor panel) { 782 if (entrySensor == null || exitSensor == null || panel == null) { 783 log.error("deleteNxPair: One or more null inputs"); // NOI18N 784 return false; 785 } 786 787 deletePairList.clear(); 788 deletePairList.add(new DeletePair(entrySensor, exitSensor, panel)); 789 if (checkNxPairs() && checkLogixNG()) { 790 // No Conditional or LogixNG references. 791 deleteNxPairs(); // Delete with no prompt 792 return true; 793 } 794 795 return false; 796 } 797 798 /** 799 * Find Logix Conditionals that have Variables or Actions for the affected NX Pairs. 800 * If any are found, display a dialog box listing the Conditionals and return false. 801 * <p> 802 * @since 4.11.2 803 * @return true if there are no references. 804 */ 805 private boolean checkNxPairs() { 806 jmri.LogixManager mgr = InstanceManager.getDefault(jmri.LogixManager.class); 807 List<String> conditionalReferences = new ArrayList<>(); 808 for (DeletePair dPair : deletePairList) { 809 if (dPair.dp == null) { 810 continue; 811 } 812 for (jmri.Logix lgx : mgr.getNamedBeanSet()) { 813 for (int i = 0; i < lgx.getNumConditionals(); i++) { 814 String cdlName = lgx.getConditionalByNumberOrder(i); 815 jmri.implementation.DefaultConditional cdl = (jmri.implementation.DefaultConditional) lgx.getConditional(cdlName); 816 String cdlUserName = cdl.getUserName(); 817 if (cdlUserName == null) { 818 cdlUserName = ""; 819 } 820 for (jmri.ConditionalVariable var : cdl.getStateVariableList()) { 821 if (var.getBean() == dPair.dp) { 822 String refName = (cdlUserName.equals("")) ? cdlName : cdlName + " ( " + cdlUserName + " )"; 823 if (!conditionalReferences.contains(refName)) { 824 conditionalReferences.add(refName); 825 } 826 } 827 } 828 for (jmri.ConditionalAction act : cdl.getActionList()) { 829 if (act.getBean() == dPair.dp) { 830 String refName = (cdlUserName.equals("")) ? cdlName : cdlName + " ( " + cdlUserName + " )"; 831 if (!conditionalReferences.contains(refName)) { 832 conditionalReferences.add(refName); 833 } 834 } 835 } 836 } 837 } 838 } 839 if (conditionalReferences.isEmpty()) { 840 return true; 841 } 842 843 conditionalReferences.sort(null); 844 StringBuilder msg = new StringBuilder(Bundle.getMessage("DeleteReferences")); 845 for (String ref : conditionalReferences) { 846 msg.append("\n " + ref); // NOI18N 847 } 848 JmriJOptionPane.showMessageDialog(null, 849 msg.toString(), 850 Bundle.getMessage("WarningTitle"), // NOI18N 851 JmriJOptionPane.WARNING_MESSAGE); 852 853 return false; 854 } 855 856 /** 857 * Find LogixNG ConditionalNGs that have Expressions or Actions for the affected NX Pairs. 858 * If any are found, display a dialog box listing the details and return false. 859 * <p> 860 * @since 5.5.7 861 * @return true if there are no references. 862 */ 863 private boolean checkLogixNG() { 864 List<String> conditionalReferences = new ArrayList<>(); 865 for (DeletePair dPair : deletePairList) { 866 if (dPair.dp == null) { 867 continue; 868 } 869 var usage = jmri.jmrit.logixng.util.WhereUsed.whereUsed(dPair.dp); 870 if (!usage.isEmpty()) { 871 conditionalReferences.add(usage); 872 } 873 } 874 if (conditionalReferences.isEmpty()) { 875 return true; 876 } 877 878 conditionalReferences.sort(null); 879 StringBuilder msg = new StringBuilder(Bundle.getMessage("DeleteReferences")); 880 for (String ref : conditionalReferences) { 881 msg.append("\n" + ref); // NOI18N 882 } 883 JmriJOptionPane.showMessageDialog(null, 884 msg.toString(), 885 Bundle.getMessage("WarningTitle"), // NOI18N 886 JmriJOptionPane.WARNING_MESSAGE); 887 888 return false; 889 } 890 891 /** 892 * Display a list of pending deletes and ask for confirmation. 893 * @since 4.11.2 894 * @return true if deletion confirmation is Yes. 895 */ 896 private boolean confirmDeletePairs() { 897 if (!deletePairList.isEmpty()) { 898 StringBuilder msg = new StringBuilder(Bundle.getMessage("DeletePairs")); // NOI18N 899 for (DeletePair dPair : deletePairList) { 900 if (dPair.dp != null) { 901 msg.append("\n ").append(dPair.dp.getDisplayName()); // NOI18N 902 } 903 } 904 msg.append("\n").append(Bundle.getMessage("DeleteContinue")); // NOI18N 905 int resp = JmriJOptionPane.showConfirmDialog(null, 906 msg.toString(), 907 Bundle.getMessage("WarningTitle"), // NOI18N 908 JmriJOptionPane.YES_NO_OPTION, 909 JmriJOptionPane.QUESTION_MESSAGE); 910 if (resp != JmriJOptionPane.YES_OPTION ) { 911 return false; 912 } 913 } 914 return true; 915 } 916 917 /** 918 * Delete the pairs in the delete pair list. 919 * @since 4.11.2 920 * @since 4.17.4 921 * Remove from Change Listener. 922 */ 923 private void deleteNxPairs() { 924 for (DeletePair dp : deletePairList) { 925 PointDetails sourcePoint = getPointDetails(dp.src, dp.pnl); 926 PointDetails destPoint = getPointDetails(dp.dest, dp.pnl); 927 nxpair.get(sourcePoint).removeDestination(destPoint); 928 firePropertyChange("length", null, null); // NOI18N 929 if (nxpair.get(sourcePoint).getDestinationPoints().isEmpty()) { 930 nxpair.get(sourcePoint).removePropertyChangeListener(this); 931 nxpair.remove(sourcePoint); 932 } 933 } 934 } 935 936 /** 937 * List of NX pairs that are scheduled for deletion. 938 * @since 4.11.2 939 */ 940 List<DeletePair> deletePairList = new ArrayList<>(); 941 942 /** 943 * Class to store NX pair components. 944 * @since 4.11.2 945 */ 946 class DeletePair { 947 NamedBean src = null; 948 NamedBean dest = null; 949 LayoutEditor pnl = null; 950 DestinationPoints dp = null; 951 952 /** 953 * Constructor for a DeletePair row. 954 * 955 * @param src Source sensor bean 956 * @param dest Ddestination sensor bean 957 * @param pnl The LayoutEditor panel for the source bean 958 */ 959 DeletePair(NamedBean src, NamedBean dest, LayoutEditor pnl) { 960 this.src = src; 961 this.dest = dest; 962 this.pnl = pnl; 963 964 // Get the actual destination point, if any. 965 PointDetails sourcePoint = getPointDetails(src, pnl); 966 PointDetails destPoint = getPointDetails(dest, pnl); 967 if (sourcePoint != null && destPoint != null) { 968 if (nxpair.containsKey(sourcePoint)) { 969 this.dp = nxpair.get(sourcePoint).getDestForPoint(destPoint); 970 } 971 } 972 } 973 } 974 975 /** 976 * Rebuild the delete pair list based on the supplied sensor. 977 * Find all of the NX pairs that use this sensor as either a source or 978 * destination. They will be candidates for deletion. 979 * 980 * @since 4.11.2 981 * @param sensor The sensor being deleted, 982 */ 983 void createDeletePairList(NamedBean sensor) { 984 deletePairList.clear(); 985 nxpair.forEach((pdSrc, src) -> { 986 Sensor sBean = pdSrc.getSensor(); 987 LayoutEditor sPanel = pdSrc.getPanel(); 988 for (PointDetails pdDest : src.getDestinationPoints()) { 989 Sensor dBean = pdDest.getSensor(); 990 if (sensor == sBean || sensor == dBean) { 991 log.debug("Delete pair: {} to {}, panel = {}", // NOI18N 992 sBean.getDisplayName(), dBean.getDisplayName(), sPanel.getLayoutName()); 993 deletePairList.add(new DeletePair(sBean, dBean, sPanel)); 994 } 995 } 996 }); 997 } 998 999 // ============ End NX Pair Delete Methods ============ 1000 1001 /** 1002 * Create a list of sensors that have the layout block as either 1003 * facing or protecting. 1004 * Called by {@link jmri.jmrit.display.layoutEditor.LayoutEditorDialogs.LayoutTrackEditor#hasNxSensorPairs}. 1005 * @since 4.11.2 1006 * @param layoutBlock The layout block to be checked. 1007 * @return the a list of sensors affected by the layout block or an empty list. 1008 */ 1009 public List<String> layoutBlockSensors(@Nonnull LayoutBlock layoutBlock) { 1010 log.debug("layoutBlockSensors: {}", layoutBlock.getDisplayName()); 1011 List<String> blockSensors = new ArrayList<>(); 1012 nxpair.forEach((pdSrc, src) -> { 1013 Sensor sBean = pdSrc.getSensor(); 1014 for (LayoutBlock sProtect : pdSrc.getProtecting()) { 1015 if (layoutBlock == pdSrc.getFacing() || layoutBlock == sProtect) { 1016 log.debug(" Source = '{}', Facing = '{}', Protecting = '{}' ", 1017 sBean.getDisplayName(), pdSrc.getFacing().getDisplayName(), sProtect.getDisplayName()); 1018 blockSensors.add(sBean.getDisplayName()); 1019 } 1020 } 1021 1022 for (PointDetails pdDest : src.getDestinationPoints()) { 1023 Sensor dBean = pdDest.getSensor(); 1024 for (LayoutBlock dProtect : pdDest.getProtecting()) { 1025 if (layoutBlock == pdDest.getFacing() || layoutBlock == dProtect) { 1026 log.debug(" Destination = '{}', Facing = '{}', Protecting = '{}' ", 1027 dBean.getDisplayName(), pdDest.getFacing().getDisplayName(), dProtect.getDisplayName()); 1028 blockSensors.add(dBean.getDisplayName()); 1029 } 1030 } 1031 } 1032 }); 1033 return blockSensors; 1034 } 1035 1036 public boolean isDestinationValid(Object source, Object dest, LayoutEditor panel) { 1037 if (nxpair.containsKey(getPointDetails(source, panel))) { 1038 return nxpair.get(getPointDetails(source, panel)).isDestinationValid(getPointDetails(dest, panel)); 1039 } 1040 return false; 1041 } 1042 1043 public boolean isUniDirection(Object source, LayoutEditor panel, Object dest) { 1044 if (nxpair.containsKey(getPointDetails(source, panel))) { 1045 return nxpair.get(getPointDetails(source, panel)).getUniDirection(dest, panel); 1046 } 1047 return false; 1048 } 1049 1050 public void setUniDirection(Object source, LayoutEditor panel, Object dest, boolean set) { 1051 if (nxpair.containsKey(getPointDetails(source, panel))) { 1052 nxpair.get(getPointDetails(source, panel)).setUniDirection(dest, panel, set); 1053 } 1054 } 1055 1056 public boolean canBeBiDirectional(Object source, LayoutEditor panel, Object dest) { 1057 if (nxpair.containsKey(getPointDetails(source, panel))) { 1058 return nxpair.get(getPointDetails(source, panel)).canBeBiDirection(dest, panel); 1059 } 1060 return false; 1061 } 1062 1063 public boolean isEnabled(Object source, LayoutEditor panel, Object dest) { 1064 if (nxpair.containsKey(getPointDetails(source, panel))) { 1065 return nxpair.get(getPointDetails(source, panel)).isEnabled(dest, panel); 1066 } 1067 return false; 1068 } 1069 1070 public void setEnabled(Object source, LayoutEditor panel, Object dest, boolean set) { 1071 if (nxpair.containsKey(getPointDetails(source, panel))) { 1072 nxpair.get(getPointDetails(source, panel)).setEnabled(dest, panel, set); 1073 } 1074 } 1075 1076 public void setEntryExitType(Object source, LayoutEditor panel, Object dest, int set) { 1077 if (nxpair.containsKey(getPointDetails(source, panel))) { 1078 nxpair.get(getPointDetails(source, panel)).setEntryExitType(dest, panel, set); 1079 } 1080 } 1081 1082 public int getEntryExitType(Object source, LayoutEditor panel, Object dest) { 1083 if (nxpair.containsKey(getPointDetails(source, panel))) { 1084 return nxpair.get(getPointDetails(source, panel)).getEntryExitType(dest, panel); 1085 } 1086 return 0x00; 1087 } 1088 1089 public String getUniqueId(Object source, LayoutEditor panel, Object dest) { 1090 if (nxpair.containsKey(getPointDetails(source, panel))) { 1091 return nxpair.get(getPointDetails(source, panel)).getUniqueId(dest, panel); 1092 } 1093 return null; 1094 } 1095 1096 public List<String> getEntryExitList() { 1097 List<String> destlist = new ArrayList<>(); 1098 for (Source e : nxpair.values()) { 1099 destlist.addAll(e.getDestinationUniqueId()); 1100 } 1101 return destlist; 1102 } 1103 1104 // protecting helps us to determine which direction we are going. 1105 // validateOnly flag is used, if all we are doing is simply checking to see if the source/destpoints are valid 1106 // when creating the pairs in the user GUI 1107 public boolean isPathActive(Object sourceObj, Object destObj, LayoutEditor panel) { 1108 PointDetails pd = getPointDetails(sourceObj, panel); 1109 if (nxpair.containsKey(pd)) { 1110 Source source = nxpair.get(pd); 1111 return source.isRouteActive(getPointDetails(destObj, panel)); 1112 } 1113 return false; 1114 } 1115 1116 public void cancelInterlock(Object source, LayoutEditor panel, Object dest) { 1117 if (nxpair.containsKey(getPointDetails(source, panel))) { 1118 nxpair.get(getPointDetails(source, panel)).cancelInterlock(dest, panel); 1119 } 1120 1121 } 1122 1123 jmri.SignalMastLogicManager smlm = InstanceManager.getDefault(jmri.SignalMastLogicManager.class); 1124 1125 public final static int CANCELROUTE = 0; 1126 public final static int CLEARROUTE = 1; 1127 public final static int EXITROUTE = 2; 1128 public final static int STACKROUTE = 4; 1129 1130 /** 1131 * Return a point from a given LE Panel. 1132 * 1133 * @param obj The point object 1134 * @param panel The Layout Editor panel on which the point was placed 1135 * @return the point object, null if the point is not found 1136 */ 1137 public PointDetails getPointDetails(Object obj, LayoutEditor panel) { 1138 for (int i = 0; i < pointDetails.size(); i++) { 1139 if ((pointDetails.get(i).getRefObject() == obj)) { 1140 return pointDetails.get(i); 1141 1142 } 1143 } 1144 return null; 1145 } 1146 1147 /** 1148 * Return either an existing point stored in pointDetails, or create a new one as required. 1149 * 1150 * @param source The Layout Block functioning as the source (origin) 1151 * @param destination A (list of) Layout Blocks functioning as destinations 1152 * @param panel The Layout Editor panel on which the point is to be placed 1153 * @return the point object 1154 */ 1155 PointDetails getPointDetails(LayoutBlock source, List<LayoutBlock> destination, LayoutEditor panel) { 1156 PointDetails newPoint = new PointDetails(source, destination); 1157 newPoint.setPanel(panel); 1158 for (int i = 0; i < pointDetails.size(); i++) { 1159 if (pointDetails.get(i).equals(newPoint)) { 1160 return pointDetails.get(i); 1161 } 1162 } 1163 //Not found so will add 1164 pointDetails.add(newPoint); 1165 return newPoint; 1166 } 1167 1168 //No point can have multiple copies of what is the same thing. 1169 static List<PointDetails> pointDetails = new ArrayList<PointDetails>(); 1170 1171 /** 1172 * Get the name of a destinationPoint on a LE Panel. 1173 * 1174 * @param obj the point object 1175 * @param panel The Layout Editor panel on which it is expected to be placed 1176 * @return the name of the point 1177 */ 1178 public String getPointAsString(NamedBean obj, LayoutEditor panel) { 1179 if (obj == null) { 1180 return "null"; // NOI18N 1181 } 1182 PointDetails valid = getPointDetails(obj, panel); //was just plain getPoint 1183 if (valid != null) { 1184 return valid.getDisplayName(); 1185 } 1186 return "empty"; // NOI18N 1187 } 1188 1189 List<StackDetails> stackList = new ArrayList<>(); 1190 1191 /** 1192 * If a route is requested but is currently blocked, ask user 1193 * if it should be added to stackList. 1194 * 1195 * @param dp DestinationPoints object 1196 * @param reverse true for a reversed running direction, mostly false 1197 */ 1198 synchronized public void stackNXRoute(DestinationPoints dp, boolean reverse) { 1199 if (isRouteStacked(dp, reverse)) { 1200 return; 1201 } 1202 stackList.add(new StackDetails(dp, reverse)); 1203 checkTimer.start(); 1204 if (stackPanel == null) { 1205 stackPanel = new StackNXPanel(); 1206 } 1207 if (stackDialog == null) { 1208 stackDialog = new JDialog(); 1209 stackDialog.setTitle(Bundle.getMessage("WindowTitleStackRoutes")); // NOI18N 1210 stackDialog.add(stackPanel); 1211 } 1212 stackPanel.updateGUI(); 1213 1214 stackDialog.pack(); 1215 stackDialog.setModal(false); 1216 stackDialog.setVisible(true); 1217 } 1218 1219 StackNXPanel stackPanel = null; 1220 JDialog stackDialog = null; 1221 1222 /** 1223 * Get a list of all stacked routes from stackList. 1224 * 1225 * @return an List containing destinationPoint elements 1226 */ 1227 public List<DestinationPoints> getStackedInterlocks() { 1228 List<DestinationPoints> dpList = new ArrayList<>(); 1229 for (StackDetails st : stackList) { 1230 dpList.add(st.getDestinationPoint()); 1231 } 1232 return dpList; 1233 } 1234 1235 /** 1236 * Query if a stacked route is in stackList. 1237 * 1238 * @param dp DestinationPoints object 1239 * @param reverse true for a reversed running direction, mostly false 1240 * @return true if dp is in stackList 1241 */ 1242 public boolean isRouteStacked(DestinationPoints dp, boolean reverse) { 1243 Iterator<StackDetails> iter = stackList.iterator(); 1244 while (iter.hasNext()) { 1245 StackDetails st = iter.next(); 1246 if (st.getDestinationPoint() == dp && st.getReverse() == reverse) { 1247 return true; 1248 } 1249 } 1250 return false; 1251 } 1252 1253 /** 1254 * Remove a stacked route from stackList. 1255 * 1256 * @param dp DestinationPoints object 1257 * @param reverse true for a reversed running direction, mostly false 1258 */ 1259 synchronized public void cancelStackedRoute(DestinationPoints dp, boolean reverse) { 1260 Iterator<StackDetails> iter = stackList.iterator(); 1261 while (iter.hasNext()) { 1262 StackDetails st = iter.next(); 1263 if (st.getDestinationPoint() == dp && st.getReverse() == reverse) { 1264 iter.remove(); 1265 } 1266 } 1267 stackPanel.updateGUI(); 1268 if (stackList.isEmpty()) { 1269 stackDialog.setVisible(false); 1270 checkTimer.stop(); 1271 } 1272 } 1273 1274 /** 1275 * Class to collect (stack) routes when they are requested but blocked. 1276 */ 1277 static class StackDetails { 1278 1279 DestinationPoints dp; 1280 boolean reverse; 1281 1282 StackDetails(DestinationPoints dp, boolean reverse) { 1283 this.dp = dp; 1284 this.reverse = reverse; 1285 } 1286 1287 boolean getReverse() { 1288 return reverse; 1289 } 1290 1291 DestinationPoints getDestinationPoint() { 1292 return dp; 1293 } 1294 } 1295 1296 javax.swing.Timer checkTimer = new javax.swing.Timer(10000, (java.awt.event.ActionEvent e) -> { 1297 checkRoute(); 1298 }); 1299 1300 /** 1301 * Step through stackList and activate the first stacked route in line 1302 * if it is no longer blocked. 1303 */ 1304 synchronized void checkRoute() { 1305 checkTimer.stop(); 1306 StackDetails[] tmp = new StackDetails[stackList.size()]; 1307 stackList.toArray(tmp); 1308 1309 for (StackDetails st : tmp) { 1310 if (!st.getDestinationPoint().isActive()) { 1311 // If the route is not already active, then check. 1312 // If the route does get set, then the setting process will remove the route from the stack. 1313 st.getDestinationPoint().setInterlockRoute(st.getReverse()); 1314 } 1315 } 1316 1317 if (!stackList.isEmpty()) { 1318 checkTimer.start(); 1319 } else { 1320 stackDialog.setVisible(false); 1321 } 1322 } 1323 1324 public void removePropertyChangeListener(PropertyChangeListener list, NamedBean obj, LayoutEditor panel) { 1325 if (obj == null) { 1326 return; 1327 } 1328 PointDetails valid = getPointDetails(obj, panel); 1329 if (valid != null) { 1330 valid.removePropertyChangeListener(list); 1331 } 1332 } 1333 1334 boolean runWhenStabilised = false; 1335 LayoutEditor toUseWhenStable; 1336 int interlockTypeToUseWhenStable; 1337 1338 /** 1339 * Discover all possible valid source and destination Signal Mast Logic pairs 1340 * on all Layout Editor panels. 1341 * 1342 * @param editor The Layout Editor panel 1343 * @param interlockType Integer value representing the type of interlocking, one of 1344 * SETUPTURNOUTSONLY, SETUPSIGNALMASTLOGIC or FULLINTERLOCK 1345 * @throws JmriException when an error occurs during discovery 1346 */ 1347 public void automaticallyDiscoverEntryExitPairs(LayoutEditor editor, int interlockType) throws JmriException { 1348 //This is almost a duplicate of that in the DefaultSignalMastLogicManager 1349 runWhenStabilised = false; 1350 jmri.jmrit.display.layoutEditor.LayoutBlockManager lbm = InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class); 1351 if (!lbm.isAdvancedRoutingEnabled()) { 1352 throw new JmriException("advanced routing not enabled"); // NOI18N 1353 } 1354 if (!lbm.routingStablised()) { 1355 runWhenStabilised = true; 1356 toUseWhenStable = editor; 1357 interlockTypeToUseWhenStable = interlockType; 1358 log.debug("Layout block routing has not yet stabilised, discovery will happen once it has"); // NOI18N 1359 return; 1360 } 1361 HashMap<NamedBean, List<NamedBean>> validPaths = lbm.getLayoutBlockConnectivityTools(). 1362 discoverValidBeanPairs(null, Sensor.class, LayoutBlockConnectivityTools.Routing.SENSORTOSENSOR); 1363 EntryExitPairs eep = this; 1364 for (Entry<NamedBean, List<NamedBean>> entry : validPaths.entrySet()) { 1365 NamedBean key = entry.getKey(); 1366 List<NamedBean> validDestMast = validPaths.get(key); 1367 if (validDestMast.size() > 0) { 1368 eep.addNXSourcePoint(key, editor); 1369 for (int i = 0; i < validDestMast.size(); i++) { 1370 if (!eep.isDestinationValid(key, validDestMast.get(i), editor)) { 1371 eep.addNXDestination(key, validDestMast.get(i), editor); 1372 eep.setEntryExitType(key, editor, validDestMast.get(i), interlockType); 1373 } 1374 } 1375 } 1376 } 1377 1378 firePropertyChange("autoGenerateComplete", null, null); // NOI18N 1379 } 1380 1381 protected PropertyChangeListener propertyBlockManagerListener = new PropertyChangeListener() { 1382 @Override 1383 public void propertyChange(PropertyChangeEvent e) { 1384 if (e.getPropertyName().equals("topology")) { // NOI18N 1385 //boolean newValue = new Boolean.parseBoolean(String.valueOf(e.getNewValue())); 1386 boolean newValue = (Boolean) e.getNewValue(); 1387 if (newValue) { 1388 if (runWhenStabilised) { 1389 try { 1390 automaticallyDiscoverEntryExitPairs(toUseWhenStable, interlockTypeToUseWhenStable); 1391 } catch (JmriException je) { 1392 //Considered normal if routing not enabled 1393 } 1394 } 1395 } 1396 } 1397 } 1398 }; 1399 1400 public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { 1401 1402 } 1403 1404 @Override 1405 public void deleteBean(@Nonnull DestinationPoints bean, @Nonnull String property) throws PropertyVetoException { 1406 1407 } 1408 1409 @Override 1410 @Nonnull 1411 public String getBeanTypeHandled(boolean plural) { 1412 return Bundle.getMessage(plural ? "BeanNameTransits" : "BeanNameTransit"); // NOI18N 1413 } 1414 1415 /** 1416 * {@inheritDoc} 1417 */ 1418 @Override 1419 public Class<DestinationPoints> getNamedBeanClass() { 1420 return DestinationPoints.class; 1421 } 1422 1423 /** 1424 * {@inheritDoc} 1425 */ 1426 @Override 1427 @OverridingMethodsMustInvokeSuper 1428 public void setPropertyChangesSilenced(@Nonnull String propertyName, boolean silenced) { 1429 if (!"beans".equals(propertyName)) { 1430 throw new IllegalArgumentException("Property " + propertyName + " cannot be silenced."); 1431 } 1432 silencedProperties.put(propertyName, silenced); 1433 if (propertyName.equals("beans") && !silenced) { 1434 fireIndexedPropertyChange("beans", getNamedBeanSet().size(), null, null); 1435 } 1436 } 1437 1438 /** {@inheritDoc} */ 1439 @Override 1440 public void addDataListener(ManagerDataListener<DestinationPoints> e) { 1441 if (e != null) listeners.add(e); 1442 } 1443 1444 /** {@inheritDoc} */ 1445 @Override 1446 public void removeDataListener(ManagerDataListener<DestinationPoints> e) { 1447 if (e != null) listeners.remove(e); 1448 } 1449 1450 final List<ManagerDataListener<DestinationPoints>> listeners = new ArrayList<>(); 1451 1452 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EntryExitPairs.class); 1453 1454}