001package jmri.jmrit.display.layoutEditor; 002 003import java.awt.Color; 004import java.awt.Component; 005import java.awt.event.ActionEvent; 006import java.beans.*; 007import java.util.*; 008 009import javax.annotation.CheckForNull; 010import javax.annotation.Nonnull; 011import javax.swing.*; 012import javax.swing.colorchooser.AbstractColorChooserPanel; 013 014import jmri.*; 015import jmri.implementation.AbstractNamedBean; 016import jmri.jmrit.beantable.beanedit.*; 017import jmri.jmrit.roster.RosterEntry; 018import jmri.swing.NamedBeanComboBox; 019import jmri.util.MathUtil; 020import jmri.util.swing.JmriColorChooser; 021import jmri.util.swing.JmriJOptionPane; 022import jmri.util.swing.SplitButtonColorChooserPanel; 023 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026import org.slf4j.MDC; 027 028/** 029 * A LayoutBlock is a group of track segments and turnouts on a LayoutEditor 030 * panel corresponding to a 'block'. LayoutBlock is a LayoutEditor specific 031 * extension of the JMRI Block object. 032 * <p> 033 * LayoutBlocks may have an occupancy Sensor. The getOccupancy method returns 034 * the occupancy state of the LayoutBlock - OCCUPIED, EMPTY, or UNKNOWN. If no 035 * occupancy sensor is provided, UNKNOWN is returned. The occupancy sensor if 036 * there is one, is the same as the occupancy sensor of the corresponding JMRI 037 * Block. 038 * <p> 039 * The name of each Layout Block is the same as that of the corresponding block 040 * as defined in Layout Editor. A corresponding JMRI Block object is created 041 * when a LayoutBlock is created. The JMRI Block uses the name of the block 042 * defined in Layout Editor as its user name and a unique IBnnn system name. The 043 * JMRI Block object and its associated Path objects are useful in tracking a 044 * train around the layout. Blocks may be viewed in the Block Table. 045 * <p> 046 * A LayoutBlock may have an associated Memory object. This Memory object 047 * contains a string representing the current "value" of the corresponding JMRI 048 * Block object. If the value contains a train name, for example, displaying 049 * Memory objects associated with LayoutBlocks, and displayed near each Layout 050 * Block can follow a train around the layout, displaying its name when it is in 051 * the LayoutBlock. 052 * <p> 053 * LayoutBlocks are "cross-panel", similar to sensors and turnouts. A 054 * LayoutBlock may be used by more than one Layout Editor panel simultaneously. 055 * As a consequence, LayoutBlocks are saved with the configuration, not with a 056 * panel. 057 * <p> 058 * LayoutBlocks are used by TrackSegments, LevelXings, and LayoutTurnouts. 059 * LevelXings carry two LayoutBlock designations, which may be the same. 060 * LayoutTurnouts carry LayoutBlock designations also, one per turnout, except 061 * for double crossovers and slips which can have up to four. 062 * <p> 063 * LayoutBlocks carry a use count. The use count counts the number of track 064 * segments, layout turnouts, and levelcrossings which use the LayoutBlock. Only 065 * LayoutBlocks which have a use count greater than zero are saved when the 066 * configuration is saved. 067 * 068 * @author Dave Duchamp Copyright (c) 2004-2008 069 * @author George Warner Copyright (c) 2017-2019 070 */ 071public class LayoutBlock extends AbstractNamedBean implements PropertyChangeListener { 072 073 private static final List<Integer> updateReferences = new ArrayList<>(500); 074 075 // might want to use the jmri ordered HashMap, so that we can add at the top 076 // and remove at the bottom. 077 private final List<Integer> actedUponUpdates = new ArrayList<>(500); 078 079 @Deprecated (since="5.11.2",forRemoval=true) // please use the SLF4J categories. 080 public void enableDeleteRouteLog() { 081 jmri.util.LoggingUtil.warnOnce( log, "Deprecated, please use the SLF4J categories"); 082 } 083 084 @Deprecated (since="5.11.2",forRemoval=true) // please use the SLF4J categories. 085 public void disableDeleteRouteLog() { 086 jmri.util.LoggingUtil.warnOnce( log, "Deprecated, please use the SLF4J categories"); 087 } 088 089 // constants 090 public static final int OCCUPIED = Block.OCCUPIED; 091 public static final int EMPTY = Block.UNOCCUPIED; 092 // operational instance variables (not saved to disk) 093 private int useCount = 0; 094 private NamedBeanHandle<Sensor> occupancyNamedSensor = null; 095 private NamedBeanHandle<Memory> namedMemory = null; 096 private boolean setSensorFromBlockEnabled = true; // Controls whether getOccupancySensor should get the sensor from the block 097 098 private Block block = null; 099 100 private final List<LayoutEditor> panels = new ArrayList<>(); // panels using this block 101 private PropertyChangeListener mBlockListener = null; 102 private int jmriblknum = 1; 103 private boolean useExtraColor = false; 104 private boolean suppressNameUpdate = false; 105 106 // persistent instances variables (saved between sessions) 107 private String occupancySensorName = ""; 108 private String memoryName = ""; 109 private int occupiedSense = Sensor.ACTIVE; 110 private Color blockTrackColor = Color.darkGray; 111 private Color blockOccupiedColor = Color.red; 112 private Color blockExtraColor = Color.white; 113 114 /** 115 * Creates a LayoutBlock object. 116 * 117 * Note: initializeLayoutBlock() must be called to complete the process. They are split 118 * so that loading of panel files will be independent of whether LayoutBlocks or 119 * Blocks are loaded first. 120 * @param sName System name of this LayoutBlock 121 * @param uName User name of this LayoutBlock but also the user name of the associated Block 122 */ 123 public LayoutBlock(String sName, String uName) { 124 super(sName, uName); 125 } 126 127 /** 128 * Completes the creation of a LayoutBlock object by adding a Block to it. 129 * 130 * The block create process takes into account that the _bean register 131 * process considers IB1 and IB01 to be the same name which results in a 132 * silent failure. 133 */ 134 public void initializeLayoutBlock() { 135 // get/create a Block object corresponding to this LayoutBlock 136 block = null; // assume failure (pessimist!) 137 String userName = getUserName(); 138 if ((userName != null) && !userName.isEmpty()) { 139 block = InstanceManager.getDefault(BlockManager.class).getByUserName(userName); 140 } 141 142 if (block == null) { 143 // Not found, create a new Block 144 BlockManager bm = InstanceManager.getDefault(BlockManager.class); 145 String s; 146 while (true) { 147 if (jmriblknum > 50000) { 148 throw new IndexOutOfBoundsException("Run away prevented while trying to create a block"); 149 } 150 s = "IB" + jmriblknum; 151 jmriblknum++; 152 153 // Find an unused system name 154 block = bm.getBySystemName(s); 155 if (block != null) { 156 log.debug("System name is already used: {}", s); 157 continue; 158 } 159 160 // Create a new block. User name is null to prevent user name checking. 161 block = bm.createNewBlock(s, null); 162 if (block == null) { 163 log.debug("Null block returned: {}", s); 164 continue; 165 } 166 167 // Verify registration 168 Block testGet = bm.getBySystemName(s); 169 if ( testGet!=null && bm.getNamedBeanSet().contains(testGet) ) { 170 log.debug("Block is valid: {}", s); 171 break; 172 } 173 log.debug("Registration failed: {}", s); 174 } 175 block.setUserName(getUserName()); 176 } 177 178 // attach a listener for changes in the Block 179 mBlockListener = this::handleBlockChange; 180 block.addPropertyChangeListener(mBlockListener, 181 getUserName(), "Layout Block:" + getUserName()); 182 if (occupancyNamedSensor != null) { 183 block.setNamedSensor(occupancyNamedSensor); 184 } 185 } 186 187 /* initializeLayoutBlockRouting */ 188 public void initializeLayoutBlockRouting() { 189 if (!InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled()) { 190 return; 191 } 192 setBlockMetric(); 193 194 block.getPaths().stream().forEach(this::addAdjacency); 195 } 196 197 /* 198 * Accessor methods 199 */ 200 // TODO: deprecate and just use getUserName() directly 201 public String getId() { 202 return getUserName(); 203 } 204 205 public Color getBlockTrackColor() { 206 return blockTrackColor; 207 } 208 209 public void setBlockTrackColor(Color color) { 210 blockTrackColor = color; 211 JmriColorChooser.addRecentColor(color); 212 } 213 214 public Color getBlockOccupiedColor() { 215 return blockOccupiedColor; 216 } 217 218 public void setBlockOccupiedColor(Color color) { 219 blockOccupiedColor = color; 220 JmriColorChooser.addRecentColor(color); 221 } 222 223 public Color getBlockExtraColor() { 224 return blockExtraColor; 225 } 226 227 public void setBlockExtraColor(Color color) { 228 blockExtraColor = color; 229 JmriColorChooser.addRecentColor(color); 230 } 231 232 // TODO: Java standard pattern for boolean getters is "useExtraColor()" 233 public boolean getUseExtraColor() { 234 return useExtraColor; 235 } 236 237 public void setUseExtraColor(boolean b) { 238 useExtraColor = b; 239 240 if (InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled()) { 241 stateUpdate(); 242 } 243 if (getBlock() != null) { 244 getBlock().setAllocated(b); 245 } 246 } 247 248 /* setUseExtraColor */ 249 public void incrementUse() { 250 useCount++; 251 } 252 253 public void decrementUse() { 254 --useCount; 255 if (useCount <= 0) { 256 useCount = 0; 257 } 258 } 259 260 public int getUseCount() { 261 return useCount; 262 } 263 264 /** 265 * Keep track of LayoutEditor panels that are using this LayoutBlock. 266 * 267 * @param panel to keep track of 268 */ 269 public void addLayoutEditor(LayoutEditor panel) { 270 // add to the panels list if not already there 271 if (!panels.contains(panel)) { 272 panels.add(panel); 273 } 274 } 275 276 public void deleteLayoutEditor(LayoutEditor panel) { 277 // remove from the panels list if there 278 if (panels.contains(panel)) { 279 panels.remove(panel); 280 } 281 } 282 283 public boolean isOnPanel(LayoutEditor panel) { 284 // returns true if this Layout Block is used on panel 285 return panels.contains(panel); 286 } 287 288 /** 289 * Redraw panels using this layout block. 290 */ 291 public void redrawLayoutBlockPanels() { 292 panels.stream().forEach(LayoutEditor::redrawPanel); 293 firePropertyChange("redraw", null, null); 294 } 295 296 /** 297 * Validate that the supplied occupancy sensor name corresponds to an 298 * existing sensor and is unique among all blocks. If valid, returns the 299 * sensor and sets the block sensor name in the block. Else returns null, 300 * and does nothing to the block. 301 * 302 * @param sensorName to check 303 * @param openFrame determines the <code>Frame</code> in which the dialog 304 * is displayed; if <code>null</code>, or if the 305 * <code>parentComponent</code> has no <code>Frame</code>, 306 * a default <code>Frame</code> is used 307 * @return the validated sensor 308 */ 309 public Sensor validateSensor(String sensorName, Component openFrame) { 310 // check if anything entered 311 if ((sensorName == null) || sensorName.isEmpty()) { 312 // no sensor name entered 313 if (occupancyNamedSensor != null) { 314 setOccupancySensorName(null); 315 } 316 return null; 317 } 318 319 // get the sensor corresponding to this name 320 Sensor s = InstanceManager.sensorManagerInstance().getSensor(sensorName); 321 if (s == null) { 322 // There is no sensor corresponding to this name 323 JmriJOptionPane.showMessageDialog(openFrame, 324 java.text.MessageFormat.format(Bundle.getMessage("Error7"), 325 new Object[]{sensorName}), 326 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 327 return null; 328 } 329 330 // ensure that this sensor is unique among defined Layout Blocks 331 NamedBeanHandle<Sensor> savedNamedSensor = occupancyNamedSensor; 332 occupancyNamedSensor = null; 333 LayoutBlock b = InstanceManager.getDefault(LayoutBlockManager.class). 334 getBlockWithSensorAssigned(s); 335 336 if (b != this) { 337 if (b != null) { 338 if (b.getUseCount() > 0) { 339 // new sensor is not unique, return to the old one 340 occupancyNamedSensor = savedNamedSensor; 341 JmriJOptionPane.showMessageDialog(openFrame, 342 Bundle.getMessage("Error6", sensorName, b.getId()), 343 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 344 return null; 345 } else { 346 // the user is assigning a sensor which is already assigned to 347 // layout block b. Layout block b is no longer in use so this 348 // should be fine but it's technically possible to put 349 // this discarded layout block back into service (possibly 350 // by mistake) by entering its name in any edit layout block window. 351 // That would cause a problem with the sensor being in use in 352 // two active blocks, so as a precaution we remove the sensor 353 // from the discarded block here. 354 b.setOccupancySensorName(null); 355 } 356 } 357 // sensor is unique, or was only in use on a layout block not in use 358 setOccupancySensorName(sensorName); 359 } 360 return s; 361 } 362 363 /** 364 * Validate that the memory name corresponds to an existing memory. If 365 * valid, returns the memory. Else returns null, and notifies the user. 366 * 367 * @param memName the memory name 368 * @param openFrame the frame to display any error dialog in 369 * @return the memory 370 */ 371 public Memory validateMemory(String memName, Component openFrame) { 372 // check if anything entered 373 if ((memName == null) || memName.isEmpty()) { 374 // no memory entered 375 return null; 376 } 377 // get the memory corresponding to this name 378 Memory m = InstanceManager.memoryManagerInstance().getMemory(memName); 379 if (m == null) { 380 // There is no memory corresponding to this name 381 JmriJOptionPane.showMessageDialog(openFrame, 382 java.text.MessageFormat.format(Bundle.getMessage("Error16"), 383 new Object[]{memName}), 384 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 385 return null; 386 } 387 memoryName = memName; 388 389 // Go through the memory icons on the panel and see if any are linked to this layout block 390 if ((m != getMemory()) && (panels.size() > 0)) { 391 boolean updateall = false; 392 boolean found = false; 393 for (LayoutEditor panel : panels) { 394 for (MemoryIcon memIcon : panel.getMemoryLabelList()) { 395 if (memIcon.getLayoutBlock() == this) { 396 if (!updateall && !found) { 397 int n = JmriJOptionPane.showConfirmDialog( 398 openFrame, 399 "Would you like to update all memory icons on the panel linked to the block to use the new one?", 400 "Update Memory Icons", 401 JmriJOptionPane.YES_NO_OPTION); 402 // TODO I18N in Bundle.properties 403 found = true; 404 if (n == JmriJOptionPane.YES_OPTION ) { 405 updateall = true; 406 } 407 } 408 if (updateall) { 409 memIcon.setMemory(memoryName); 410 } 411 } 412 } 413 } 414 } 415 return m; 416 } 417 418 /** 419 * Get the color for drawing items in this block. Returns color based on 420 * block occupancy. 421 * 422 * @return color for block 423 */ 424 public Color getBlockColor() { 425 if (getOccupancy() == OCCUPIED) { 426 return blockOccupiedColor; 427 } else if (useExtraColor) { 428 return blockExtraColor; 429 } else { 430 return blockTrackColor; 431 } 432 } 433 434 /** 435 * Get the Block corresponding to this LayoutBlock. 436 * 437 * @return block 438 */ 439 public Block getBlock() { 440 return block; 441 } 442 443 /** 444 * Returns Memory name 445 * 446 * @return name of memory 447 */ 448 public String getMemoryName() { 449 if (namedMemory != null) { 450 return namedMemory.getName(); 451 } 452 return memoryName; 453 } 454 455 /** 456 * Get Memory. 457 * 458 * @return memory bean 459 */ 460 public Memory getMemory() { 461 if (namedMemory == null) { 462 setMemoryName(memoryName); 463 } 464 if (namedMemory != null) { 465 return namedMemory.getBean(); 466 } 467 return null; 468 } 469 470 /** 471 * Add Memory by name. 472 * 473 * @param name for memory 474 */ 475 public void setMemoryName(String name) { 476 if ((name == null) || name.isEmpty()) { 477 namedMemory = null; 478 memoryName = ""; 479 return; 480 } 481 memoryName = name; 482 Memory memory = InstanceManager.memoryManagerInstance().getMemory(name); 483 if (memory != null) { 484 namedMemory = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(name, memory); 485 } 486 } 487 488 public void setMemory(Memory m, String name) { 489 if (m == null) { 490 namedMemory = null; 491 memoryName = name == null ? "" : name; 492 return; 493 } 494 namedMemory = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(name, m); 495 } 496 497 /** 498 * Get occupancy Sensor name. 499 * 500 * @return name of occupancy sensor 501 */ 502 public String getOccupancySensorName() { 503 if (occupancyNamedSensor == null) { 504 if (block != null) { 505 occupancyNamedSensor = block.getNamedSensor(); 506 } 507 } 508 if (occupancyNamedSensor != null) { 509 return occupancyNamedSensor.getName(); 510 } 511 return occupancySensorName; 512 } 513 514 /** 515 * Get occupancy Sensor. 516 * <p> 517 * If a sensor has not been assigned, try getting the sensor from the related 518 * block. 519 * <p> 520 * When setting the layout block sensor from the block itself using the OccupancySensorChange 521 * event, the automatic assignment has to be disabled for the sensor checking performed by 522 * {@link jmri.jmrit.display.layoutEditor.LayoutBlockManager#getBlockWithSensorAssigned} 523 * 524 * @return occupancy sensor or null 525 */ 526 public Sensor getOccupancySensor() { 527 if (occupancyNamedSensor == null && setSensorFromBlockEnabled) { 528 if (block != null) { 529 occupancyNamedSensor = block.getNamedSensor(); 530 } 531 } 532 if (occupancyNamedSensor != null) { 533 return occupancyNamedSensor.getBean(); 534 } 535 return null; 536 } 537 538 /** 539 * Add occupancy sensor by name. 540 * 541 * @param name for senor to add 542 */ 543 public void setOccupancySensorName(String name) { 544 if ((name == null) || name.isEmpty()) { 545 if (occupancyNamedSensor != null) { 546 occupancyNamedSensor.getBean().removePropertyChangeListener(mBlockListener); 547 } 548 occupancyNamedSensor = null; 549 occupancySensorName = ""; 550 551 if (block != null) { 552 block.setNamedSensor(null); 553 } 554 return; 555 } 556 occupancySensorName = name; 557 Sensor sensor = InstanceManager.sensorManagerInstance().getSensor(name); 558 if (sensor != null) { 559 occupancyNamedSensor = InstanceManager.getDefault( 560 NamedBeanHandleManager.class).getNamedBeanHandle(name, sensor); 561 if (block != null) { 562 block.setNamedSensor(occupancyNamedSensor); 563 } 564 } 565 } 566 567 /** 568 * Get occupied sensor state. 569 * 570 * @return occupied sensor state, defaults to Sensor.ACTIVE 571 */ 572 public int getOccupiedSense() { 573 return occupiedSense; 574 } 575 576 /** 577 * Set occupied sensor state. 578 * 579 * @param sense eg. Sensor.INACTIVE 580 */ 581 public void setOccupiedSense(int sense) { 582 occupiedSense = sense; 583 } 584 585 /** 586 * Test block occupancy. 587 * 588 * @return occupancy state 589 */ 590 public int getOccupancy() { 591 if (occupancyNamedSensor == null) { 592 Sensor s = null; 593 if (!occupancySensorName.isEmpty()) { 594 s = InstanceManager.sensorManagerInstance().getSensor(occupancySensorName); 595 } 596 if (s == null) { 597 // no occupancy sensor, so base upon block occupancy state 598 if (block != null) { 599 return block.getState(); 600 } 601 // if no block or sensor return unknown 602 return UNKNOWN; 603 } 604 occupancyNamedSensor = InstanceManager.getDefault( 605 NamedBeanHandleManager.class).getNamedBeanHandle(occupancySensorName, s); 606 if (block != null) { 607 block.setNamedSensor(occupancyNamedSensor); 608 } 609 } 610 611 Sensor s = getOccupancySensor(); 612 if ( s == null) { 613 return UNKNOWN; 614 } 615 616 if (s.getKnownState() != occupiedSense) { 617 return EMPTY; 618 } else if (s.getKnownState() == occupiedSense) { 619 return OCCUPIED; 620 } 621 return UNKNOWN; 622 } 623 624 @Override 625 public int getState() { 626 return getOccupancy(); 627 } 628 629 /** 630 * Does nothing, do not use.Dummy for completion of NamedBean interface 631 * @param i does nothing 632 */ 633 @Override 634 public void setState(int i) { 635 log.error("this state does nothing {}", getDisplayName()); 636 } 637 638 /** 639 * Get the panel with the highest connectivity to this Layout Block. 640 * 641 * @return panel with most connections to this block 642 */ 643 public LayoutEditor getMaxConnectedPanel() { 644 LayoutEditor result = null; 645 // a block is attached and this LayoutBlock is used 646 if ((block != null) && (panels.size() > 0)) { 647 // initialize connectivity as defined in first Layout Editor panel 648 int maxConnectivity = Integer.MIN_VALUE; 649 for (LayoutEditor panel : panels) { 650 List<LayoutConnectivity> c = panel.getLEAuxTools().getConnectivityList(this); 651 if (maxConnectivity < c.size()) { 652 maxConnectivity = c.size(); 653 result = panel; 654 } 655 } 656 } 657 return result; 658 } 659 660 /** 661 * Check/Update Path objects for the attached Block 662 * <p> 663 * If multiple panels are present, Paths are set according to the panel with 664 * the highest connectivity (most LayoutConnectivity objects). 665 */ 666 public void updatePaths() { 667 // Update paths is called by the panel, turnouts, xings, track segments etc 668 if ((block != null) && !panels.isEmpty()) { 669 // a block is attached and this LayoutBlock is used 670 // initialize connectivity as defined in first Layout Editor panel 671 LayoutEditor panel = panels.get(0); 672 List<LayoutConnectivity> c = panel.getLEAuxTools().getConnectivityList(this); 673 674 // if more than one panel, find panel with the highest connectivity 675 if (panels.size() > 1) { 676 for (int i = 1; i < panels.size(); i++) { 677 if (c.size() < panels.get(i).getLEAuxTools(). 678 getConnectivityList(this).size()) { 679 panel = panels.get(i); 680 c = panel.getLEAuxTools().getConnectivityList(this); 681 } 682 } 683 684 // Now try to determine if this block is across two panels due to a linked point 685 PositionablePoint point = panel.getFinder().findPositionableLinkPoint(this); 686 if ((point != null) && (point.getLinkedEditor() != null) && panels.contains(point.getLinkedEditor())) { 687 c = panel.getLEAuxTools().getConnectivityList(this); 688 c.addAll(point.getLinkedEditor().getLEAuxTools().getConnectivityList(this)); 689 } else { 690 // check that this connectivity is compatible with that of other panels. 691 for (LayoutEditor tPanel : panels) { 692 if ((tPanel != panel) && InstanceManager.getDefault( 693 LayoutBlockManager.class).warn() 694 && (!compareConnectivity(c, tPanel.getLEAuxTools().getConnectivityList(this)))) { 695 // send user an error message 696 int response = JmriJOptionPane.showOptionDialog(null, 697 java.text.MessageFormat.format(Bundle.getMessage("Warn1"), 698 new Object[]{getUserName(), tPanel.getLayoutName(), panel.getLayoutName()}), 699 Bundle.getMessage("WarningTitle"), 700 JmriJOptionPane.DEFAULT_OPTION, JmriJOptionPane.QUESTION_MESSAGE, 701 null, 702 new Object[]{Bundle.getMessage("ButtonOK"), Bundle.getMessage("ButtonOKPlus")}, 703 Bundle.getMessage("ButtonOK")); 704 if (response == 1 ) { // ButtokOKPlus pressed, user elected to disable messages 705 InstanceManager.getDefault( 706 LayoutBlockManager.class).turnOffWarning(); 707 } 708 } 709 } 710 } 711 } 712 // update block Paths to reflect connectivity as needed 713 updateBlockPaths(c, panel); 714 } 715 } 716 717 /** 718 * Check/Update Path objects for the attached Block using the connectivity 719 * in the specified Layout Editor panel. 720 * 721 * @param panel to extract paths 722 */ 723 public void updatePathsUsingPanel(LayoutEditor panel) { 724 if (panel == null) { 725 log.error("Null panel in call to updatePathsUsingPanel"); 726 return; 727 } 728 List<LayoutConnectivity> c = panel.getLEAuxTools().getConnectivityList(this); 729 updateBlockPaths(c, panel); 730 731 } 732 733 private void updateBlockPaths(List<LayoutConnectivity> c, LayoutEditor panel) { 734 addRouteLog.debug("From {} updateBlockPaths Called", getDisplayName()); 735 auxTools = panel.getLEAuxTools(); 736 List<Path> paths = block.getPaths(); 737 boolean[] used = new boolean[c.size()]; 738 int[] need = new int[paths.size()]; 739 Arrays.fill(used, false); 740 Arrays.fill(need, -1); 741 742 // cycle over existing Paths, checking against LayoutConnectivity 743 for (int i = 0; i < paths.size(); i++) { 744 Path p = paths.get(i); 745 746 // cycle over LayoutConnectivity matching to this Path 747 for (int j = 0; ((j < c.size()) && (need[i] == -1)); j++) { 748 if (!used[j]) { 749 // this LayoutConnectivity not used yet 750 LayoutConnectivity lc = c.get(j); 751 if ((lc.getBlock1().getBlock() == p.getBlock()) || (lc.getBlock2().getBlock() == p.getBlock())) { 752 // blocks match - record 753 used[j] = true; 754 need[i] = j; 755 } 756 } 757 } 758 } 759 760 // update needed Paths 761 for (int i = 0; i < paths.size(); i++) { 762 if (need[i] >= 0) { 763 Path p = paths.get(i); 764 LayoutConnectivity lc = c.get(need[i]); 765 if (lc.getBlock1() == this) { 766 p.setToBlockDirection(lc.getDirection()); 767 p.setFromBlockDirection(lc.getReverseDirection()); 768 } else { 769 p.setToBlockDirection(lc.getReverseDirection()); 770 p.setFromBlockDirection(lc.getDirection()); 771 } 772 List<BeanSetting> beans = new ArrayList<>(p.getSettings()); 773 for (BeanSetting bean : beans) { 774 p.removeSetting(bean); 775 } 776 auxTools.addBeanSettings(p, lc, this); 777 } 778 } 779 // delete unneeded Paths 780 for (int i = 0; i < paths.size(); i++) { 781 if (need[i] < 0) { 782 block.removePath(paths.get(i)); 783 if (InstanceManager.getDefault( 784 LayoutBlockManager.class).isAdvancedRoutingEnabled()) { 785 removeAdjacency(paths.get(i)); 786 } 787 } 788 } 789 790 // add Paths as required 791 for (int j = 0; j < c.size(); j++) { 792 if (!used[j]) { 793 // there is no corresponding Path, add one. 794 LayoutConnectivity lc = c.get(j); 795 Path newp; 796 797 if (lc.getBlock1() == this) { 798 newp = new Path(lc.getBlock2().getBlock(), lc.getDirection(), 799 lc.getReverseDirection()); 800 } else { 801 newp = new Path(lc.getBlock1().getBlock(), lc.getReverseDirection(), 802 lc.getDirection()); 803 } 804 block.addPath(newp); 805 806 addRouteLog.debug("From {} addPath({})", getDisplayName(), newp.toString()); 807 808 if (InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled()) { 809 addAdjacency(newp); 810 } 811 auxTools.addBeanSettings(newp, lc, this); 812 } 813 } 814 815 // djd debugging - lists results of automatic initialization of Paths and BeanSettings 816 if (log.isDebugEnabled()) { 817 block.getPaths().stream().forEach( p -> log.debug("From {} to {}", getDisplayName(), p )); 818 } 819 } 820 821 /** 822 * Make sure all the layout connectivity objects in test are in main. 823 * 824 * @param main the main list of LayoutConnectivity objects 825 * @param test the test list of LayoutConnectivity objects 826 * @return true if all test layout connectivity objects are in main 827 */ 828 private boolean compareConnectivity(List<LayoutConnectivity> main, List<LayoutConnectivity> test) { 829 boolean result = false; // assume failure (pessimsit!) 830 if (!main.isEmpty() && !test.isEmpty()) { 831 result = true; // assume success (optimist!) 832 // loop over connectivities in test list 833 for (LayoutConnectivity tc : test) { 834 LayoutBlock tlb1 = tc.getBlock1(), tlb2 = tc.getBlock2(); 835 // loop over main list to make sure the same blocks are connected 836 boolean found = false; // assume failure (pessimsit!) 837 for (LayoutConnectivity mc : main) { 838 LayoutBlock mlb1 = mc.getBlock1(), mlb2 = mc.getBlock2(); 839 if (((tlb1 == mlb1) && (tlb2 == mlb2)) 840 || ((tlb1 == mlb2) && (tlb2 == mlb1))) { 841 found = true; // success! 842 break; 843 } 844 } 845 if (!found) { 846 result = false; 847 break; 848 } 849 } 850 } else if (main.isEmpty() && test.isEmpty()) { 851 result = true; // OK if both have no neighbors, common for turntable rays 852 } 853 return result; 854 } 855 856 /** 857 * Handle tasks when block changes 858 * 859 * @param e propChgEvent 860 */ 861 void handleBlockChange(PropertyChangeEvent e) { 862 // Update memory object if there is one 863 Memory m = getMemory(); 864 if ((m != null) && (block != null) && !suppressNameUpdate) { 865 // copy block value to memory if there is a value 866 Object val = block.getValue(); 867 if (val != null) { 868 if (!(val instanceof RosterEntry) && !(val instanceof Reportable)) { 869 val = val.toString(); 870 } 871 } 872 m.setValue(val); 873 } 874 875 if (e.getPropertyName().equals("UserName")) { 876 setUserName(e.getNewValue().toString()); 877 InstanceManager.getDefault(NamedBeanHandleManager.class). 878 renameBean(e.getOldValue().toString(), e.getNewValue().toString(), this); 879 } 880 881 if (e.getPropertyName().equals("OccupancySensorChange")) { 882 if (e.getNewValue() == null){ 883 // Remove Sensor 884 setOccupancySensorName(null); 885 } else { 886 // Set/change sensor 887 Sensor sensor = (Sensor) e.getNewValue(); 888 setSensorFromBlockEnabled = false; 889 if (validateSensor(sensor.getSystemName(), null) == null) { 890 // Sensor change rejected, reset block sensor assignment 891 Sensor origSensor = (Sensor) e.getOldValue(); 892 block.setSensor(origSensor == null ? "" : origSensor.getSystemName()); 893 } 894 setSensorFromBlockEnabled = true; 895 } 896 } 897 898 // Redraw all Layout Editor panels using this Layout Block 899 redrawLayoutBlockPanels(); 900 901 if (InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled()) { 902 stateUpdate(); 903 } 904 } 905 906 /** 907 * Deactivate block listener for redraw of panels and update of memories on 908 * change of state 909 */ 910 private void deactivateBlock() { 911 if ((mBlockListener != null) && (block != null)) { 912 block.removePropertyChangeListener(mBlockListener); 913 } 914 mBlockListener = null; 915 } 916 917 /** 918 * Set/reset update of memory name when block goes from occupied to 919 * unoccupied or vice versa. If set is true, name update is suppressed. If 920 * set is false, name update works normally. 921 * 922 * @param set true, update suppress. false, update normal 923 */ 924 public void setSuppressNameUpdate(boolean set) { 925 suppressNameUpdate = set; 926 } 927 928 929 private final NamedBeanComboBox<Memory> memoryComboBox = new NamedBeanComboBox<>( 930 InstanceManager.getDefault(MemoryManager.class), null, DisplayOptions.DISPLAYNAME); 931 932 private final JTextField metricField = new JTextField(10); 933 934 private final JComboBox<String> senseBox = new JComboBox<>(); 935 936 // TODO I18N in Bundle.properties 937 private int senseActiveIndex; 938 private int senseInactiveIndex; 939 940 private JColorChooser trackColorChooser = null; 941 private JColorChooser occupiedColorChooser = null; 942 private JColorChooser extraColorChooser = null; 943 944 public void editLayoutBlock(Component callingPane) { 945 LayoutBlockEditAction beanEdit = new LayoutBlockEditAction(); 946 if (block == null) { 947 // Block may not have been initialised due to an error so manually set it in the edit window 948 String userName = getUserName(); 949 if ((userName != null) && !userName.isEmpty()) { 950 Block b = InstanceManager.getDefault(BlockManager.class).getBlock(userName); 951 if (b != null) { 952 beanEdit.setBean(b); 953 } 954 } 955 } else { 956 beanEdit.setBean(block); 957 } 958 beanEdit.actionPerformed(null); 959 } 960 961 private final String[] working = {"Bi-Directional", "Receive Only", "Send Only"}; 962 963 // TODO I18N in ManagersBundle.properties 964 protected List<JComboBox<String>> neighbourDir; 965 966 protected class LayoutBlockEditAction extends BlockEditAction { 967 968 @Override 969 public String helpTarget() { 970 return "package.jmri.jmrit.display.EditLayoutBlock"; 971 } // NOI18N 972 973 @Override 974 protected void initPanels() { 975 super.initPanels(); 976 BeanItemPanel ld = layoutDetails(); 977 if (InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled()) { 978 blockRoutingDetails(); 979 } 980 setSelectedComponent(ld); 981 } 982 983 BeanItemPanel layoutDetails() { 984 BeanItemPanel layout = new BeanItemPanel(); 985 layout.setName(Bundle.getMessage("LayoutEditor")); 986 987 LayoutEditor.setupComboBox(memoryComboBox, false, true, false); 988 989 layout.addItem(new BeanEditItem(new JLabel("" + useCount), Bundle.getMessage("UseCount"), null)); 990 layout.addItem(new BeanEditItem(memoryComboBox, Bundle.getMessage("BeanNameMemory"), 991 Bundle.getMessage("MemoryVariableTip"))); 992 993 senseBox.removeAllItems(); 994 senseBox.addItem(Bundle.getMessage("SensorStateActive")); 995 senseActiveIndex = 0; 996 senseBox.addItem(Bundle.getMessage("SensorStateInactive")); 997 senseInactiveIndex = 1; 998 999 layout.addItem(new BeanEditItem(senseBox, Bundle.getMessage("OccupiedSense"), Bundle.getMessage("OccupiedSenseHint"))); 1000 1001 trackColorChooser = new JColorChooser(blockTrackColor); 1002 trackColorChooser.setPreviewPanel(new JPanel()); // remove the preview panel 1003 AbstractColorChooserPanel[] trackColorPanels = {new SplitButtonColorChooserPanel()}; 1004 trackColorChooser.setChooserPanels(trackColorPanels); 1005 layout.addItem(new BeanEditItem(trackColorChooser, Bundle.getMessage("TrackColor"), Bundle.getMessage("TrackColorHint"))); 1006 1007 occupiedColorChooser = new JColorChooser(blockOccupiedColor); 1008 occupiedColorChooser.setPreviewPanel(new JPanel()); // remove the preview panel 1009 AbstractColorChooserPanel[] occupiedColorPanels = {new SplitButtonColorChooserPanel()}; 1010 occupiedColorChooser.setChooserPanels(occupiedColorPanels); 1011 layout.addItem(new BeanEditItem(occupiedColorChooser, Bundle.getMessage("OccupiedColor"), Bundle.getMessage("OccupiedColorHint"))); 1012 1013 extraColorChooser = new JColorChooser(blockExtraColor); 1014 extraColorChooser.setPreviewPanel(new JPanel()); // remove the preview panel 1015 AbstractColorChooserPanel[] extraColorPanels = {new SplitButtonColorChooserPanel()}; 1016 extraColorChooser.setChooserPanels(extraColorPanels); 1017 layout.addItem(new BeanEditItem(extraColorChooser, Bundle.getMessage("ExtraColor"), Bundle.getMessage("ExtraColorHint"))); 1018 1019 layout.setSaveItem(new AbstractAction() { 1020 @Override 1021 public void actionPerformed(ActionEvent e) { 1022 boolean needsRedraw = false; 1023 int k = senseBox.getSelectedIndex(); 1024 int oldSense = occupiedSense; 1025 1026 if (k == senseActiveIndex) { 1027 occupiedSense = Sensor.ACTIVE; 1028 } else { 1029 occupiedSense = Sensor.INACTIVE; 1030 } 1031 1032 if (oldSense != occupiedSense) { 1033 needsRedraw = true; 1034 } 1035 // check if track color changed 1036 Color oldColor = blockTrackColor; 1037 blockTrackColor = trackColorChooser.getColor(); 1038 if (oldColor != blockTrackColor) { 1039 needsRedraw = true; 1040 JmriColorChooser.addRecentColor(blockTrackColor); 1041 } 1042 // check if occupied color changed 1043 oldColor = blockOccupiedColor; 1044 blockOccupiedColor = occupiedColorChooser.getColor(); 1045 if (oldColor != blockOccupiedColor) { 1046 needsRedraw = true; 1047 JmriColorChooser.addRecentColor(blockOccupiedColor); 1048 } 1049 // check if extra color changed 1050 oldColor = blockExtraColor; 1051 blockExtraColor = extraColorChooser.getColor(); 1052 if (oldColor != blockExtraColor) { 1053 needsRedraw = true; 1054 JmriColorChooser.addRecentColor(blockExtraColor); 1055 } 1056 // check if Memory changed 1057 String newName = memoryComboBox.getSelectedItemDisplayName(); 1058 if (newName == null) { 1059 newName = ""; 1060 } 1061 if (!memoryName.equals(newName)) { 1062 // memory has changed 1063 setMemory(validateMemory(newName, null), newName); 1064 if (getMemory() == null) { 1065 // invalid memory entered 1066 memoryName = ""; 1067 memoryComboBox.setSelectedItem(null); 1068 return; 1069 } else { 1070 memoryComboBox.setSelectedItem(getMemory()); 1071 needsRedraw = true; 1072 } 1073 } 1074 1075 if (needsRedraw) { 1076 redrawLayoutBlockPanels(); 1077 } 1078 } 1079 }); 1080 1081 layout.setResetItem(new AbstractAction() { 1082 @Override 1083 public void actionPerformed(ActionEvent e) { 1084 memoryComboBox.setSelectedItem(getMemory()); 1085 trackColorChooser.setColor(blockTrackColor); 1086 occupiedColorChooser.setColor(blockOccupiedColor); 1087 extraColorChooser.setColor(blockExtraColor); 1088 if (occupiedSense == Sensor.ACTIVE) { 1089 senseBox.setSelectedIndex(senseActiveIndex); 1090 } else { 1091 senseBox.setSelectedIndex(senseInactiveIndex); 1092 } 1093 } 1094 }); 1095 bei.add(layout); 1096 return layout; 1097 } 1098 1099 BeanItemPanel blockRoutingDetails() { 1100 BeanItemPanel routing = new BeanItemPanel(); 1101 routing.setName("Routing"); 1102 1103 routing.addItem(new BeanEditItem(metricField, "Block Metric", "set the cost for going over this block")); 1104 1105 routing.addItem(new BeanEditItem(null, null, "Set the direction of the connection to the neighbouring block")); 1106 neighbourDir = new ArrayList<>(getNumberOfNeighbours()); 1107 for (int i = 0; i < getNumberOfNeighbours(); i++) { 1108 JComboBox<String> dir = new JComboBox<>(working); 1109 routing.addItem(new BeanEditItem(dir, getNeighbourAtIndex(i).getDisplayName(), null)); 1110 neighbourDir.add(dir); 1111 } 1112 1113 routing.setResetItem(new AbstractAction() { 1114 @Override 1115 public void actionPerformed(ActionEvent e) { 1116 metricField.setText(Integer.toString(metric)); 1117 for (int i = 0; i < getNumberOfNeighbours(); i++) { 1118 JComboBox<String> dir = neighbourDir.get(i); 1119 Block blk = neighbours.get(i).getBlock(); 1120 if (block.isBlockDenied(blk)) { 1121 dir.setSelectedIndex(2); 1122 } else if (blk.isBlockDenied(block)) { 1123 dir.setSelectedIndex(1); 1124 } else { 1125 dir.setSelectedIndex(0); 1126 } 1127 } 1128 } 1129 }); 1130 1131 routing.setSaveItem(new AbstractAction() { 1132 @Override 1133 public void actionPerformed(ActionEvent e) { 1134 int m = Integer.parseInt(metricField.getText().trim()); 1135 if (m != metric) { 1136 setBlockMetric(m); 1137 } 1138 if (neighbourDir != null) { 1139 for (int i = 0; i < neighbourDir.size(); i++) { 1140 int neigh = neighbourDir.get(i).getSelectedIndex(); 1141 neighbours.get(i).getBlock().removeBlockDenyList(block); 1142 block.removeBlockDenyList(neighbours.get(i).getBlock()); 1143 switch (neigh) { 1144 case 0: { 1145 updateNeighbourPacketFlow(neighbours.get(i), RXTX); 1146 break; 1147 } 1148 1149 case 1: { 1150 neighbours.get(i).getBlock().addBlockDenyList(block.getDisplayName()); 1151 updateNeighbourPacketFlow(neighbours.get(i), TXONLY); 1152 break; 1153 } 1154 1155 case 2: { 1156 block.addBlockDenyList(neighbours.get(i).getBlock().getDisplayName()); 1157 updateNeighbourPacketFlow(neighbours.get(i), RXONLY); 1158 break; 1159 } 1160 1161 default: { 1162 break; 1163 } 1164 } 1165 /* switch */ 1166 } 1167 } 1168 } 1169 }); 1170 bei.add(routing); 1171 return routing; 1172 } 1173 } 1174 1175 /** 1176 * Remove this object from display and persistance. 1177 */ 1178 void remove() { 1179 // if an occupancy sensor has been activated, deactivate it 1180 deactivateBlock(); 1181 // remove from persistance by flagging inactive 1182 active = false; 1183 } 1184 1185 boolean active = true; 1186 1187 /** 1188 * "active" is true if the object is still displayed, and should be stored. 1189 * 1190 * @return active 1191 */ 1192 public boolean isActive() { 1193 return active; 1194 } 1195 1196 /* 1197 The code below relates to the layout block routing protocol 1198 */ 1199 /** 1200 * Set the block metric based upon the track segment that the block is 1201 * associated with if the (200 if Side, 50 if Main). If the block is 1202 * assigned against multiple track segments all with different types then 1203 * the highest type will be used. In theory no reason why it couldn't be a 1204 * compromise. 1205 */ 1206 void setBlockMetric() { 1207 if (!defaultMetric) { 1208 return; 1209 } 1210 updateRouteLog.debug("From '{}' default set block metric called", getDisplayName()); 1211 LayoutEditor panel = getMaxConnectedPanel(); 1212 if (panel == null) { 1213 updateRouteLog.debug("From '{}' unable to set metric as we are not connected to a panel yet", 1214 getDisplayName()); 1215 return; 1216 } 1217 String userName = getUserName(); 1218 if (userName == null) { 1219 log.info("From '{}': unable to get user name", this.getDisplayName()); 1220 return; 1221 } 1222 List<TrackSegment> ts = panel.getFinder().findTrackSegmentByBlock(userName); 1223 int mainline = 0; 1224 int side = 0; 1225 1226 for (TrackSegment t : ts) { 1227 if (t.isMainline()) { 1228 mainline++; 1229 } else { 1230 side++; 1231 } 1232 } 1233 1234 if (mainline > side) { 1235 metric = 50; 1236 } else if (mainline < side) { 1237 metric = 200; 1238 } else { 1239 // They must both be equal so will set as a mainline. 1240 metric = 50; 1241 } 1242 1243 updateRouteLog.debug("From '{}' metric set to {}", getDisplayName(), metric); 1244 1245 // What we need to do here, is resend our routing packets with the new metric 1246 RoutingPacket update = new RoutingPacket(UPDATE, this.getBlock(), -1, metric, -1, -1, getNextPacketID()); 1247 firePropertyChange("routing", null, update); 1248 } 1249 1250 private boolean defaultMetric = true; 1251 1252 public boolean useDefaultMetric() { 1253 return defaultMetric; 1254 } 1255 1256 public void useDefaultMetric(boolean boo) { 1257 if (boo == defaultMetric) { 1258 return; 1259 } 1260 defaultMetric = boo; 1261 if (boo) { 1262 setBlockMetric(); 1263 } 1264 } 1265 1266 /** 1267 * Set a metric cost against a block, this is used in the calculation of a 1268 * path between two location on the layout, a lower path cost is always 1269 * preferred For Layout blocks defined as Mainline the default metric is 50. 1270 * For Layout blocks defined as a Siding the default metric is 200. 1271 * 1272 * @param m metric value 1273 */ 1274 public void setBlockMetric(int m) { 1275 if (metric == m) { 1276 return; 1277 } 1278 metric = m; 1279 defaultMetric = false; 1280 RoutingPacket update = new RoutingPacket(UPDATE, this.getBlock(), -1, metric, -1, -1, getNextPacketID()); 1281 firePropertyChange("routing", null, update); 1282 } 1283 1284 /** 1285 * Get the layout block metric cost 1286 * 1287 * @return metric cost of block 1288 */ 1289 public int getBlockMetric() { 1290 return metric; 1291 } 1292 1293 // re work this so that is makes beter us of existing code. 1294 // This is no longer required currently, but might be used at a later date. 1295 public void addAllThroughPaths() { 1296 addRouteLog.debug("Add all ThroughPaths {}", getDisplayName()); 1297 1298 if ((block != null) && (panels.size() > 0)) { 1299 // a block is attached and this LayoutBlock is used 1300 // initialize connectivity as defined in first Layout Editor panel 1301 LayoutEditor panel = panels.get(0); 1302 List<LayoutConnectivity> c = panel.getLEAuxTools().getConnectivityList(this); 1303 1304 // if more than one panel, find panel with the highest connectivity 1305 if (panels.size() > 1) { 1306 for (int i = 1; i < panels.size(); i++) { 1307 if (c.size() < panels.get(i).getLEAuxTools(). 1308 getConnectivityList(this).size()) { 1309 panel = panels.get(i); 1310 c = panel.getLEAuxTools().getConnectivityList(this); 1311 } 1312 } 1313 1314 // check that this connectivity is compatible with that of other panels. 1315 for (LayoutEditor tPanel : panels) { 1316 if ((tPanel != panel) 1317 && InstanceManager.getDefault(LayoutBlockManager.class). 1318 warn() && (!compareConnectivity(c, tPanel.getLEAuxTools().getConnectivityList(this)))) { 1319 1320 // send user an error message 1321 int response = JmriJOptionPane.showOptionDialog(null, 1322 java.text.MessageFormat.format(Bundle.getMessage("Warn1"), 1323 new Object[]{getUserName(), tPanel.getLayoutName(), 1324 panel.getLayoutName()}), Bundle.getMessage("WarningTitle"), 1325 JmriJOptionPane.DEFAULT_OPTION, JmriJOptionPane.QUESTION_MESSAGE, 1326 null, 1327 new Object[]{Bundle.getMessage("ButtonOK"), Bundle.getMessage("ButtonOKPlus")}, 1328 Bundle.getMessage("ButtonOK")); 1329 if (response == 1) { // array position 1 ButtonOKPlus pressed, user elected to disable messages 1330 InstanceManager.getDefault(LayoutBlockManager.class).turnOffWarning(); 1331 } 1332 } 1333 } 1334 } 1335 auxTools = panel.getLEAuxTools(); 1336 List<LayoutConnectivity> d = auxTools.getConnectivityList(this); 1337 List<LayoutBlock> attachedBlocks = new ArrayList<>(); 1338 1339 for (LayoutConnectivity connectivity : d) { 1340 if (connectivity.getBlock1() != this) { 1341 attachedBlocks.add(connectivity.getBlock1()); 1342 } else { 1343 attachedBlocks.add(connectivity.getBlock2()); 1344 } 1345 } 1346 // Will need to re-look at this to cover both way and single way routes 1347 for (LayoutBlock attachedBlock : attachedBlocks) { 1348 addRouteLog.debug("From {} block is attached {}", getDisplayName(), attachedBlock.getDisplayName()); 1349 1350 for (LayoutBlock layoutBlock : attachedBlocks) { 1351 addThroughPath(attachedBlock.getBlock(), layoutBlock.getBlock(), panel); 1352 } 1353 } 1354 } 1355 } 1356 1357 // TODO: if the block already exists, we still may want to re-work the through paths 1358 // With this bit we need to get our neighbour to send new routes 1359 private void addNeighbour(Block addBlock, int direction, int workingDirection) { 1360 boolean layoutConnectivityBefore = layoutConnectivity; 1361 1362 addRouteLog.debug("From {} asked to add block {} as new neighbour {}", getDisplayName(), 1363 addBlock.getDisplayName(), decodePacketFlow(workingDirection)); 1364 1365 if (getAdjacency(addBlock) != null) { 1366 addRouteLog.debug("Block is already registered"); 1367 addThroughPath(getAdjacency(addBlock)); 1368 } else { 1369 Adjacencies adj = new Adjacencies(addBlock, direction, workingDirection); 1370 neighbours.add(adj); 1371 1372 // Add the neighbour to our routing table. 1373 LayoutBlock blk = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(addBlock); 1374 LayoutEditor editor = getMaxConnectedPanel(); 1375 1376 if ((editor != null) && (connection == null)) { 1377 // We should be able to determine block metric now as the tracksegment should be valid 1378 connection = editor.getConnectivityUtil(); 1379 } 1380 1381 // Need to inform our neighbours of our new addition 1382 // We only add an entry into the routing table if we are able to reach the next working block. 1383 // If we only transmit routes to it, then we can not route to it therefore it is not added 1384 Routes route = null; 1385 1386 if ((workingDirection == RXTX) || (workingDirection == RXONLY)) { 1387 if (blk != null) { 1388 route = new Routes(addBlock, this.getBlock(), 1, direction, blk.getBlockMetric(), addBlock.getLengthMm()); 1389 } else { 1390 route = new Routes(addBlock, this.getBlock(), 1, direction, 0, 0); 1391 } 1392 routes.add(route); 1393 } 1394 1395 if (blk != null) { 1396 boolean mutual = blk.informNeighbourOfAttachment(this, this.getBlock(), workingDirection); 1397 1398 // The propertychange listener will have to be modified depending upon RX or TX selection. 1399 // if we only transmit routes to this neighbour then we do not want to listen to thier broadcast messages 1400 if ((workingDirection == RXTX) || (workingDirection == RXONLY)) { 1401 blk.addPropertyChangeListener(this); 1402 // log.info("From {} add property change {}", this.getDisplayName(), blk.getDisplayName()); 1403 } else { 1404 blk.removePropertyChangeListener(this); 1405 } 1406 1407 int neighwork = blk.getAdjacencyPacketFlow(this.getBlock()); 1408 addRouteLog.debug("{}.getAdjacencyPacketFlow({}): {}, {}", 1409 blk.getDisplayName(), getBlock().getDisplayName(), 1410 ( neighwork==-1 ? "Unset" : decodePacketFlow(neighwork)), neighwork); 1411 1412 if (neighwork != -1) { 1413 addRouteLog.debug("From {} Updating flow direction to {} for block {} choice of {} {}", 1414 getDisplayName(), 1415 decodePacketFlow(determineAdjPacketFlow(workingDirection, neighwork)), 1416 blk.getDisplayName(), decodePacketFlow(workingDirection), decodePacketFlow(neighwork)); 1417 1418 int newPacketFlow = determineAdjPacketFlow(workingDirection, neighwork); 1419 adj.setPacketFlow(newPacketFlow); 1420 1421 if (newPacketFlow == TXONLY) { 1422 for (int j = routes.size() - 1; j > -1; j--) { 1423 Routes ro = routes.get(j); 1424 if ((ro.getDestBlock() == addBlock) 1425 && (ro.getNextBlock() == this.getBlock())) { 1426 adj.removeRouteAdvertisedToNeighbour(ro); 1427 routes.remove(j); 1428 } 1429 } 1430 RoutingPacket newUpdate = new RoutingPacket(REMOVAL, addBlock, -1, -1, -1, -1, getNextPacketID()); 1431 neighbours.forEach((adja) -> adja.removeRouteAdvertisedToNeighbour(addBlock)); 1432 firePropertyChange("routing", null, newUpdate); 1433 } 1434 } else { 1435 addRouteLog.debug("From {} neighbour {} working direction is not valid", 1436 getDisplayName(), addBlock.getDisplayName()); 1437 return; 1438 } 1439 adj.setMutual(mutual); 1440 1441 if (route != null) { 1442 route.stateChange(); 1443 } 1444 addThroughPath(getAdjacency(addBlock)); 1445 // We get our new neighbour to send us a list of valid routes that they have. 1446 // This might have to be re-written as a property change event? 1447 // Also only inform our neighbour if they have us down as a mutual, otherwise it will just reject the packet. 1448 if (((workingDirection == RXTX) || (workingDirection == TXONLY)) && mutual) { 1449 blk.informNeighbourOfValidRoutes(getBlock()); 1450 } 1451 } else { 1452 addRouteLog.debug("From {} neighbour {} has no layoutBlock associated, metric set to {}", 1453 getDisplayName(), addBlock.getDisplayName(), adj.getMetric()); 1454 } 1455 } 1456 1457 /* If the connectivity before has not completed and produced an error with 1458 setting up through Paths, we will cycle through them */ 1459 addRouteLog.debug("From {} layout connectivity before {}", getDisplayName(), layoutConnectivityBefore); 1460 if (!layoutConnectivityBefore) { 1461 for (Adjacencies neighbour : neighbours) { 1462 addThroughPath(neighbour); 1463 } 1464 } 1465 /* We need to send our new neighbour our copy of the routing table however 1466 we can only send valid routes that would be able to traverse as definded by 1467 through paths table */ 1468 } 1469 1470 private boolean informNeighbourOfAttachment(LayoutBlock lBlock, Block block, int workingDirection) { 1471 Adjacencies adj = getAdjacency(block); 1472 if (adj == null) { 1473 addRouteLog.debug("From {} neighbour {} has informed us of its attachment to us, however we do not yet have it registered", 1474 getDisplayName(), lBlock.getDisplayName()); 1475 return false; 1476 } 1477 1478 if (!adj.isMutual()) { 1479 addRouteLog.debug("From {} neighbour {} wants us to {}; we have it set as {}", 1480 getDisplayName(), block.getDisplayName(), 1481 decodePacketFlow(workingDirection), decodePacketFlow(adj.getPacketFlow())); 1482 1483 // Simply if both the neighbour and us both want to do the same thing with sending routing information, 1484 // in one direction then no routes will be passed 1485 int newPacketFlow = determineAdjPacketFlow(adj.getPacketFlow(), workingDirection); 1486 addRouteLog.debug("From {} neighbour {} passed {} we have {} this will be updated to {}", 1487 getDisplayName(), block.getDisplayName(), decodePacketFlow(workingDirection), 1488 decodePacketFlow(adj.getPacketFlow()), decodePacketFlow(newPacketFlow)); 1489 adj.setPacketFlow(newPacketFlow); 1490 1491 // If we are only set to transmit routing information to the adj, then 1492 // we will not have it appearing in the routing table 1493 if (newPacketFlow != TXONLY) { 1494 Routes neighRoute = getValidRoute(this.getBlock(), adj.getBlock()); 1495 // log.info("From " + this.getDisplayName() + " neighbour " + adj.getBlock().getDisplayName() + " valid routes returned as " + neighRoute); 1496 if (neighRoute == null) { 1497 log.info("Null route so will bomb out"); 1498 return false; 1499 } 1500 1501 if (neighRoute.getMetric() != adj.getMetric()) { 1502 addRouteLog.debug("From {} The value of the metric we have for this route" 1503 + " is not correct {}, stored {} v {}", 1504 getDisplayName(), getBlock().getDisplayName(), neighRoute.getMetric(), adj.getMetric()); 1505 neighRoute.setMetric(adj.getMetric()); 1506 // This update might need to be more selective 1507 RoutingPacket update = new RoutingPacket(UPDATE, adj.getBlock(), -1, (adj.getMetric() + metric), -1, -1, getNextPacketID()); 1508 firePropertyChange("routing", null, update); 1509 } 1510 1511 if (neighRoute.getMetric() != (int) adj.getLength()) { 1512 addRouteLog.debug("From {} The value of the length we have for this route" 1513 + " is not correct {}, stored {} v {}", 1514 getDisplayName(), getBlock().getDisplayName(), neighRoute.getMetric(), adj.getMetric()); 1515 neighRoute.setLength(adj.getLength()); 1516 // This update might need to be more selective 1517 RoutingPacket update = new RoutingPacket(UPDATE, adj.getBlock(), -1, -1, 1518 adj.getLength() + block.getLengthMm(), -1, getNextPacketID()); 1519 firePropertyChange("routing", null, update); 1520 } 1521 Routes r = getRouteByDestBlock(block); 1522 if (r != null) { 1523 r.setMetric(lBlock.getBlockMetric()); 1524 } else { 1525 log.warn("No getRouteByDestBlock('{}')", block.getDisplayName()); 1526 } 1527 } 1528 1529 addRouteLog.debug("From {} We were not a mutual adjacency with {} but now are", 1530 getDisplayName(), lBlock.getDisplayName()); 1531 1532 if ((newPacketFlow == RXTX) || (newPacketFlow == RXONLY)) { 1533 lBlock.addPropertyChangeListener(this); 1534 } else { 1535 lBlock.removePropertyChangeListener(this); 1536 } 1537 1538 if (newPacketFlow == TXONLY) { 1539 for (int j = routes.size() - 1; j > -1; j--) { 1540 Routes ro = routes.get(j); 1541 if ((ro.getDestBlock() == block) && (ro.getNextBlock() == this.getBlock())) { 1542 adj.removeRouteAdvertisedToNeighbour(ro); 1543 routes.remove(j); 1544 } 1545 } 1546 1547 for (int j = throughPaths.size() - 1; j > -1; j--) { 1548 if ((throughPaths.get(j).getDestinationBlock() == block)) { 1549 addRouteLog.debug("From {} removed throughpath {} {}", 1550 getDisplayName(), throughPaths.get(j).getSourceBlock().getDisplayName(), 1551 throughPaths.get(j).getDestinationBlock().getDisplayName()); 1552 throughPaths.remove(j); 1553 } 1554 } 1555 RoutingPacket newUpdate = new RoutingPacket(REMOVAL, block, -1, -1, -1, -1, getNextPacketID()); 1556 neighbours.forEach((adja) -> adja.removeRouteAdvertisedToNeighbour(block)); 1557 firePropertyChange("routing", null, newUpdate); 1558 } 1559 1560 adj.setMutual(true); 1561 addThroughPath(adj); 1562 1563 // As we are now mutual we will send our neigh a list of valid routes. 1564 if ((newPacketFlow == RXTX) || (newPacketFlow == TXONLY)) { 1565 addRouteLog.debug("From {} inform neighbour of valid routes", getDisplayName()); 1566 informNeighbourOfValidRoutes(block); 1567 } 1568 } 1569 return true; 1570 } 1571 1572 private int determineAdjPacketFlow(int our, int neigh) { 1573 // Both are the same 1574 updateRouteLog.debug("From {} values passed our {} neigh {}", getDisplayName(), 1575 decodePacketFlow(our), decodePacketFlow(neigh)); 1576 if ((our == RXTX) && (neigh == RXTX)) { 1577 return RXTX; 1578 } 1579 1580 /*First off reverse the neighbour flow, as it will be telling us if it will allow or deny traffic from us. 1581 So if it is set to RX, then we can TX to it.*/ 1582 if (neigh == RXONLY) { 1583 neigh = TXONLY; 1584 } else if (neigh == TXONLY) { 1585 neigh = RXONLY; 1586 } 1587 1588 if (our == neigh) { 1589 return our; 1590 } 1591 return NONE; 1592 } 1593 1594 private void informNeighbourOfValidRoutes(Block newblock) { 1595 // java.sql.Timestamp t1 = new java.sql.Timestamp(System.nanoTime()); 1596 List<Block> validFromPath = new ArrayList<>(); 1597 addRouteLog.debug("From {} new block {}", getDisplayName(), newblock.getDisplayName()); 1598 1599 for (ThroughPaths tp : throughPaths) { 1600 addRouteLog.debug("From {} B through routes {} {}", 1601 getDisplayName(), tp.getSourceBlock().getDisplayName(), 1602 tp.getDestinationBlock().getDisplayName()); 1603 1604 if (tp.getSourceBlock() == newblock) { 1605 validFromPath.add(tp.getDestinationBlock()); 1606 } else if (tp.getDestinationBlock() == newblock) { 1607 validFromPath.add(tp.getSourceBlock()); 1608 } 1609 } 1610 1611 addRouteLog.debug("From {} ===== valid from size path {} ====", getDisplayName(), validFromPath.size()); 1612 addRouteLog.debug("To {}", newblock.getDisplayName()); 1613 1614 // We only send packets on to our neighbour that are registered as being on a valid through path and are mutual. 1615 LayoutBlock lBnewblock = null; 1616 Adjacencies adj = getAdjacency(newblock); 1617 if (adj.isMutual()) { 1618 addRouteLog.debug("From {} adj with {} is mutual", getDisplayName(), newblock.getDisplayName()); 1619 lBnewblock = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(newblock); 1620 } else { 1621 addRouteLog.debug("From {} adj with {} is NOT mutual", getDisplayName(), newblock.getDisplayName()); 1622 } 1623 1624 if (lBnewblock == null) { 1625 return; 1626 } 1627 1628 for (Routes ro : new ArrayList<>(routes)) { 1629 addRouteLog.debug("next:{} dest:{}", ro.getNextBlock().getDisplayName(), 1630 ro.getDestBlock().getDisplayName()); 1631 1632 if (ro.getNextBlock() == getBlock()) { 1633 addRouteLog.debug("From {} ro next block is this", getDisplayName()); 1634 if (validFromPath.contains(ro.getDestBlock())) { 1635 addRouteLog.debug("From {} route to {} we have it with a metric of {} we will add our metric of {} " 1636 + "this will be sent to {} a", 1637 getDisplayName(), ro.getDestBlock().getDisplayName(), 1638 ro.getMetric(), metric, lBnewblock.getDisplayName()); 1639 // we added +1 to hop count and our metric. 1640 1641 RoutingPacket update = new RoutingPacket(ADDITION, ro.getDestBlock(), ro.getHopCount() + 1, (ro.getMetric() + metric), (ro.getLength() + block.getLengthMm()), -1, getNextPacketID()); 1642 lBnewblock.addRouteFromNeighbour(this, update); 1643 } 1644 } else { 1645 // Don't know if this might need changing so that we only send out our best 1646 // route to the neighbour, rather than cycling through them all. 1647 if (validFromPath.contains(ro.getNextBlock())) { 1648 addRouteLog.debug("From {} route to {} we have it with a metric of {} we will add our metric of {} this will be sent to {} b", this.getDisplayName(), ro.getDestBlock().getDisplayName(), ro.getMetric(), metric, lBnewblock.getDisplayName()); 1649 // we added +1 to hop count and our metric. 1650 if (adj.advertiseRouteToNeighbour(ro)) { 1651 addRouteLog.debug("Told to advertise to neighbour"); 1652 // this should keep track of the routes we sent to our neighbour. 1653 adj.addRouteAdvertisedToNeighbour(ro); 1654 RoutingPacket update = new RoutingPacket(ADDITION, ro.getDestBlock(), ro.getHopCount() + 1, (ro.getMetric() + metric), (ro.getLength() + block.getLengthMm()), -1, getNextPacketID()); 1655 lBnewblock.addRouteFromNeighbour(this, update); 1656 } else { 1657 addRouteLog.debug("Not advertised to neighbour"); 1658 } 1659 } else { 1660 addRouteLog.debug("failed valid from path Not advertised/added"); 1661 } 1662 } 1663 } 1664 } 1665 1666 static long time = 0; 1667 1668 /** 1669 * Work out our direction of route flow correctly. 1670 */ 1671 private void addAdjacency(Path addPath) { 1672 addRouteLog.debug("From {} path to be added {} {}", 1673 getDisplayName(), addPath.getBlock().getDisplayName(), 1674 Path.decodeDirection(addPath.getToBlockDirection())); 1675 1676 Block destBlockToAdd = addPath.getBlock(); 1677 int ourWorkingDirection = RXTX; 1678 if (destBlockToAdd == null) { 1679 log.error("Found null destination block for path from {}", this.getDisplayName()); 1680 return; 1681 } 1682 1683 if (this.getBlock().isBlockDenied(destBlockToAdd.getDisplayName())) { 1684 ourWorkingDirection = RXONLY; 1685 } else if (destBlockToAdd.isBlockDenied(this.getBlock().getDisplayName())) { 1686 ourWorkingDirection = TXONLY; 1687 } 1688 1689 addRouteLog.debug("From {} to block {} we should therefore be... {}", 1690 getDisplayName(), addPath.getBlock().getDisplayName(), decodePacketFlow(ourWorkingDirection)); 1691 addNeighbour(addPath.getBlock(), addPath.getToBlockDirection(), ourWorkingDirection); 1692 1693 } 1694 1695 // Might be possible to refactor the removal to use a bit of common code. 1696 private void removeAdjacency(Path removedPath) { 1697 Block ablock = removedPath.getBlock(); 1698 if (ablock != null) { 1699 deleteRouteLog.debug("From {} Adjacency to be removed {} {}", 1700 getDisplayName(), ablock.getDisplayName(), Path.decodeDirection(removedPath.getToBlockDirection())); 1701 LayoutBlock layoutBlock = InstanceManager.getDefault( 1702 LayoutBlockManager.class).getLayoutBlock(ablock); 1703 if (layoutBlock != null) { 1704 removeAdjacency(layoutBlock); 1705 } 1706 } else { 1707 log.debug("removeAdjacency() removedPath.getBlock() is null"); 1708 } 1709 } 1710 1711 private void removeAdjacency(LayoutBlock layoutBlock) { 1712 deleteRouteLog.debug("From {} Adjacency to be removed {}", 1713 getDisplayName(), layoutBlock.getDisplayName()); 1714 Block removedBlock = layoutBlock.getBlock(); 1715 1716 // Work our way backward through the list of neighbours 1717 // We need to work out which routes to remove first. 1718 // here we simply remove the routes which are advertised from the removed neighbour 1719 List<Routes> tmpBlock = removeRouteReceivedFromNeighbour(removedBlock); 1720 1721 for (int i = neighbours.size() - 1; i > -1; i--) { 1722 // Use to check against direction but don't now. 1723 if ((neighbours.get(i).getBlock() == removedBlock)) { 1724 // Was previously before the for loop. 1725 // Pos move the remove list and remove thoughpath out of this for loop. 1726 layoutBlock.removePropertyChangeListener(this); 1727 deleteRouteLog.debug("From {} block {} found and removed", 1728 getDisplayName(), removedBlock.getDisplayName()); 1729 LayoutBlock layoutBlockToNotify = InstanceManager.getDefault( 1730 LayoutBlockManager.class).getLayoutBlock(neighbours.get(i).getBlock()); 1731 if (layoutBlockToNotify==null){ // move to provides? 1732 log.error("Unable to notify neighbours for block {}",neighbours.get(i).getBlock()); 1733 continue; 1734 } 1735 getAdjacency(neighbours.get(i).getBlock()).dispose(); 1736 neighbours.remove(i); 1737 layoutBlockToNotify.notifiedNeighbourNoLongerMutual(this); 1738 } 1739 } 1740 1741 for (int i = throughPaths.size() - 1; i > -1; i--) { 1742 if (throughPaths.get(i).getSourceBlock() == removedBlock) { 1743 // only mark for removal if the source isn't in the adjcency table 1744 if (getAdjacency(throughPaths.get(i).getSourceBlock()) == null) { 1745 deleteRouteLog.debug("remove {} to {}", 1746 throughPaths.get(i).getSourceBlock().getDisplayName(), 1747 throughPaths.get(i).getDestinationBlock().getDisplayName()); 1748 throughPaths.remove(i); 1749 } 1750 } else if (throughPaths.get(i).getDestinationBlock() == removedBlock) { 1751 // only mark for removal if the destination isn't in the adjcency table 1752 if (getAdjacency(throughPaths.get(i).getDestinationBlock()) == null) { 1753 deleteRouteLog.debug("remove {} to {}", 1754 throughPaths.get(i).getSourceBlock().getDisplayName(), 1755 throughPaths.get(i).getDestinationBlock().getDisplayName()); 1756 throughPaths.remove(i); 1757 } 1758 } 1759 } 1760 1761 deleteRouteLog.debug("From {} neighbour has been removed - Number of routes to this neighbour removed{}", 1762 getDisplayName(), tmpBlock.size()); 1763 notifyNeighboursOfRemoval(tmpBlock, removedBlock); 1764 } 1765 1766 // This is used when a property event change is triggered for a removed route. 1767 // Not sure that bulk removals will be necessary 1768 private void removeRouteFromNeighbour(LayoutBlock src, RoutingPacket update) { 1769 InstanceManager.getDefault(LayoutBlockManager.class).setLastRoutingChange(); 1770 Block srcblk = src.getBlock(); 1771 Block destblk = update.getBlock(); 1772 String msgPrefix = "From " + this.getDisplayName() + " notify block " + srcblk.getDisplayName() + " "; 1773 1774 deleteRouteLog.debug("{} remove route from neighbour called", msgPrefix); 1775 1776 if (InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(srcblk) == this) { 1777 deleteRouteLog.debug("From {} source block is the same as our block! {}", 1778 getDisplayName(), destblk.getDisplayName()); 1779 return; 1780 } 1781 1782 deleteRouteLog.debug("{} (Direct Notification) neighbour {} has removed route to {}", 1783 msgPrefix, srcblk.getDisplayName(), destblk.getDisplayName()); 1784 deleteRouteLog.debug("{} routes in table {} Remove route from neighbour", msgPrefix, routes.size()); 1785 List<Routes> routesToRemove = new ArrayList<>(); 1786 for (int i = routes.size() - 1; i > -1; i--) { 1787 Routes ro = routes.get(i); 1788 if ((ro.getNextBlock() == srcblk) && (ro.getDestBlock() == destblk)) { 1789 routesToRemove.add(new Routes(routes.get(i).getDestBlock(), routes.get(i).getNextBlock(), 0, 0, 0, 0)); 1790 deleteRouteLog.debug("{} route to {} from block {} to be removed triggered by propertyChange", 1791 msgPrefix, ro.getDestBlock().getDisplayName(), ro.getNextBlock().getDisplayName()); 1792 routes.remove(i); 1793 // We only fire off routing update the once 1794 } 1795 } 1796 notifyNeighboursOfRemoval(routesToRemove, srcblk); 1797 } 1798 1799 private List<Routes> removeRouteReceivedFromNeighbour(Block removedBlock) { 1800 List<Routes> tmpBlock = new ArrayList<>(); 1801 1802 // here we simply remove the routes which are advertised from the removed neighbour 1803 for (int j = routes.size() - 1; j > -1; j--) { 1804 Routes ro = routes.get(j); 1805 deleteRouteLog.debug("From {} route to check {} from Block {}", 1806 getDisplayName(), routes.get(j).getDestBlock().getDisplayName(), 1807 routes.get(j).getNextBlock().getDisplayName()); 1808 1809 if (ro.getDestBlock() == removedBlock) { 1810 deleteRouteLog.debug("From {} route to {} from block {} to be removed" 1811 + " triggered by adjancey removal as dest block has been removed", 1812 getDisplayName(), routes.get(j).getDestBlock().getDisplayName(), 1813 routes.get(j).getNextBlock().getDisplayName()); 1814 1815 if (!tmpBlock.contains(ro)) { 1816 tmpBlock.add(ro); 1817 } 1818 routes.remove(j); 1819 // This will need to be removed fromth directly connected 1820 } else if (ro.getNextBlock() == removedBlock) { 1821 deleteRouteLog.debug("From {} route to {} from block {} to be removed" 1822 + " triggered by adjancey removal", 1823 getDisplayName(), routes.get(j).getDestBlock().getDisplayName(), 1824 routes.get(j).getNextBlock().getDisplayName()); 1825 1826 if (!tmpBlock.contains(ro)) { 1827 tmpBlock.add(ro); 1828 } 1829 routes.remove(j); 1830 // This will also need to be removed from the directly connected list as well. 1831 } 1832 } 1833 return tmpBlock; 1834 } 1835 1836 private void updateNeighbourPacketFlow(Block neighbour, int flow) { 1837 // Packet flow from neighbour will need to be reversed. 1838 Adjacencies neighAdj = getAdjacency(neighbour); 1839 1840 if (flow == RXONLY) { 1841 flow = TXONLY; 1842 } else if (flow == TXONLY) { 1843 flow = RXONLY; 1844 } 1845 1846 if (neighAdj.getPacketFlow() == flow) { 1847 return; 1848 } 1849 updateNeighbourPacketFlow(neighAdj, flow); 1850 } 1851 1852 protected void updateNeighbourPacketFlow(Adjacencies neighbour, final int flow) { 1853 if (neighbour.getPacketFlow() == flow) { 1854 return; 1855 } 1856 1857 final LayoutBlock neighLBlock = neighbour.getLayoutBlock(); 1858 Runnable r = () -> neighLBlock.updateNeighbourPacketFlow(block, flow); 1859 1860 Block neighBlock = neighbour.getBlock(); 1861 int oldPacketFlow = neighbour.getPacketFlow(); 1862 1863 neighbour.setPacketFlow(flow); 1864 1865 SwingUtilities.invokeLater(r); 1866 1867 if (flow == TXONLY) { 1868 neighBlock.addBlockDenyList(this.block); 1869 neighLBlock.removePropertyChangeListener(this); 1870 1871 // This should remove routes learned from our neighbour 1872 List<Routes> tmpBlock = removeRouteReceivedFromNeighbour(neighBlock); 1873 1874 notifyNeighboursOfRemoval(tmpBlock, neighBlock); 1875 1876 // Need to also remove all through paths to this neighbour 1877 for (int i = throughPaths.size() - 1; i > -1; i--) { 1878 if (throughPaths.get(i).getDestinationBlock() == neighBlock) { 1879 throughPaths.remove(i); 1880 firePropertyChange("through-path-removed", null, null); 1881 } 1882 } 1883 1884 // We potentially will need to re-advertise routes to this neighbour 1885 if (oldPacketFlow == RXONLY) { 1886 addThroughPath(neighbour); 1887 } 1888 } else if (flow == RXONLY) { 1889 neighLBlock.addPropertyChangeListener(this); 1890 neighBlock.removeBlockDenyList(this.block); 1891 this.block.addBlockDenyList(neighBlock); 1892 1893 for (int i = throughPaths.size() - 1; i > -1; i--) { 1894 if (throughPaths.get(i).getSourceBlock() == neighBlock) { 1895 throughPaths.remove(i); 1896 firePropertyChange("through-path-removed", null, null); 1897 } 1898 } 1899 1900 // Might need to rebuild through paths. 1901 if (oldPacketFlow == TXONLY) { 1902 routes.add(new Routes(neighBlock, this.getBlock(), 1903 1, neighbour.getDirection(), neighLBlock.getBlockMetric(), neighBlock.getLengthMm())); 1904 addThroughPath(neighbour); 1905 } 1906 // We would need to withdraw the routes that we advertise to the neighbour 1907 } else if (flow == RXTX) { 1908 neighBlock.removeBlockDenyList(this.block); 1909 this.block.removeBlockDenyList(neighBlock); 1910 neighLBlock.addPropertyChangeListener(this); 1911 1912 // Might need to rebuild through paths. 1913 if (oldPacketFlow == TXONLY) { 1914 routes.add(new Routes(neighBlock, this.getBlock(), 1915 1, neighbour.getDirection(), neighLBlock.getBlockMetric(), neighBlock.getLengthMm())); 1916 } 1917 addThroughPath(neighbour); 1918 } 1919 } 1920 1921 private void notifyNeighboursOfRemoval(List<Routes> routesToRemove, Block notifyingblk) { 1922 String msgPrefix = "From " + this.getDisplayName() + " notify block " + notifyingblk.getDisplayName() + " "; 1923 1924 deleteRouteLog.debug("{} notifyNeighboursOfRemoval called for routes from {} ===", 1925 msgPrefix, notifyingblk.getDisplayName()); 1926 boolean notifyvalid = false; 1927 1928 for (int i = neighbours.size() - 1; i > -1; i--) { 1929 if (neighbours.get(i).getBlock() == notifyingblk) { 1930 notifyvalid = true; 1931 } 1932 } 1933 1934 deleteRouteLog.debug("{} The notifying block is still valid? {}", msgPrefix, notifyvalid); 1935 1936 for (int j = routesToRemove.size() - 1; j > -1; j--) { 1937 boolean stillexist = false; 1938 Block destBlock = routesToRemove.get(j).getDestBlock(); 1939 Block sourceBlock = routesToRemove.get(j).getNextBlock(); 1940 RoutingPacket newUpdate = new RoutingPacket(REMOVAL, destBlock, -1, -1, -1, -1, getNextPacketID()); 1941 1942 deleteRouteLog.debug("From {} notify block {} checking {} from {}", 1943 getDisplayName(), notifyingblk.getDisplayName(), 1944 destBlock.getDisplayName(), sourceBlock.getDisplayName()); 1945 List<Routes> validroute = new ArrayList<>(); 1946 List<Routes> destRoutes = getDestRoutes(destBlock); 1947 for (Routes r : destRoutes) { 1948 // We now know that we still have a valid route to the dest 1949 if (r.getNextBlock() == this.getBlock()) { 1950 deleteRouteLog.debug("{} The destBlock {} is our neighbour", 1951 msgPrefix, destBlock.getDisplayName()); 1952 validroute.add(new Routes(r.getDestBlock(), r.getNextBlock(), 0, 0, 0, 0)); 1953 stillexist = true; 1954 } else { 1955 // At this stage do we need to check if the valid route comes from a neighbour? 1956 deleteRouteLog.debug("{} we still have a route to {} via {} in our list", 1957 msgPrefix, destBlock.getDisplayName(), r.getNextBlock().getDisplayName()); 1958 validroute.add(new Routes(destBlock, r.getNextBlock(), 0, 0, 0, 0)); 1959 stillexist = true; 1960 } 1961 } 1962 // We may need to find out who else we could of sent the route to by checking in the through paths 1963 1964 if (stillexist) { 1965 deleteRouteLog.debug("{}A Route still exists", msgPrefix); 1966 deleteRouteLog.debug("{} the number of routes installed to block {} is {}", 1967 msgPrefix, destBlock.getDisplayName(), validroute.size()); 1968 1969 if (validroute.size() == 1) { 1970 // Specific routing update. 1971 Block nextHop = validroute.get(0).getNextBlock(); 1972 LayoutBlock layoutBlock; 1973 if (validroute.get(0).getNextBlock() != this.getBlock()) { 1974 layoutBlock = InstanceManager.getDefault( 1975 LayoutBlockManager.class).getLayoutBlock(nextHop); 1976 deleteRouteLog.debug("{} We only have a single valid route left to {}" 1977 + " So will tell {} we no longer have it", 1978 msgPrefix, destBlock.getDisplayName(), 1979 layoutBlock == null ? "NULL" : layoutBlock.getDisplayName()); 1980 1981 if (layoutBlock != null) { 1982 layoutBlock.removeRouteFromNeighbour(this, newUpdate); 1983 } 1984 getAdjacency(nextHop).removeRouteAdvertisedToNeighbour(routesToRemove.get(j)); 1985 } 1986 1987 // At this point we could probably do with checking for other valid paths from the notifyingblock 1988 // Have a feeling that this is pretty much the same as above! 1989 List<Block> validNeighboursToNotify = new ArrayList<>(); 1990 1991 // Problem we have here is that although we only have one valid route, one of our neighbours 1992 // could still hold a valid through path. 1993 for (int i = neighbours.size() - 1; i > -1; i--) { 1994 // Need to ignore if the dest block is our neighour in this instance 1995 if ((neighbours.get(i).getBlock() != destBlock) && (neighbours.get(i).getBlock() != nextHop) 1996 && validThroughPath(notifyingblk, neighbours.get(i).getBlock())) { 1997 Block neighblock = neighbours.get(i).getBlock(); 1998 1999 deleteRouteLog.debug("{} we could of potentially sent the route to {}", 2000 msgPrefix, neighblock.getDisplayName()); 2001 2002 if (!validThroughPath(nextHop, neighblock)) { 2003 deleteRouteLog.debug("{} there is no other valid path so will mark for removal", 2004 msgPrefix); 2005 validNeighboursToNotify.add(neighblock); 2006 } else { 2007 deleteRouteLog.debug("{} there is another valid path so will NOT mark for removal", 2008 msgPrefix); 2009 } 2010 } 2011 } 2012 2013 deleteRouteLog.debug("{} the next block is our selves so we won't remove!", msgPrefix); 2014 deleteRouteLog.debug("{} do we need to find out if we could of send the route" 2015 + " to another neighbour such as?", msgPrefix); 2016 2017 for (Block value : validNeighboursToNotify) { 2018 // If the neighbour has a valid through path to the dest 2019 // we will not notify the neighbour of our loss of route 2020 if (!validThroughPath(value, destBlock)) { 2021 layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class). 2022 getLayoutBlock(value); 2023 if (layoutBlock != null) { 2024 layoutBlock.removeRouteFromNeighbour(this, newUpdate); 2025 } 2026 getAdjacency(value).removeRouteAdvertisedToNeighbour(routesToRemove.get(j)); 2027 } else { 2028 deleteRouteLog.debug("{}{} has a valid path to {}", 2029 msgPrefix, value.getDisplayName(), destBlock.getDisplayName()); 2030 } 2031 } 2032 } else { 2033 // Need to deal with having multiple routes left. 2034 deleteRouteLog.debug("{} routes left to block {}", msgPrefix, destBlock.getDisplayName()); 2035 2036 for (Routes item : validroute) { 2037 // We need to see if we have valid routes. 2038 if (validThroughPath(notifyingblk, item.getNextBlock())) { 2039 deleteRouteLog.debug("{} to {} Is a valid route", 2040 msgPrefix, item.getNextBlock().getDisplayName()); 2041 // Will mark the route for potential removal 2042 item.setMiscFlags(0x02); 2043 } else { 2044 deleteRouteLog.debug("{} to {} Is not a valid route", 2045 msgPrefix, item.getNextBlock().getDisplayName()); 2046 // Mark the route to not be removed. 2047 item.setMiscFlags(0x01); 2048 2049 // Given that the route to this is not valid, we do not want to 2050 // be notifying this next block about the loss of route. 2051 } 2052 } 2053 2054 // We have marked all the routes for either potential notification of route removal, or definate no removal; 2055 // Now need to get through the list and cross reference each one. 2056 for (int i = 0; i < validroute.size(); i++) { 2057 if (validroute.get(i).getMiscFlags() == 0x02) { 2058 Block nextblk = validroute.get(i).getNextBlock(); 2059 2060 deleteRouteLog.debug("{} route from {} has been flagged for removal", 2061 msgPrefix, nextblk.getDisplayName()); 2062 2063 // Need to cross reference it with the routes that are left. 2064 boolean leaveroute = false; 2065 for (Routes value : validroute) { 2066 if (value.getMiscFlags() == 0x01) { 2067 if (validThroughPath(nextblk, value.getNextBlock())) { 2068 deleteRouteLog.debug("{} we have a valid path from {} to {}", 2069 msgPrefix, nextblk.getDisplayName(), value.getNextBlock()); 2070 leaveroute = true; 2071 } 2072 } 2073 } 2074 2075 if (!leaveroute) { 2076 LayoutBlock layoutBlock = InstanceManager.getDefault( 2077 LayoutBlockManager.class).getLayoutBlock(nextblk); 2078 deleteRouteLog.debug("{}############ We need to send notification to {} to remove route ########### haven't found an example of this yet!", 2079 msgPrefix, nextblk.getDisplayName()); 2080 if (layoutBlock==null) { // change to provides 2081 log.error("Unable to fetch block {}",nextblk); 2082 continue; 2083 } 2084 layoutBlock.removeRouteFromNeighbour(this, newUpdate); 2085 getAdjacency(nextblk).removeRouteAdvertisedToNeighbour(routesToRemove.get(j)); 2086 2087 } else { 2088 deleteRouteLog.debug("{} a valid path through exists {} so we will not remove route.", 2089 msgPrefix, nextblk.getDisplayName()); 2090 } 2091 } 2092 } 2093 } 2094 } else { 2095 deleteRouteLog.debug("{} We have no other routes to {} Therefore we will broadast this to our neighbours", 2096 msgPrefix, destBlock.getDisplayName()); 2097 2098 for (Adjacencies adj : neighbours) { 2099 adj.removeRouteAdvertisedToNeighbour(destBlock); 2100 } 2101 firePropertyChange("routing", null, newUpdate); 2102 } 2103 } 2104 2105 deleteRouteLog.debug("{} finshed check and notifying of removed routes from {} ===", 2106 msgPrefix, notifyingblk.getDisplayName()); 2107 } 2108 2109 private void addThroughPath(Adjacencies adj) { 2110 Block newAdj = adj.getBlock(); 2111 int packetFlow = adj.getPacketFlow(); 2112 2113 addRouteLog.debug("From {} addThroughPathCalled with adj {}", 2114 getDisplayName(), adj.getBlock().getDisplayName()); 2115 2116 for (Adjacencies neighbour : neighbours) { 2117 // cycle through all the neighbours 2118 if (neighbour.getBlock() != newAdj) { 2119 int neighPacketFlow = neighbour.getPacketFlow(); 2120 2121 addRouteLog.debug("From {} our direction: {}, neighbour direction: {}", 2122 getDisplayName(), decodePacketFlow(packetFlow), decodePacketFlow(neighPacketFlow)); 2123 2124 if ((packetFlow == RXTX) && (neighPacketFlow == RXTX)) { 2125 // if both are RXTX then add flow in both directions 2126 addThroughPath(neighbour.getBlock(), newAdj); 2127 addThroughPath(newAdj, neighbour.getBlock()); 2128 } else if ((packetFlow == RXONLY) && (neighPacketFlow == TXONLY)) { 2129 addThroughPath(neighbour.getBlock(), newAdj); 2130 } else if ((packetFlow == TXONLY) && (neighPacketFlow == RXONLY)) { 2131 addThroughPath(newAdj, neighbour.getBlock()); 2132 } else if ((packetFlow == RXTX) && (neighPacketFlow == TXONLY)) { // was RX 2133 addThroughPath(neighbour.getBlock(), newAdj); 2134 } else if ((packetFlow == RXTX) && (neighPacketFlow == RXONLY)) { // was TX 2135 addThroughPath(newAdj, neighbour.getBlock()); 2136 } else if ((packetFlow == RXONLY) && (neighPacketFlow == RXTX)) { 2137 addThroughPath(neighbour.getBlock(), newAdj); 2138 } else if ((packetFlow == TXONLY) && (neighPacketFlow == RXTX)) { 2139 addThroughPath(newAdj, neighbour.getBlock()); 2140 } else { 2141 addRouteLog.debug("Invalid combination {} and {}", 2142 decodePacketFlow(packetFlow), decodePacketFlow(neighPacketFlow)); 2143 } 2144 } 2145 } 2146 } 2147 2148 /** 2149 * Add a path between two blocks, but without spec a panel. 2150 */ 2151 private void addThroughPath(Block srcBlock, Block dstBlock) { 2152 addRouteLog.debug("Block {}.addThroughPath(src:{}, dst: {})", 2153 getDisplayName(), srcBlock.getDisplayName(), dstBlock.getDisplayName()); 2154 2155 if ((block != null) && (panels.size() > 0)) { 2156 // a block is attached and this LayoutBlock is used 2157 // initialize connectivity as defined in first Layout Editor panel 2158 LayoutEditor panel = panels.get(0); 2159 List<LayoutConnectivity> c = panel.getLEAuxTools().getConnectivityList(this); 2160 2161 // if more than one panel, find panel with the highest connectivity 2162 if (panels.size() > 1) { 2163 for (int i = 1; i < panels.size(); i++) { 2164 if (c.size() < panels.get(i).getLEAuxTools(). 2165 getConnectivityList(this).size()) { 2166 panel = panels.get(i); 2167 c = panel.getLEAuxTools().getConnectivityList(this); 2168 } 2169 } 2170 2171 // check that this connectivity is compatible with that of other panels. 2172 for (LayoutEditor tPanel : panels) { 2173 if ((tPanel != panel) && InstanceManager.getDefault(LayoutBlockManager.class). 2174 warn() && (!compareConnectivity(c, 2175 tPanel.getLEAuxTools().getConnectivityList(this)))) { 2176 // send user an error message 2177 int response = JmriJOptionPane.showOptionDialog(null, 2178 java.text.MessageFormat.format(Bundle.getMessage("Warn1"), 2179 new Object[]{getUserName(), tPanel.getLayoutName(), 2180 panel.getLayoutName()}), Bundle.getMessage("WarningTitle"), 2181 JmriJOptionPane.DEFAULT_OPTION, JmriJOptionPane.QUESTION_MESSAGE, 2182 null, 2183 new Object[]{Bundle.getMessage("ButtonOK"), Bundle.getMessage("ButtonOKPlus")}, 2184 Bundle.getMessage("ButtonOK")); 2185 if (response == 1 ) { // array position 1 ButtonOKPlus pressed, user elected to disable messages 2186 InstanceManager.getDefault(LayoutBlockManager.class).turnOffWarning(); 2187 } 2188 } 2189 } 2190 } 2191 // update block Paths to reflect connectivity as needed 2192 addThroughPath(srcBlock, dstBlock, panel); 2193 } 2194 } 2195 2196 private LayoutEditorAuxTools auxTools = null; 2197 private ConnectivityUtil connection = null; 2198 private boolean layoutConnectivity = true; 2199 2200 /** 2201 * Add a through path on this layout block, going from the source block to 2202 * the destination block, using a specific panel. Note: If the reverse path 2203 * is required, then this needs to be added seperately. 2204 */ 2205 // Was public 2206 private void addThroughPath(Block srcBlock, Block dstBlock, LayoutEditor panel) { 2207 // Reset connectivity flag. 2208 layoutConnectivity = true; 2209 2210 if (srcBlock == dstBlock) { 2211 // Do not do anything if the blocks are the same! 2212 return; 2213 } 2214 2215 addRouteLog.debug("Block {}.addThroughPath(src:{}, dst: {}, <panel>)", 2216 getDisplayName(), srcBlock.getDisplayName(), dstBlock.getDisplayName()); 2217 2218 // Initally check to make sure that the through path doesn't already exist. 2219 // no point in going through the checks if the path already exists. 2220 boolean add = true; 2221 for (ThroughPaths throughPath : throughPaths) { 2222 if (throughPath.getSourceBlock() == srcBlock) { 2223 if (throughPath.getDestinationBlock() == dstBlock) { 2224 add = false; 2225 } 2226 } 2227 } 2228 2229 if (!add) { 2230 return; 2231 } 2232 2233 addRouteLog.debug("Block {}, src: {}, dst: {}", 2234 block.getDisplayName(), srcBlock.getDisplayName(), dstBlock.getDisplayName()); 2235 connection = panel.getConnectivityUtil(); 2236 List<LayoutTrackExpectedState<LayoutTurnout>> stod; 2237 2238 try { 2239 MDC.put("loggingDisabled", connection.getClass().getCanonicalName()); 2240 stod = connection.getTurnoutList(block, srcBlock, dstBlock, true); 2241 MDC.remove("loggingDisabled"); 2242 } catch (java.lang.NullPointerException ex) { 2243 MDC.remove("loggingDisabled"); 2244 if (addRouteLog.isDebugEnabled()) { 2245 log.error("Exception ({}) caught while trying to discover turnout connectivity" 2246 + "\nBlock: {}, srcBlock ({}) to dstBlock ({})", ex.getMessage(), 2247 block.getDisplayName(), srcBlock.getDisplayName(), dstBlock.getDisplayName()); 2248 log.error("@ Line # {}", ex.getStackTrace()[1].getLineNumber()); 2249 } 2250 return; 2251 } 2252 2253 if (!connection.isTurnoutConnectivityComplete()) { 2254 layoutConnectivity = false; 2255 } 2256 List<LayoutTrackExpectedState<LayoutTurnout>> tmpdtos; 2257 2258 try { 2259 MDC.put("loggingDisabled", connection.getClass().getName()); 2260 tmpdtos = connection.getTurnoutList(block, dstBlock, srcBlock, true); 2261 MDC.remove("loggingDisabled"); 2262 } catch (java.lang.NullPointerException ex) { 2263 MDC.remove("loggingDisabled"); 2264 addRouteLog.debug("Exception ({}) caught while trying to discover turnout connectivity" 2265 + "\nBlock: {}, dstBlock ({}) to srcBlock ({})", ex.getMessage(), 2266 block.getDisplayName(), dstBlock.getDisplayName(), srcBlock.getDisplayName()); 2267 addRouteLog.debug("@ Line # {}", ex.getStackTrace()[1].getLineNumber()); 2268 return; 2269 } 2270 2271 if (!connection.isTurnoutConnectivityComplete()) { 2272 layoutConnectivity = false; 2273 } 2274 2275 if (stod.size() == tmpdtos.size()) { 2276 // Need to reorder the tmplist (dst-src) to be the same order as src-dst 2277 List<LayoutTrackExpectedState<LayoutTurnout>> dtos = new ArrayList<>(); 2278 for (int i = tmpdtos.size(); i > 0; i--) { 2279 dtos.add(tmpdtos.get(i - 1)); 2280 } 2281 2282 // check to make sure that we pass through the same turnouts 2283 addRouteLog.debug("From {} destination size {} v source size {}", 2284 getDisplayName(), dtos.size(), stod.size()); 2285 2286 for (int i = 0; i < dtos.size(); i++) { 2287 if (dtos.get(i).getObject() != stod.get(i).getObject()) { 2288 addRouteLog.debug("{} != {}: will quit", dtos.get(i).getObject(), stod.get(i).getObject()); 2289 return; 2290 } 2291 } 2292 2293 for (int i = 0; i < dtos.size(); i++) { 2294 int x = stod.get(i).getExpectedState(); 2295 int y = dtos.get(i).getExpectedState(); 2296 2297 if (x != y) { 2298 addRouteLog.debug("{} not on setting equal will quit {}, {}", block.getDisplayName(), x, y); 2299 return; 2300 } else if (x == Turnout.UNKNOWN) { 2301 addRouteLog.debug("{} turnout state returned as UNKNOWN", block.getDisplayName()); 2302 return; 2303 } 2304 } 2305 Set<LayoutTurnout> set = new HashSet<>(); 2306 2307 for (LayoutTrackExpectedState<LayoutTurnout> layoutTurnoutLayoutTrackExpectedState : stod) { 2308 boolean val = set.add(layoutTurnoutLayoutTrackExpectedState.getObject()); 2309 if (val == false) { 2310 // Duplicate found. will not add 2311 return; 2312 } 2313 } 2314 // for (LayoutTurnout turn : stod) { 2315 // if (turn.type == LayoutTurnout.DOUBLE_XOVER) { 2316 // // Further checks might be required. 2317 // } 2318 //} 2319 addThroughPathPostChecks(srcBlock, dstBlock, stod); 2320 } else { 2321 // We know that a path that contains a double cross-over, is not reported correctly, 2322 // therefore we shall do some additional checks and add it. 2323 addRouteLog.debug("sizes are not the same therefore, we will do some further checks"); 2324 List<LayoutTrackExpectedState<LayoutTurnout>> maxt; 2325 if (stod.size() >= tmpdtos.size()) { 2326 maxt = stod; 2327 } else { 2328 maxt = tmpdtos; 2329 } 2330 2331 Set<LayoutTrackExpectedState<LayoutTurnout>> set = new HashSet<>(maxt); 2332 2333 if (set.size() == maxt.size()) { 2334 addRouteLog.debug("All turnouts are unique so potentially a valid path"); 2335 boolean allowAddition = false; 2336 for (LayoutTrackExpectedState<LayoutTurnout> layoutTurnoutLayoutTrackExpectedState : maxt) { 2337 LayoutTurnout turn = layoutTurnoutLayoutTrackExpectedState.getObject(); 2338 if (turn.type == LayoutTurnout.TurnoutType.DOUBLE_XOVER) { 2339 allowAddition = true; 2340 // The double crossover gets reported in the opposite setting. 2341 if (layoutTurnoutLayoutTrackExpectedState.getExpectedState() == 2) { 2342 layoutTurnoutLayoutTrackExpectedState.setExpectedState(4); 2343 } else { 2344 layoutTurnoutLayoutTrackExpectedState.setExpectedState(2); 2345 } 2346 } 2347 } 2348 2349 if (allowAddition) { 2350 addRouteLog.debug("addition allowed"); 2351 addThroughPathPostChecks(srcBlock, dstBlock, maxt); 2352 } else { 2353 addRouteLog.debug("No double cross-over so not a valid path"); 2354 } 2355 } 2356 } 2357 } // addThroughPath 2358 2359 private void addThroughPathPostChecks(Block srcBlock, 2360 Block dstBlock, List<LayoutTrackExpectedState<LayoutTurnout>> stod) { 2361 List<Path> paths = block.getPaths(); 2362 Path srcPath = null; 2363 2364 for (Path item : paths) { 2365 if (item.getBlock() == srcBlock) { 2366 srcPath = item; 2367 } 2368 } 2369 Path dstPath = null; 2370 2371 for (Path value : paths) { 2372 if (value.getBlock() == dstBlock) { 2373 dstPath = value; 2374 } 2375 } 2376 ThroughPaths path = new ThroughPaths(srcBlock, srcPath, dstBlock, dstPath); 2377 path.setTurnoutList(stod); 2378 2379 addRouteLog.debug("From {} added Throughpath {} {}", 2380 getDisplayName(), path.getSourceBlock().getDisplayName(), path.getDestinationBlock().getDisplayName()); 2381 throughPaths.add(path); 2382 firePropertyChange("through-path-added", null, null); 2383 2384 // update our neighbours of the new valid paths; 2385 informNeighbourOfValidRoutes(srcBlock); 2386 informNeighbourOfValidRoutes(dstBlock); 2387 } 2388 2389 void notifiedNeighbourNoLongerMutual(LayoutBlock srcBlock) { 2390 deleteRouteLog.debug("From {}Notification from neighbour that it is no longer our friend {}", 2391 getDisplayName(), srcBlock.getDisplayName()); 2392 Block blk = srcBlock.getBlock(); 2393 2394 for (int i = neighbours.size() - 1; i > -1; i--) { 2395 // Need to check if the block we are being informed about has already been removed or not 2396 if (neighbours.get(i).getBlock() == blk) { 2397 removeAdjacency(srcBlock); 2398 break; 2399 } 2400 } 2401 } 2402 2403 public static final int RESERVED = 0x08; 2404 2405 void stateUpdate() { 2406 // Need to find a way to fire off updates to the various tables 2407 updateRouteLog.trace("From {} A block state change ({}) has occurred", getDisplayName(), getBlockStatusString()); 2408 RoutingPacket update = new RoutingPacket(UPDATE, this.getBlock(), -1, -1, -1, getBlockStatus(), getNextPacketID()); 2409 firePropertyChange("routing", null, update); 2410 } 2411 2412 int getBlockStatus() { 2413 if (getOccupancy() == OCCUPIED) { 2414 useExtraColor = false; 2415 // Our section of track is occupied 2416 return OCCUPIED; 2417 } else if (useExtraColor) { 2418 return RESERVED; 2419 } else if (getOccupancy() == EMPTY) { 2420 return EMPTY; 2421 } else { 2422 return UNKNOWN; 2423 } 2424 } 2425 2426 String getBlockStatusString() { 2427 String result = "UNKNOWN"; 2428 if (getOccupancy() == OCCUPIED) { 2429 result = "OCCUPIED"; 2430 } else if (useExtraColor) { 2431 result = "RESERVED"; 2432 } else if (getOccupancy() == EMPTY) { 2433 result = "EMPTY"; 2434 } 2435 return result; 2436 } 2437 2438 Integer getNextPacketID() { 2439 Integer lastID; 2440 2441 if (updateReferences.isEmpty()) { 2442 lastID = 0; 2443 } else { 2444 int lastIDPos = updateReferences.size() - 1; 2445 lastID = updateReferences.get(lastIDPos) + 1; 2446 } 2447 2448 if (lastID > 2000) { 2449 lastID = 0; 2450 } 2451 updateReferences.add(lastID); 2452 2453 /*As we are originating a packet, we will added to the acted upion list 2454 thus making sure if the packet gets back to us we do knowing with it.*/ 2455 actedUponUpdates.add(lastID); 2456 2457 if (updateReferences.size() > 500) { 2458 // log.info("flush update references"); 2459 updateReferences.subList(0, 250).clear(); 2460 } 2461 2462 if (actedUponUpdates.size() > 500) { 2463 actedUponUpdates.subList(0, 250).clear(); 2464 } 2465 return lastID; 2466 } 2467 2468 boolean updatePacketActedUpon(Integer packetID) { 2469 return actedUponUpdates.contains(packetID); 2470 } 2471 2472 public List<Block> getActiveNextBlocks(Block source) { 2473 List<Block> currentPath = new ArrayList<>(); 2474 2475 for (ThroughPaths path : throughPaths) { 2476 if ((path.getSourceBlock() == source) && (path.isPathActive())) { 2477 currentPath.add(path.getDestinationBlock()); 2478 } 2479 } 2480 return currentPath; 2481 } 2482 2483 public Path getThroughPathSourcePathAtIndex(int i) { 2484 return throughPaths.get(i).getSourcePath(); 2485 } 2486 2487 public Path getThroughPathDestinationPathAtIndex(int i) { 2488 return throughPaths.get(i).getDestinationPath(); 2489 } 2490 2491 public boolean validThroughPath(Block sourceBlock, Block destinationBlock) { 2492 for (ThroughPaths throughPath : throughPaths) { 2493 if ((throughPath.getSourceBlock() == sourceBlock) && (throughPath.getDestinationBlock() == destinationBlock)) { 2494 return true; 2495 } else if ((throughPath.getSourceBlock() == destinationBlock) && (throughPath.getDestinationBlock() == sourceBlock)) { 2496 return true; 2497 } 2498 } 2499 return false; 2500 } 2501 2502 public int getThroughPathIndex(Block sourceBlock, Block destinationBlock) { 2503 for (int i = 0; i < throughPaths.size(); i++) { 2504 if ((throughPaths.get(i).getSourceBlock() == sourceBlock) 2505 && (throughPaths.get(i).getDestinationBlock() == destinationBlock)) { 2506 return i; 2507 } else if ((throughPaths.get(i).getSourceBlock() == destinationBlock) 2508 && (throughPaths.get(i).getDestinationBlock() == sourceBlock)) { 2509 return i; 2510 } 2511 } 2512 return -1; 2513 } 2514 2515 List<Adjacencies> neighbours = new ArrayList<>(); 2516 2517 List<ThroughPaths> throughPaths = new ArrayList<>(); 2518 2519 // A sub class that holds valid routes through the block. 2520 // Possibly want to store the path direction in here as well. 2521 // or we store the ref to the path, so we can get the directions. 2522 List<Routes> routes = new ArrayList<>(); 2523 2524 String decodePacketFlow(int value) { 2525 switch (value) { 2526 case RXTX: { 2527 return "Bi-Direction Operation"; 2528 } 2529 2530 case RXONLY: { 2531 return "Uni-Directional - Trains can only exit to this block (RX) "; 2532 } 2533 2534 case TXONLY: { 2535 return "Uni-Directional - Trains can not be sent down this block (TX) "; 2536 } 2537 2538 case NONE: { 2539 return "None routing updates will be passed"; 2540 } 2541 default: 2542 log.warn("Unhandled packet flow value: {}", value); 2543 break; 2544 } 2545 return "Unknown"; 2546 } 2547 2548 /** 2549 * Provide an output to the console of all the valid paths through this 2550 * block. 2551 */ 2552 public void printValidThroughPaths() { 2553 log.info("Through paths for block {}", this.getDisplayName()); 2554 log.info("Current Block, From Block, To Block"); 2555 for (ThroughPaths tp : throughPaths) { 2556 String activeStr = ""; 2557 if (tp.isPathActive()) { 2558 activeStr = ", *"; 2559 } 2560 log.info("From {}, {}, {}{}", this.getDisplayName(), (tp.getSourceBlock()).getDisplayName(), (tp.getDestinationBlock()).getDisplayName(), activeStr); 2561 } 2562 } 2563 2564 /** 2565 * Provide an output to the console of all our neighbouring blocks. 2566 */ 2567 public void printAdjacencies() { 2568 log.info("Adjacencies for block {}", this.getDisplayName()); 2569 log.info("Neighbour, Direction, mutual, relationship, metric"); 2570 for (Adjacencies neighbour : neighbours) { 2571 log.info(" neighbor: {}, {}, {}, {}, {}", neighbour.getBlock().getDisplayName(), 2572 Path.decodeDirection(neighbour.getDirection()), neighbour.isMutual(), 2573 decodePacketFlow(neighbour.getPacketFlow()), neighbour.getMetric()); 2574 } 2575 } 2576 2577 /** 2578 * Provide an output to the console of all the remote blocks reachable from 2579 * our block. 2580 */ 2581 public void printRoutes() { 2582 log.info("Routes for block {}", this.getDisplayName()); 2583 log.info("Destination, Next Block, Hop Count, Direction, State, Metric"); 2584 for (Routes r : routes) { 2585 String nexthop = r.getNextBlock().getDisplayName(); 2586 2587 if (r.getNextBlock() == this.getBlock()) { 2588 nexthop = "Directly Connected"; 2589 } 2590 String activeString = ""; 2591 if (r.isRouteCurrentlyValid()) { 2592 activeString = ", *"; 2593 } 2594 2595 log.info(" neighbor: {}, {}, {}, {}, {}, {}{}", r.getDestBlock().getDisplayName(), 2596 nexthop, r.getHopCount(), Path.decodeDirection(r.getDirection()), 2597 r.getState(), r.getMetric(), activeString); 2598 } 2599 } 2600 2601 /** 2602 * Provide an output to the console of how to reach a specific block from 2603 * our block. 2604 * 2605 * @param inBlockName to find in route 2606 */ 2607 public void printRoutes(String inBlockName) { 2608 log.info("Routes for block {}", this.getDisplayName()); 2609 log.info("Our Block, Destination, Next Block, Hop Count, Direction, Metric"); 2610 for (Routes route : routes) { 2611 if (route.getDestBlock().getDisplayName().equals(inBlockName)) { 2612 log.info("From {}, {}, {}, {}, {}, {}", 2613 getDisplayName(), (route.getDestBlock()).getDisplayName(), 2614 route.getNextBlock().getDisplayName(), route.getHopCount(), 2615 Path.decodeDirection(route.getDirection()), route.getMetric()); 2616 } 2617 } 2618 } 2619 2620 /** 2621 * @param destBlock is the destination of the block we are following 2622 * @param direction is the direction of travel from the previous block 2623 * @return next block 2624 */ 2625 public Block getNextBlock(Block destBlock, int direction) { 2626 int bestMetric = 965000; 2627 Block bestBlock = null; 2628 2629 for (Routes r : routes) { 2630 if ((r.getDestBlock() == destBlock) && (r.getDirection() == direction)) { 2631 if (r.getMetric() < bestMetric) { 2632 bestMetric = r.getMetric(); 2633 bestBlock = r.getNextBlock(); 2634 // bestBlock=r.getDestBlock(); 2635 } 2636 } 2637 } 2638 return bestBlock; 2639 } 2640 2641 /** 2642 * Used if we already know the block prior to our block, and the destination 2643 * block. direction, is optional and is used where the previousBlock is 2644 * equal to our block. 2645 * 2646 * @param previousBlock start block 2647 * @param destBlock finish block 2648 * @return next block 2649 */ 2650 @CheckForNull 2651 public Block getNextBlock(Block previousBlock, Block destBlock) { 2652 int bestMetric = 965000; 2653 Block bestBlock = null; 2654 2655 for (Routes r : routes) { 2656 if (r.getDestBlock() == destBlock) { 2657 // Check that the route through from the previous block, to the next hop is valid 2658 if (validThroughPath(previousBlock, r.getNextBlock())) { 2659 if (r.getMetric() < bestMetric) { 2660 bestMetric = r.getMetric(); 2661 // bestBlock=r.getDestBlock(); 2662 bestBlock = r.getNextBlock(); 2663 } 2664 } 2665 } 2666 } 2667 return bestBlock; 2668 } 2669 2670 public int getConnectedBlockRouteIndex(Block destBlock, int direction) { 2671 for (int i = 0; i < routes.size(); i++) { 2672 if (routes.get(i).getNextBlock() == this.getBlock()) { 2673 log.info("Found a block that is directly connected"); 2674 2675 if ((routes.get(i).getDestBlock() == destBlock)) { 2676 log.info("In getConnectedBlockRouteIndex, {}", 2677 Integer.toString(routes.get(i).getDirection() & direction)); 2678 if ((routes.get(i).getDirection() & direction) != 0) { 2679 return i; 2680 } 2681 } 2682 } 2683 2684 if (log.isDebugEnabled()) { 2685 log.debug("From {}, {}, nexthop {}, {}, {}, {}", getDisplayName(), 2686 routes.get(i).getDestBlock().getDisplayName(), 2687 routes.get(i).getHopCount(), 2688 Path.decodeDirection(routes.get(i).getDirection()), 2689 routes.get(i).getState(), routes.get(i).getMetric()); 2690 } 2691 } 2692 return -1; 2693 } 2694 2695 // Need to work on this to deal with the method of routing 2696 public int getNextBlockByIndex(Block destBlock, int direction, int offSet) { 2697 for (int i = offSet; i < routes.size(); i++) { 2698 Routes ro = routes.get(i); 2699 if ((ro.getDestBlock() == destBlock)) { 2700 log.info("getNextBlockByIndex {}", Integer.toString(ro.getDirection() & direction)); 2701 if ((ro.getDirection() & direction) != 0) { 2702 return i; 2703 } 2704 } 2705 } 2706 return -1; 2707 } 2708 2709 // Need to work on this to deal with the method of routing 2710 /* 2711 * 2712 */ 2713 public int getNextBlockByIndex(Block previousBlock, Block destBlock, int offSet) { 2714 for (int i = offSet; i < routes.size(); i++) { 2715 Routes ro = routes.get(i); 2716 // log.info(r.getDestBlock().getDisplayName() + " vs " + destBlock.getDisplayName()); 2717 if (ro.getDestBlock() == destBlock) { 2718 // Check that the route through from the previous block, to the next hop is valid 2719 if (validThroughPath(previousBlock, ro.getNextBlock())) { 2720 log.debug("valid through path"); 2721 return i; 2722 } 2723 2724 if (ro.getNextBlock() == this.getBlock()) { 2725 log.debug("getNextBlock is this block therefore directly connected"); 2726 return i; 2727 } 2728 } 2729 } 2730 return -1; 2731 } 2732 2733 /** 2734 * last index - the index of the last block we returned ie we last returned 2735 * index 10, so we don't want to return it again. The block returned will 2736 * have a hopcount or metric equal to or greater than the one of the last 2737 * block returned. if the exclude block list is empty this is the first 2738 * time, it has been used. The parameters for the best last block are based 2739 * upon the last entry in the excludedBlock list. 2740 * 2741 * @param previousBlock starting block 2742 * @param destBlock finish block 2743 * @param excludeBlock blocks to skip 2744 * @param routingMethod value to match metric 2745 * @return next block 2746 */ 2747 public int getNextBestBlock(Block previousBlock, Block destBlock, List<Integer> excludeBlock, LayoutBlockConnectivityTools.Metric routingMethod) { 2748 searchRouteLog.debug("From {} find best route from {} to {} index {} routingMethod {}", 2749 getDisplayName(), previousBlock.getDisplayName(), destBlock.getDisplayName(), excludeBlock, routingMethod); 2750 2751 int bestCount = 965255; // set stupidly high 2752 int bestIndex = -1; 2753 int lastValue = 0; 2754 List<Block> nextBlocks = new ArrayList<>(5); 2755 if (!excludeBlock.isEmpty() && (excludeBlock.get(excludeBlock.size() - 1) < routes.size())) { 2756 if (routingMethod == LayoutBlockConnectivityTools.Metric.METRIC) { 2757 lastValue = routes.get(excludeBlock.get(excludeBlock.size() - 1)).getMetric(); 2758 } else /* if (routingMethod==LayoutBlockManager.HOPCOUNT)*/ { 2759 lastValue = routes.get(excludeBlock.get(excludeBlock.size() - 1)).getHopCount(); 2760 } 2761 2762 for (int i : excludeBlock) { 2763 nextBlocks.add(routes.get(i).getNextBlock()); 2764 } 2765 2766 searchRouteLog.debug("last index is {} {}", excludeBlock.get(excludeBlock.size() - 1), 2767 routes.get(excludeBlock.get(excludeBlock.size() - 1)).getDestBlock().getDisplayName()); 2768 } 2769 2770 for (int i = 0; i < routes.size(); i++) { 2771 if (!excludeBlock.contains(i)) { 2772 Routes ro = routes.get(i); 2773 if (!nextBlocks.contains(ro.getNextBlock())) { 2774 // if(ro.getNextBlock()!=nextBlock){ 2775 int currentValue; 2776 if (routingMethod == LayoutBlockConnectivityTools.Metric.METRIC) { 2777 currentValue = routes.get(i).getMetric(); 2778 } else /*if (routingMethod==InstanceManager.getDefault( 2779 LayoutBlockManager.class).HOPCOUNT)*/ { 2780 currentValue = routes.get(i).getHopCount(); // was lastindex changed to i 2781 } 2782 2783 if (currentValue >= lastValue) { 2784 if (ro.getDestBlock() == destBlock) { 2785 searchRouteLog.debug("Match on dest blocks"); 2786 // Check that the route through from the previous block, to the next hop is valid 2787 searchRouteLog.debug("Is valid through path previous block {} to {}", 2788 previousBlock.getDisplayName(), ro.getNextBlock().getDisplayName()); 2789 2790 if (validThroughPath(previousBlock, ro.getNextBlock())) { 2791 searchRouteLog.debug("valid through path"); 2792 2793 if (routingMethod == LayoutBlockConnectivityTools.Metric.METRIC) { 2794 if (ro.getMetric() < bestCount) { 2795 bestIndex = i; 2796 bestCount = ro.getMetric(); 2797 } 2798 } else /*if (routingMethod==InstanceManager.getDefault( 2799 LayoutBlockManager.class).HOPCOUNT)*/ { 2800 if (ro.getHopCount() < bestCount) { 2801 bestIndex = i; 2802 bestCount = ro.getHopCount(); 2803 } 2804 } 2805 } 2806 2807 if (ro.getNextBlock() == this.getBlock()) { 2808 searchRouteLog.debug("getNextBlock is this block therefore directly connected"); 2809 return i; 2810 } 2811 } 2812 } 2813 } 2814 } 2815 } 2816 2817 searchRouteLog.debug("returning {} best count {}", bestIndex, bestCount); 2818 return bestIndex; 2819 } 2820 2821 @CheckForNull 2822 Routes getRouteByDestBlock(Block blk) { 2823 for (int i = routes.size() - 1; i > -1; i--) { 2824 if (routes.get(i).getDestBlock() == blk) { 2825 return routes.get(i); 2826 } 2827 } 2828 return null; 2829 } 2830 2831 @Nonnull 2832 List<Routes> getRouteByNeighbour(Block blk) { 2833 List<Routes> rtr = new ArrayList<>(); 2834 for (Routes route : routes) { 2835 if (route.getNextBlock() == blk) { 2836 rtr.add(route); 2837 } 2838 } 2839 return rtr; 2840 } 2841 2842 int getAdjacencyPacketFlow(Block blk) { 2843 for (Adjacencies neighbour : neighbours) { 2844 if (neighbour.getBlock() == blk) { 2845 return neighbour.getPacketFlow(); 2846 } 2847 } 2848 return -1; 2849 } 2850 2851 boolean isValidNeighbour(Block blk) { 2852 for (Adjacencies neighbour : neighbours) { 2853 if (neighbour.getBlock() == blk) { 2854 return true; 2855 } 2856 } 2857 return false; 2858 } 2859 2860 @Override 2861 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { 2862 if (listener == this) { 2863 log.debug("adding ourselves as a listener for some strange reason! Skipping"); 2864 return; 2865 } 2866 super.addPropertyChangeListener(listener); 2867 } 2868 2869 @Override 2870 public void propertyChange(PropertyChangeEvent e) { 2871 2872 switch (e.getPropertyName()) { 2873 case "NewRoute": { 2874 updateRouteLog.debug("==Event type {} New {}", 2875 e.getPropertyName(), ((LayoutBlock) e.getNewValue()).getDisplayName()); 2876 break; 2877 } 2878 case "through-path-added": { 2879 updateRouteLog.debug("neighbour has new through path"); 2880 break; 2881 } 2882 case "through-path-removed": { 2883 updateRouteLog.debug("neighbour has through removed"); 2884 break; 2885 } 2886 case "routing": { 2887 if (e.getSource() instanceof LayoutBlock) { 2888 LayoutBlock sourceLayoutBlock = (LayoutBlock) e.getSource(); 2889 updateRouteLog.debug("From {} we have a routing packet update from neighbour {}", 2890 getDisplayName(), sourceLayoutBlock.getDisplayName()); 2891 RoutingPacket update = (RoutingPacket) e.getNewValue(); 2892 int updateType = update.getPacketType(); 2893 switch (updateType) { 2894 case ADDITION: { 2895 updateRouteLog.debug("\t updateType: Addition"); 2896 // InstanceManager.getDefault( 2897 // LayoutBlockManager.class).setLastRoutingChange(); 2898 addRouteFromNeighbour(sourceLayoutBlock, update); 2899 break; 2900 } 2901 case UPDATE: { 2902 updateRouteLog.debug("\t updateType: Update"); 2903 updateRoutingInfo(sourceLayoutBlock, update); 2904 break; 2905 } 2906 case REMOVAL: { 2907 updateRouteLog.debug("\t updateType: Removal"); 2908 InstanceManager.getDefault(LayoutBlockManager.class).setLastRoutingChange(); 2909 removeRouteFromNeighbour(sourceLayoutBlock, update); 2910 break; 2911 } 2912 default: { 2913 break; 2914 } 2915 } // switch (updateType) 2916 } // if (e.getSource() instanceof LayoutBlock) 2917 break; 2918 } 2919 default: { 2920 log.debug("Unhandled propertyChange({}): ", e); 2921 break; 2922 } 2923 } // switch (e.getPropertyName()) 2924 } // propertyChange 2925 2926 /** 2927 * Get valid Routes, based upon the next block and destination block 2928 * 2929 * @param nxtBlock next block 2930 * @param dstBlock final block 2931 * @return routes that fit, or null 2932 */ 2933 @CheckForNull 2934 Routes getValidRoute(Block nxtBlock, Block dstBlock) { 2935 if ((nxtBlock != null) && (dstBlock != null)) { 2936 List<Routes> rtr = getRouteByNeighbour(nxtBlock); 2937 2938 if (rtr.isEmpty()) { 2939 log.debug("From {}, no routes returned for getRouteByNeighbour({})", 2940 this.getDisplayName(), 2941 nxtBlock.getDisplayName()); 2942 return null; 2943 } 2944 2945 for (Routes rt : rtr) { 2946 if (rt.getDestBlock() == dstBlock) { 2947 log.debug("From {}, found dest {}.", this.getDisplayName(), dstBlock.getDisplayName()); 2948 return rt; 2949 } 2950 } 2951 log.debug("From {}, no routes to {}.", this.getDisplayName(), nxtBlock.getDisplayName()); 2952 } else { 2953 log.warn("getValidRoute({}, {}", 2954 (nxtBlock != null) ? nxtBlock.getDisplayName() : "<null>", 2955 (dstBlock != null) ? dstBlock.getDisplayName() : "<null>"); 2956 } 2957 return null; 2958 } 2959 2960 /** 2961 * Is the route to the destination block, going via our neighbouring block 2962 * valid. ie Does the block have a route registered via neighbour 2963 * "protecting" to the destination block. 2964 * 2965 * @param protecting neighbour block that might protect 2966 * @param destination block 2967 * @return true if we have valid path to block 2968 */ 2969 public boolean isRouteToDestValid(Block protecting, Block destination) { 2970 if (protecting == destination) { 2971 log.debug("protecting and destination blocks are the same therefore we need to check if we have a valid neighbour"); 2972 2973 // We are testing for a directly connected block. 2974 if (getAdjacency(protecting) != null) { 2975 return true; 2976 } 2977 } else if (getValidRoute(protecting, destination) != null) { 2978 return true; 2979 } 2980 return false; 2981 } 2982 2983 /** 2984 * Get a list of valid Routes to our destination block 2985 * 2986 * @param dstBlock target to find 2987 * @return routes between this and dstBlock 2988 */ 2989 List<Routes> getDestRoutes(Block dstBlock) { 2990 List<Routes> rtr = new ArrayList<>(); 2991 for (Routes route : routes) { 2992 if (route.getDestBlock() == dstBlock) { 2993 rtr.add(route); 2994 } 2995 } 2996 return rtr; 2997 } 2998 2999 /** 3000 * Get a list of valid Routes via our next block 3001 * 3002 * @param nxtBlock target block 3003 * @return list of routes to target block 3004 */ 3005 List<Routes> getNextRoutes(Block nxtBlock) { 3006 List<Routes> rtr = new ArrayList<>(); 3007 for (Routes route : routes) { 3008 if (route.getNextBlock() == nxtBlock) { 3009 rtr.add(route); 3010 } 3011 } 3012 return rtr; 3013 } 3014 3015 void updateRoutingInfo(Routes route) { 3016 if (route.getHopCount() >= 254) { 3017 return; 3018 } 3019 Block destBlock = route.getDestBlock(); 3020 3021 RoutingPacket update = new RoutingPacket(UPDATE, destBlock, getBestRouteByHop(destBlock).getHopCount() + 1, 3022 ((getBestRouteByMetric(destBlock).getMetric()) + metric), 3023 ((getBestRouteByMetric(destBlock).getMetric()) 3024 + block.getLengthMm()), -1, 3025 getNextPacketID()); 3026 firePropertyChange("routing", null, update); 3027 } 3028 3029 // This lot might need changing to only forward on the best route details. 3030 void updateRoutingInfo(LayoutBlock src, RoutingPacket update) { 3031 updateRouteLog.debug("From {} src: {}, block: {}, hopCount: {}, metric: {}, status: {}, packetID: {}", 3032 getDisplayName(), src.getDisplayName(), update.getBlock().getDisplayName(), 3033 update.getHopCount(), update.getMetric(), update.getBlockState(), update.getPacketId()); 3034 Block srcblk = src.getBlock(); 3035 Adjacencies adj = getAdjacency(srcblk); 3036 3037 if (adj == null) { 3038 updateRouteLog.debug("From {} packet is from a src that is not registered {}", 3039 getDisplayName(), srcblk.getDisplayName()); 3040 // If the packet is from a src that is not registered as a neighbour 3041 // Then we will simply reject it. 3042 return; 3043 } 3044 3045 if (updatePacketActedUpon(update.getPacketId())) { 3046 if (adj.updatePacketActedUpon(update.getPacketId())) { 3047 updateRouteLog.debug("Reject packet update as we have already acted up on it from this neighbour"); 3048 return; 3049 } 3050 } 3051 3052 updateRouteLog.debug("From {} an Update packet from neighbour {}", getDisplayName(), src.getDisplayName()); 3053 3054 Block updateBlock = update.getBlock(); 3055 3056 // Block srcblk = src.getBlock(); 3057 // Need to add in a check to make sure that we have a route registered from the source neighbour 3058 // for the block that they are referring too. 3059 if (updateBlock == this.getBlock()) { 3060 updateRouteLog.debug("Reject packet update as it is a route advertised by our selves"); 3061 return; 3062 } 3063 3064 Routes ro; 3065 boolean neighbour = false; 3066 if (updateBlock == srcblk) { 3067 // Very likely that this update is from a neighbour about its own status. 3068 ro = getValidRoute(this.getBlock(), updateBlock); 3069 neighbour = true; 3070 } else { 3071 ro = getValidRoute(srcblk, updateBlock); 3072 } 3073 3074 if (ro == null) { 3075 updateRouteLog.debug("From {} update is from a source that we do not have listed as a route to the destination", getDisplayName()); 3076 updateRouteLog.debug("From {} update packet is for a block that we do not have route registered for {}", getDisplayName(), updateBlock.getDisplayName()); 3077 // If the packet is for a dest that is not in the routing table 3078 // Then we will simply reject it. 3079 return; 3080 } 3081 /*This prevents us from entering into an update loop. 3082 We only add it to our list once it has passed through as being a valid 3083 packet, otherwise we may get the same packet id back, but from a valid source 3084 which would end up be rejected*/ 3085 3086 actedUponUpdates.add(update.getPacketId()); 3087 adj.addPacketReceivedFromNeighbour(update.getPacketId()); 3088 3089 int hopCount = update.getHopCount(); 3090 int packetmetric = update.getMetric(); 3091 int blockstate = update.getBlockState(); 3092 float length = update.getLength(); 3093 3094 // Need to add in a check for a block that is directly connected. 3095 if (hopCount != -1) { 3096 // Was increase hop count before setting it 3097 // int oldHop = ro.getHopCount(); 3098 if (ro.getHopCount() != hopCount) { 3099 updateRouteLog.debug("{} Hop counts to {} not the same so will change from {} to {}", getDisplayName(), ro.getDestBlock().getDisplayName(), ro.getHopCount(), hopCount); 3100 ro.setHopCount(hopCount); 3101 hopCount++; 3102 } else { 3103 // No point in forwarding on the update if the hopcount hasn't changed 3104 hopCount = -1; 3105 } 3106 } 3107 3108 // bad to use values as errors, but it's pre-existing code, and code wins 3109 if ((int) length != -1) { 3110 // Length is added at source 3111 float oldLength = ro.getLength(); 3112 if (!MathUtil.equals(oldLength, length)) { 3113 ro.setLength(length); 3114 boolean forwardUpdate = true; 3115 3116 if (ro != getBestRouteByLength(update.getBlock())) { 3117 forwardUpdate = false; 3118 } 3119 3120 updateRouteLog.debug("From {} updating length from {} to {}", getDisplayName(), oldLength, length); 3121 3122 if (neighbour) { 3123 length = srcblk.getLengthMm(); 3124 adj.setLength(length); 3125 3126 // ro.setLength(length); 3127 // Also if neighbour we need to update the cost of the routes via it to reflect the new metric 02/20/2011 3128 if (forwardUpdate) { 3129 List<Routes> neighbourRoute = getNextRoutes(srcblk); 3130 3131 // neighbourRoutes, contains all the routes that have been advertised by the neighbour 3132 // that will need to have their metric updated to reflect the change. 3133 for (Routes nRo : neighbourRoute) { 3134 // Need to remove old metric to the neigbour, then add the new one on 3135 float updateLength = nRo.getLength(); 3136 updateLength = (updateLength - oldLength) + length; 3137 3138 updateRouteLog.debug("From {} update metric for route {} from {} to {}", 3139 getDisplayName(), nRo.getDestBlock().getDisplayName(), nRo.getLength(), updateLength); 3140 nRo.setLength(updateLength); 3141 List<Block> messageRecipients = getThroughPathDestinationBySource(srcblk); 3142 RoutingPacket newUpdate = new RoutingPacket(UPDATE, nRo.getDestBlock(), -1, -1, updateLength + block.getLengthMm(), -1, getNextPacketID()); 3143 updateRoutesToNeighbours(messageRecipients, nRo, newUpdate); 3144 } 3145 } 3146 } else if (forwardUpdate) { 3147 // This can cause a loop, if the layout is in a loop, so we send out the same packetID. 3148 List<Block> messageRecipients = getThroughPathSourceByDestination(srcblk); 3149 RoutingPacket newUpdate = new RoutingPacket(UPDATE, updateBlock, -1, -1, 3150 length + block.getLengthMm(), -1, update.getPacketId()); 3151 updateRoutesToNeighbours(messageRecipients, ro, newUpdate); 3152 } 3153 length += metric; 3154 } else { 3155 length = -1; 3156 } 3157 } 3158 3159 if (packetmetric != -1) { 3160 // Metric is added at source 3161 // Keep a reference of the old metric. 3162 int oldmetric = ro.getMetric(); 3163 if (oldmetric != packetmetric) { 3164 ro.setMetric(packetmetric); 3165 3166 updateRouteLog.debug("From {} updating metric from {} to {}", getDisplayName(), oldmetric, packetmetric); 3167 boolean forwardUpdate = true; 3168 3169 if (ro != getBestRouteByMetric(update.getBlock())) { 3170 forwardUpdate = false; 3171 } 3172 3173 // if the metric update is for a neighbour then we will go directly to the neighbour for the value, 3174 // rather than trust what is in the message at this stage. 3175 if (neighbour) { 3176 packetmetric = src.getBlockMetric(); 3177 adj.setMetric(packetmetric); 3178 3179 if (forwardUpdate) { 3180 // ro.setMetric(packetmetric); 3181 // Also if neighbour we need to update the cost of the routes via it to 3182 // reflect the new metric 02/20/2011 3183 List<Routes> neighbourRoute = getNextRoutes(srcblk); 3184 3185 // neighbourRoutes, contains all the routes that have been advertised by the neighbour that 3186 // will need to have their metric updated to reflect the change. 3187 for (Routes nRo : neighbourRoute) { 3188 // Need to remove old metric to the neigbour, then add the new one on 3189 int updatemet = nRo.getMetric(); 3190 updatemet = (updatemet - oldmetric) + packetmetric; 3191 3192 updateRouteLog.debug("From {} update metric for route {} from {} to {}", getDisplayName(), nRo.getDestBlock().getDisplayName(), nRo.getMetric(), updatemet); 3193 nRo.setMetric(updatemet); 3194 List<Block> messageRecipients = getThroughPathDestinationBySource(srcblk); 3195 RoutingPacket newUpdate = new RoutingPacket(UPDATE, nRo.getDestBlock(), hopCount, updatemet + metric, -1, -1, getNextPacketID()); 3196 updateRoutesToNeighbours(messageRecipients, nRo, newUpdate); 3197 } 3198 } 3199 } else if (forwardUpdate) { 3200 // This can cause a loop, if the layout is in a loop, so we send out the same packetID. 3201 List<Block> messageRecipients = getThroughPathSourceByDestination(srcblk); 3202 RoutingPacket newUpdate = new RoutingPacket(UPDATE, updateBlock, hopCount, 3203 packetmetric + metric, -1, -1, update.getPacketId()); 3204 updateRoutesToNeighbours(messageRecipients, ro, newUpdate); 3205 } 3206 packetmetric += metric; 3207 // Think we need a list of routes that originate from this source neighbour 3208 } else { 3209 // No point in forwarding on the update if the metric hasn't changed 3210 packetmetric = -1; 3211 // Potentially when we do this we need to update all the routes that go via this block, not just this route. 3212 } 3213 } 3214 3215 if (blockstate != -1) { 3216 // We will update all the destination blocks with the new state, it 3217 // saves re-firing off new updates block status 3218 boolean stateUpdated = false; 3219 List<Routes> rtr = getDestRoutes(updateBlock); 3220 3221 for (Routes rt : rtr) { 3222 if (rt.getState() != blockstate) { 3223 stateUpdated = true; 3224 rt.stateChange(); 3225 } 3226 } 3227 3228 if (stateUpdated) { 3229 RoutingPacket newUpdate = new RoutingPacket(UPDATE, updateBlock, -1, -1, -1, blockstate, getNextPacketID()); 3230 firePropertyChange("routing", null, newUpdate); 3231 } 3232 } 3233 3234 // We need to expand on this so that any update to routing metric is propergated correctly 3235 if ((packetmetric != -1) || (hopCount != -1) || (length != -1)) { 3236 // We only want to send the update on to neighbours that we have advertised the route to. 3237 List<Block> messageRecipients = getThroughPathSourceByDestination(srcblk); 3238 RoutingPacket newUpdate = new RoutingPacket(UPDATE, updateBlock, hopCount, packetmetric, 3239 length, blockstate, update.getPacketId()); 3240 updateRoutesToNeighbours(messageRecipients, ro, newUpdate); 3241 } 3242 // Was just pass on hop count 3243 } 3244 3245 void updateRoutesToNeighbours(List<Block> messageRecipients, Routes ro, RoutingPacket update) { 3246 for (Block messageRecipient : messageRecipients) { 3247 Adjacencies adj = getAdjacency(messageRecipient); 3248 if (adj.advertiseRouteToNeighbour(ro)) { 3249 adj.addRouteAdvertisedToNeighbour(ro); 3250 LayoutBlock recipient = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(messageRecipient); 3251 if (recipient != null) { 3252 recipient.updateRoutingInfo(this, update); 3253 } 3254 } 3255 } 3256 } 3257 3258 Routes getBestRouteByMetric(Block dest) { 3259 // int bestHopCount = 255; 3260 int bestMetric = 965000; 3261 int bestIndex = -1; 3262 3263 List<Routes> destRoutes = getDestRoutes(dest); 3264 for (int i = 0; i < destRoutes.size(); i++) { 3265 if (destRoutes.get(i).getMetric() < bestMetric) { 3266 bestMetric = destRoutes.get(i).getMetric(); 3267 bestIndex = i; 3268 } 3269 } 3270 3271 if (bestIndex == -1) { 3272 return null; 3273 } 3274 return destRoutes.get(bestIndex); 3275 } 3276 3277 Routes getBestRouteByHop(Block dest) { 3278 int bestHopCount = 255; 3279 // int bestMetric = 965000; 3280 int bestIndex = -1; 3281 3282 List<Routes> destRoutes = getDestRoutes(dest); 3283 for (int i = 0; i < destRoutes.size(); i++) { 3284 if (destRoutes.get(i).getHopCount() < bestHopCount) { 3285 bestHopCount = destRoutes.get(i).getHopCount(); 3286 bestIndex = i; 3287 } 3288 } 3289 3290 if (bestIndex == -1) { 3291 return null; 3292 } 3293 return destRoutes.get(bestIndex); 3294 } 3295 3296 Routes getBestRouteByLength(Block dest) { 3297 // int bestHopCount = 255; 3298 // int bestMetric = 965000; 3299 // long bestLength = 999999999; 3300 int bestIndex = -1; 3301 List<Routes> destRoutes = getDestRoutes(dest); 3302 float bestLength = destRoutes.get(0).getLength(); 3303 3304 for (int i = 0; i < destRoutes.size(); i++) { 3305 if (destRoutes.get(i).getLength() < bestLength) { 3306 bestLength = destRoutes.get(i).getLength(); 3307 bestIndex = i; 3308 } 3309 } 3310 3311 if (bestIndex == -1) { 3312 return null; 3313 } 3314 return destRoutes.get(bestIndex); 3315 } 3316 3317 void addRouteToNeighbours(Routes ro) { 3318 addRouteLog.debug("From {} Add route to neighbour", getDisplayName()); 3319 Block nextHop = ro.getNextBlock(); 3320 List<LayoutBlock> validFromPath = new ArrayList<>(); 3321 3322 addRouteLog.debug("From {} new block {}", getDisplayName(), nextHop.getDisplayName()); 3323 3324 for (int i = 0; i < throughPaths.size(); i++) { 3325 LayoutBlock validBlock = null; 3326 3327 addRouteLog.debug("Through routes index {}", i); 3328 addRouteLog.debug("From {} A through routes {} {}", getDisplayName(), 3329 throughPaths.get(i).getSourceBlock().getDisplayName(), 3330 throughPaths.get(i).getDestinationBlock().getDisplayName()); 3331 3332 /*As the through paths include each possible path, ie 2 > 3 and 3 > 2 3333 as seperate entries then we only need to forward the new route to those 3334 source blocks that have a desination of the next hop*/ 3335 if (throughPaths.get(i).getDestinationBlock() == nextHop) { 3336 if (getAdjacency(throughPaths.get(i).getSourceBlock()).isMutual()) { 3337 validBlock = InstanceManager.getDefault( 3338 LayoutBlockManager.class). 3339 getLayoutBlock(throughPaths.get(i).getSourceBlock()); 3340 } 3341 } 3342 3343 // only need to add it the once. Not sure if the contains is required. 3344 if ((validBlock != null) && (!validFromPath.contains(validBlock))) { 3345 validFromPath.add(validBlock); 3346 } 3347 } 3348 3349 if ( addRouteLog.isDebugEnabled() ) { 3350 addRouteLog.debug("From {} ===== valid from size path {} ==== (addroutetoneigh)", this.getDisplayName(), validFromPath.size()); 3351 3352 validFromPath.forEach( valid -> addRouteLog.debug("fromPath: {}", valid.getDisplayName())); 3353 addRouteLog.debug("Next Hop {}", nextHop.getDisplayName()); 3354 } 3355 RoutingPacket update = new RoutingPacket(ADDITION, ro.getDestBlock(), ro.getHopCount() + 1, 3356 ro.getMetric() + metric, 3357 (ro.getLength() + getBlock().getLengthMm()), -1, getNextPacketID()); 3358 3359 for (LayoutBlock layoutBlock : validFromPath) { 3360 Adjacencies adj = getAdjacency(layoutBlock.getBlock()); 3361 if (adj.advertiseRouteToNeighbour(ro)) { 3362 // getBestRouteByHop(destBlock).getHopCount()+1, ((getBestRouteByMetric(destBlock).getMetric())+metric), 3363 //((getBestRouteByMetric(destBlock).getMetric())+block.getLengthMm()) 3364 addRouteLog.debug("From {} Sending update to {} As this has a better hop count or metric", 3365 getDisplayName(), layoutBlock.getDisplayName()); 3366 adj.addRouteAdvertisedToNeighbour(ro); 3367 layoutBlock.addRouteFromNeighbour(this, update); 3368 } 3369 } 3370 } 3371 3372 void addRouteFromNeighbour(LayoutBlock src, RoutingPacket update) { 3373 // log.info("From " + this.getDisplayName() + " packet to be added from neighbour " + src.getDisplayName()); 3374 addRouteLog.debug("From {} src: {}, block: {}, hopCount: {}, metric: {}, status: {}, packetID: {}", 3375 getDisplayName(), src.getDisplayName(), update.getBlock().getDisplayName(), 3376 update.getHopCount(), update.getMetric(), update.getBlockState(), update.getPacketId()); 3377 InstanceManager.getDefault(LayoutBlockManager.class).setLastRoutingChange(); 3378 Block destBlock = update.getBlock(); 3379 Block srcblk = src.getBlock(); 3380 3381 if (destBlock == this.getBlock()) { 3382 addRouteLog.debug("Reject packet update as it is to a route advertised by our selves"); 3383 return; 3384 } 3385 3386 Adjacencies adj = getAdjacency(srcblk); 3387 if (adj == null) { 3388 addRouteLog.debug("From {} packet is from a src that is not registered {}", 3389 getDisplayName(), srcblk.getDisplayName()); 3390 // If the packet is from a src that is not registered as a neighbour 3391 // Then we will simply reject it. 3392 return; 3393 } else if (adj.getPacketFlow() == TXONLY) { 3394 addRouteLog.debug("From {} packet is from a src {} that is registered as one that we should be transmitting to only", 3395 getDisplayName(), src.getDisplayName()); 3396 // we should only be transmitting routes to this neighbour not receiving them 3397 return; 3398 } 3399 int hopCount = update.getHopCount(); 3400 int updatemetric = update.getMetric(); 3401 float length = update.getLength(); 3402 3403 if (hopCount > 255) { 3404 addRouteLog.debug("From {} hop count exceeded {}", getDisplayName(), destBlock.getDisplayName()); 3405 return; 3406 } 3407 3408 for (Routes ro : routes) { 3409 if ((ro.getNextBlock() == srcblk) && (ro.getDestBlock() == destBlock)) { 3410 addRouteLog.debug("From {} Route to {} is already configured", 3411 getDisplayName(), destBlock.getDisplayName()); 3412 addRouteLog.debug("{} v {}", ro.getHopCount(), hopCount); 3413 addRouteLog.debug("{} v {}", ro.getMetric(), updatemetric); 3414 updateRoutingInfo(src, update); 3415 return; 3416 } 3417 } 3418 3419 addRouteLog.debug("From {} We should be adding route {}", getDisplayName(), destBlock.getDisplayName()); 3420 3421 // We need to propergate out the routes that we have added to our neighbour 3422 int direction = adj.getDirection(); 3423 Routes route = new Routes(destBlock, srcblk, hopCount, direction, updatemetric, length); 3424 routes.add(route); 3425 3426 // Need to propergate the route down to our neighbours 3427 addRouteToNeighbours(route); 3428 } 3429 3430 /* this should look after removal of a specific next hop from our neighbour*/ 3431 /** 3432 * Get the direction of travel to our neighbouring block. 3433 * 3434 * @param neigh neighbor block 3435 * @return direction to get to neighbor block 3436 */ 3437 public int getNeighbourDirection(LayoutBlock neigh) { 3438 if (neigh == null) { 3439 return Path.NONE; 3440 } 3441 Block neighbourBlock = neigh.getBlock(); 3442 return getNeighbourDirection(neighbourBlock); 3443 } 3444 3445 public int getNeighbourDirection(Block neighbourBlock) { 3446 for (Adjacencies neighbour : neighbours) { 3447 if (neighbour.getBlock() == neighbourBlock) { 3448 return neighbour.getDirection(); 3449 } 3450 } 3451 return Path.NONE; 3452 } 3453 3454 Adjacencies getAdjacency(Block blk) { 3455 for (Adjacencies neighbour : neighbours) { 3456 if (neighbour.getBlock() == blk) { 3457 return neighbour; 3458 } 3459 } 3460 return null; 3461 } 3462 3463 final static int ADDITION = 0x00; 3464 final static int UPDATE = 0x02; 3465 final static int REMOVAL = 0x04; 3466 3467 final static int RXTX = 0x00; 3468 final static int RXONLY = 0x02; 3469 final static int TXONLY = 0x04; 3470 final static int NONE = 0x08; 3471 int metric = 100; 3472 3473 private static class RoutingPacket { 3474 3475 int packetType; 3476 Block block; 3477 int hopCount = -1; 3478 int packetMetric = -1; 3479 int blockstate = -1; 3480 float length = -1; 3481 Integer packetRef = -1; 3482 3483 RoutingPacket(int packetType, Block blk, int hopCount, int packetMetric, float length, int blockstate, Integer packetRef) { 3484 this.packetType = packetType; 3485 this.block = blk; 3486 this.hopCount = hopCount; 3487 this.packetMetric = packetMetric; 3488 this.blockstate = blockstate; 3489 this.packetRef = packetRef; 3490 this.length = length; 3491 } 3492 3493 int getPacketType() { 3494 return packetType; 3495 } 3496 3497 Block getBlock() { 3498 return block; 3499 } 3500 3501 int getHopCount() { 3502 return hopCount; 3503 } 3504 3505 int getMetric() { 3506 return packetMetric; 3507 } 3508 3509 int getBlockState() { 3510 return blockstate; 3511 } 3512 3513 float getLength() { 3514 return length; 3515 } 3516 3517 Integer getPacketId() { 3518 return packetRef; 3519 } 3520 } 3521 3522 /** 3523 * Get the number of neighbor blocks attached to this block. 3524 * 3525 * @return count of neighbor 3526 */ 3527 public int getNumberOfNeighbours() { 3528 return neighbours.size(); 3529 } 3530 3531 /** 3532 * Get the neighboring block at index i. 3533 * 3534 * @param i index to neighbor 3535 * @return neighbor block 3536 */ 3537 public Block getNeighbourAtIndex(int i) { 3538 return neighbours.get(i).getBlock(); 3539 } 3540 3541 /** 3542 * Get the direction of travel to neighbouring block at index i. 3543 * 3544 * @param i index in neighbors 3545 * @return neighbor block 3546 */ 3547 public int getNeighbourDirection(int i) { 3548 return neighbours.get(i).getDirection(); 3549 } 3550 3551 /** 3552 * Get the metric/cost to neighbouring block at index i. 3553 * 3554 * @param i index in neighbors 3555 * @return metric of neighbor 3556 */ 3557 public int getNeighbourMetric(int i) { 3558 return neighbours.get(i).getMetric(); 3559 } 3560 3561 /** 3562 * Get the flow of traffic to and from neighbouring block at index i RXTX - 3563 * Means Traffic can flow both ways between the blocks RXONLY - Means we can 3564 * only receive traffic from our neighbour, we can not send traffic to it 3565 * TXONLY - Means we do not receive traffic from our neighbour, but can send 3566 * traffic to it. 3567 * 3568 * @param i index in neighbors 3569 * @return direction of traffic 3570 */ 3571 public String getNeighbourPacketFlowAsString(int i) { 3572 return decodePacketFlow(neighbours.get(i).getPacketFlow()); 3573 } 3574 3575 /** 3576 * Is our neighbouring block at index i a mutual neighbour, ie both blocks 3577 * have each other registered as neighbours and are exchanging information. 3578 * 3579 * @param i index of neighbor 3580 * @return true if both are mutual neighbors 3581 */ 3582 public boolean isNeighbourMutual(int i) { 3583 return neighbours.get(i).isMutual(); 3584 } 3585 3586 int getNeighbourIndex(Adjacencies adj) { 3587 for (int i = 0; i < neighbours.size(); i++) { 3588 if (neighbours.get(i) == adj) { 3589 return i; 3590 } 3591 } 3592 return -1; 3593 } 3594 3595 private class Adjacencies { 3596 3597 Block adjBlock; 3598 LayoutBlock adjLayoutBlock; 3599 int direction; 3600 int packetFlow = RXTX; 3601 boolean mutualAdjacency = false; 3602 3603 HashMap<Block, Routes> adjDestRoutes = new HashMap<>(); 3604 List<Integer> actedUponUpdates = new ArrayList<>(501); 3605 3606 Adjacencies(Block block, int dir, int packetFlow) { 3607 adjBlock = block; 3608 direction = dir; 3609 this.packetFlow = packetFlow; 3610 } 3611 3612 Block getBlock() { 3613 return adjBlock; 3614 } 3615 3616 LayoutBlock getLayoutBlock() { 3617 return adjLayoutBlock; 3618 } 3619 3620 int getDirection() { 3621 return direction; 3622 } 3623 3624 // If a set true on mutual, then we could go through the list of what to send out to neighbour 3625 void setMutual(boolean mut) { 3626 if (mut == mutualAdjacency) { // No change will exit 3627 return; 3628 } 3629 mutualAdjacency = mut; 3630 if (mutualAdjacency) { 3631 adjLayoutBlock = InstanceManager.getDefault( 3632 LayoutBlockManager.class).getLayoutBlock(adjBlock); 3633 } 3634 } 3635 3636 boolean isMutual() { 3637 return mutualAdjacency; 3638 } 3639 3640 int getPacketFlow() { 3641 return packetFlow; 3642 } 3643 3644 void setPacketFlow(int flow) { 3645 if (flow != packetFlow) { 3646 int oldFlow = packetFlow; 3647 packetFlow = flow; 3648 firePropertyChange("neighbourpacketflow", oldFlow, packetFlow); 3649 } 3650 } 3651 3652 // The metric could just be read directly from the neighbour as we have no 3653 // need to specifically keep a copy of it here this is here just to fire off the change 3654 void setMetric(int met) { 3655 firePropertyChange("neighbourmetric", null, getNeighbourIndex(this)); 3656 } 3657 3658 int getMetric() { 3659 if (adjLayoutBlock != null) { 3660 return adjLayoutBlock.getBlockMetric(); 3661 } 3662 adjLayoutBlock = InstanceManager.getDefault( 3663 LayoutBlockManager.class).getLayoutBlock(adjBlock); 3664 if (adjLayoutBlock != null) { 3665 return adjLayoutBlock.getBlockMetric(); 3666 } 3667 3668 if (log.isDebugEnabled()) { 3669 log.debug("Layout Block {} returned as null", adjBlock.getDisplayName()); 3670 } 3671 return -1; 3672 } 3673 3674 void setLength(float len) { 3675 firePropertyChange("neighbourlength", null, getNeighbourIndex(this)); 3676 } 3677 3678 float getLength() { 3679 if (adjLayoutBlock != null) { 3680 return adjLayoutBlock.getBlock().getLengthMm(); 3681 } 3682 adjLayoutBlock = InstanceManager.getDefault( 3683 LayoutBlockManager.class).getLayoutBlock(adjBlock); 3684 if (adjLayoutBlock != null) { 3685 return adjLayoutBlock.getBlock().getLengthMm(); 3686 } 3687 3688 if (log.isDebugEnabled()) { 3689 log.debug("Layout Block {} returned as null", adjBlock.getDisplayName()); 3690 } 3691 return -1; 3692 } 3693 3694 void removeRouteAdvertisedToNeighbour(Routes removeRoute) { 3695 Block dest = removeRoute.getDestBlock(); 3696 3697 if (adjDestRoutes.get(dest) == removeRoute) { 3698 adjDestRoutes.remove(dest); 3699 } 3700 } 3701 3702 void removeRouteAdvertisedToNeighbour(Block block) { 3703 adjDestRoutes.remove(block); 3704 } 3705 3706 void addRouteAdvertisedToNeighbour(Routes addedRoute) { 3707 adjDestRoutes.put(addedRoute.getDestBlock(), addedRoute); 3708 } 3709 3710 boolean advertiseRouteToNeighbour(Routes routeToAdd) { 3711 if (!isMutual()) { 3712 log.debug("In block {}: Neighbour is not mutual so will not advertise it (Routes {})", 3713 getDisplayName(), routeToAdd); 3714 return false; 3715 } 3716 3717 // Just wonder if this should forward on the new packet to the neighbour? 3718 Block dest = routeToAdd.getDestBlock(); 3719 if (!adjDestRoutes.containsKey(dest)) { 3720 log.debug("In block {}: We are not currently advertising a route to the destination to neighbour: {}", 3721 getDisplayName(), dest.getDisplayName()); 3722 return true; 3723 } 3724 3725 if (routeToAdd.getHopCount() > 255) { 3726 log.debug("Hop count is gereater than 255 we will therefore do nothing with this route"); 3727 return false; 3728 } 3729 Routes existingRoute = adjDestRoutes.get(dest); 3730 if (existingRoute.getMetric() > routeToAdd.getMetric()) { 3731 return true; 3732 } 3733 if (existingRoute.getHopCount() > routeToAdd.getHopCount()) { 3734 return true; 3735 } 3736 3737 if (existingRoute == routeToAdd) { 3738 // We return true as the metric might have changed 3739 return false; 3740 } 3741 return false; 3742 } 3743 3744 boolean updatePacketActedUpon(Integer packetID) { 3745 return actedUponUpdates.contains(packetID); 3746 } 3747 3748 void addPacketReceivedFromNeighbour(Integer packetID) { 3749 actedUponUpdates.add(packetID); 3750 if (actedUponUpdates.size() > 500) { 3751 actedUponUpdates.subList(0, 250).clear(); 3752 } 3753 } 3754 3755 void dispose() { 3756 adjBlock = null; 3757 adjLayoutBlock = null; 3758 mutualAdjacency = false; 3759 adjDestRoutes = null; 3760 actedUponUpdates = null; 3761 } 3762 } 3763 3764 /** 3765 * Get the number of routes that the block has registered. 3766 * 3767 * @return count of routes 3768 */ 3769 public int getNumberOfRoutes() { 3770 return routes.size(); 3771 } 3772 3773 /** 3774 * Get the direction of route i. 3775 * 3776 * @param i index in routes 3777 * @return direction 3778 */ 3779 public int getRouteDirectionAtIndex(int i) { 3780 return routes.get(i).getDirection(); 3781 } 3782 3783 /** 3784 * Get the destination block at route i 3785 * 3786 * @param i index in routes 3787 * @return dest block from route 3788 */ 3789 public Block getRouteDestBlockAtIndex(int i) { 3790 return routes.get(i).getDestBlock(); 3791 } 3792 3793 /** 3794 * Get the next block at route i 3795 * 3796 * @param i index in routes 3797 * @return next block from route 3798 */ 3799 public Block getRouteNextBlockAtIndex(int i) { 3800 return routes.get(i).getNextBlock(); 3801 } 3802 3803 /** 3804 * Get the hop count of route i.<br> 3805 * The Hop count is the number of other blocks that we traverse to get to 3806 * the destination 3807 * 3808 * @param i index in routes 3809 * @return hop count 3810 */ 3811 public int getRouteHopCountAtIndex(int i) { 3812 return routes.get(i).getHopCount(); 3813 } 3814 3815 /** 3816 * Get the length of route i.<br> 3817 * The length is the combined length of all the blocks that we traverse to 3818 * get to the destination 3819 * 3820 * @param i index in routes 3821 * @return length of block in route 3822 */ 3823 public float getRouteLengthAtIndex(int i) { 3824 return routes.get(i).getLength(); 3825 } 3826 3827 /** 3828 * Get the metric/cost at route i 3829 * 3830 * @param i index in routes 3831 * @return metric 3832 */ 3833 public int getRouteMetric(int i) { 3834 return routes.get(i).getMetric(); 3835 } 3836 3837 /** 3838 * Get the state (Occupied, unoccupied) of the destination layout block at 3839 * index i 3840 * 3841 * @param i index in routes 3842 * @return state of block 3843 */ 3844 public int getRouteState(int i) { 3845 return routes.get(i).getState(); 3846 } 3847 3848 /** 3849 * Is the route to the destination potentially valid from our block. 3850 * 3851 * @param i index in route 3852 * @return true if route is valid 3853 */ 3854 // TODO: Java standard pattern for boolean getters is "isRouteValid()" 3855 public boolean getRouteValid(int i) { 3856 return routes.get(i).isRouteCurrentlyValid(); 3857 } 3858 3859 /** 3860 * Get the state of the destination layout block at index i as a string. 3861 * 3862 * @param i index in routes 3863 * @return dest status 3864 */ 3865 public String getRouteStateAsString(int i) { 3866 int state = routes.get(i).getState(); 3867 switch (state) { 3868 case OCCUPIED: { 3869 return Bundle.getMessage("TrackOccupied"); // i18n using NamedBeanBundle.properties TODO remove duplicate keys 3870 } 3871 3872 case RESERVED: { 3873 return Bundle.getMessage("StateReserved"); // "Reserved" 3874 } 3875 3876 case EMPTY: { 3877 return Bundle.getMessage("StateFree"); // "Free" 3878 } 3879 3880 default: { 3881 return Bundle.getMessage("BeanStateUnknown"); // "Unknown" 3882 } 3883 } 3884 } 3885 3886 int getRouteIndex(Routes r) { 3887 for (int i = 0; i < routes.size(); i++) { 3888 if (routes.get(i) == r) { 3889 return i; 3890 } 3891 } 3892 return -1; 3893 } 3894 3895 /** 3896 * Get the number of layout blocks to our destintation block going from the 3897 * next directly connected block. If the destination block and nextblock are 3898 * the same and the block is also registered as a neighbour then 1 is 3899 * returned. If no valid route to the destination block can be found via the 3900 * next block then -1 is returned. If more than one route exists to the 3901 * destination then the route with the lowest count is returned. 3902 * 3903 * @param destination final block 3904 * @param nextBlock adjcent block 3905 * @return hop count to final, -1 if not available 3906 */ 3907 public int getBlockHopCount(Block destination, Block nextBlock) { 3908 if ((destination == nextBlock) && (isValidNeighbour(nextBlock))) { 3909 return 1; 3910 } 3911 3912 for (Routes route : routes) { 3913 if (route.getDestBlock() == destination) { 3914 if (route.getNextBlock() == nextBlock) { 3915 return route.getHopCount(); 3916 } 3917 } 3918 } 3919 return -1; 3920 } 3921 3922 /** 3923 * Get the metric to our desintation block going from the next directly 3924 * connected block. If the destination block and nextblock are the same and 3925 * the block is also registered as a neighbour then 1 is returned. If no 3926 * valid route to the destination block can be found via the next block then 3927 * -1 is returned. If more than one route exists to the destination then the 3928 * route with the lowest count is returned. 3929 * 3930 * @param destination final block 3931 * @param nextBlock adjcent block 3932 * @return metric to final block, -1 if not available 3933 */ 3934 public int getBlockMetric(Block destination, Block nextBlock) { 3935 if ((destination == nextBlock) && (isValidNeighbour(nextBlock))) { 3936 return 1; 3937 } 3938 3939 for (Routes route : routes) { 3940 if (route.getDestBlock() == destination) { 3941 if (route.getNextBlock() == nextBlock) { 3942 return route.getMetric(); 3943 } 3944 } 3945 } 3946 return -1; 3947 } 3948 3949 /** 3950 * Get the distance to our desintation block going from the next directly 3951 * connected block. If the destination block and nextblock are the same and 3952 * the block is also registered as a neighbour then 1 is returned. If no 3953 * valid route to the destination block can be found via the next block then 3954 * -1 is returned. If more than one route exists to the destination then the 3955 * route with the lowest count is returned. 3956 * 3957 * @param destination final block 3958 * @param nextBlock adjcent block 3959 * @return lenght to final, -1 if not viable 3960 */ 3961 public float getBlockLength(Block destination, Block nextBlock) { 3962 if ((destination == nextBlock) && (isValidNeighbour(nextBlock))) { 3963 return 1; 3964 } 3965 3966 for (Routes route : routes) { 3967 if (route.getDestBlock() == destination) { 3968 if (route.getNextBlock() == nextBlock) { 3969 return route.getLength(); 3970 } 3971 } 3972 } 3973 return -1; 3974 } 3975 3976 // TODO This needs a propertychange listener adding 3977 private class Routes implements PropertyChangeListener { 3978 3979 int direction; 3980 Block destBlock; 3981 Block nextBlock; 3982 int hopCount; 3983 int routeMetric; 3984 float length; 3985 3986 // int state =-1; 3987 int miscflags = 0x00; 3988 boolean validCurrentRoute = false; 3989 3990 public Routes(Block dstBlock, Block nxtBlock, int hop, int dir, int met, float len) { 3991 destBlock = dstBlock; 3992 nextBlock = nxtBlock; 3993 hopCount = hop; 3994 direction = dir; 3995 routeMetric = met; 3996 length = len; 3997 init(); 3998 } 3999 4000 final void init() { 4001 validCurrentRoute = checkIsRouteOnValidThroughPath(this); 4002 firePropertyChange("length", null, null); 4003 destBlock.addPropertyChangeListener(this); 4004 } 4005 4006 @Override 4007 public String toString() { 4008 return "Routes(dst:" + destBlock + ", nxt:" + nextBlock 4009 + ", hop:" + hopCount + ", dir:" + direction 4010 + ", met:" + routeMetric + ", len: " + length + ")"; 4011 } 4012 4013 @Override 4014 public void propertyChange(PropertyChangeEvent e) { 4015 if (e.getPropertyName().equals("state")) { 4016 stateChange(); 4017 } 4018 } 4019 4020 public Block getDestBlock() { 4021 return destBlock; 4022 } 4023 4024 public Block getNextBlock() { 4025 return nextBlock; 4026 } 4027 4028 public int getHopCount() { 4029 return hopCount; 4030 } 4031 4032 public int getDirection() { 4033 return direction; 4034 } 4035 4036 public int getMetric() { 4037 return routeMetric; 4038 } 4039 4040 public float getLength() { 4041 return length; 4042 } 4043 4044 public void setMetric(int met) { 4045 if (met == routeMetric) { 4046 return; 4047 } 4048 routeMetric = met; 4049 firePropertyChange("metric", null, getRouteIndex(this)); 4050 } 4051 4052 public void setHopCount(int hop) { 4053 if (hopCount == hop) { 4054 return; 4055 } 4056 hopCount = hop; 4057 firePropertyChange("hop", null, getRouteIndex(this)); 4058 } 4059 4060 public void setLength(float len) { 4061 if (len == length) { 4062 return; 4063 } 4064 length = len; 4065 firePropertyChange("length", null, getRouteIndex(this)); 4066 } 4067 4068 // This state change is only here for the routing table view 4069 void stateChange() { 4070 firePropertyChange("state", null, getRouteIndex(this)); 4071 } 4072 4073 int getState() { 4074 LayoutBlock destLBlock = InstanceManager.getDefault( 4075 LayoutBlockManager.class).getLayoutBlock(destBlock); 4076 if (destLBlock != null) { 4077 return destLBlock.getBlockStatus(); 4078 } 4079 4080 log.debug("Layout Block {} returned as null", destBlock.getDisplayName()); 4081 return -1; 4082 } 4083 4084 void setValidCurrentRoute(boolean boo) { 4085 if (validCurrentRoute == boo) { 4086 return; 4087 } 4088 validCurrentRoute = boo; 4089 firePropertyChange("valid", null, getRouteIndex(this)); 4090 } 4091 4092 boolean isRouteCurrentlyValid() { 4093 return validCurrentRoute; 4094 } 4095 4096 // Misc flags is not used in general routing, but is used for determining route removals 4097 void setMiscFlags(int f) { 4098 miscflags = f; 4099 } 4100 4101 int getMiscFlags() { 4102 return miscflags; 4103 } 4104 } 4105 4106 /** 4107 * Get the number of valid through paths on this block. 4108 * 4109 * @return count of paths through this block 4110 */ 4111 public int getNumberOfThroughPaths() { 4112 return throughPaths.size(); 4113 } 4114 4115 /** 4116 * Get the source block at index i 4117 * 4118 * @param i index in throughPaths 4119 * @return source block 4120 */ 4121 public Block getThroughPathSource(int i) { 4122 return throughPaths.get(i).getSourceBlock(); 4123 } 4124 4125 /** 4126 * Get the destination block at index i 4127 * 4128 * @param i index in throughPaths 4129 * @return final block 4130 */ 4131 public Block getThroughPathDestination(int i) { 4132 return throughPaths.get(i).getDestinationBlock(); 4133 } 4134 4135 /** 4136 * Is the through path at index i active? 4137 * 4138 * @param i index in path 4139 * @return active or not 4140 */ 4141 public Boolean isThroughPathActive(int i) { 4142 return throughPaths.get(i).isPathActive(); 4143 } 4144 4145 private class ThroughPaths implements PropertyChangeListener { 4146 4147 Block sourceBlock; 4148 Block destinationBlock; 4149 Path sourcePath; 4150 Path destinationPath; 4151 4152 boolean pathActive = false; 4153 4154 HashMap<Turnout, Integer> _turnouts = new HashMap<>(); 4155 4156 ThroughPaths(Block srcBlock, Path srcPath, Block destBlock, Path dstPath) { 4157 sourceBlock = srcBlock; 4158 destinationBlock = destBlock; 4159 sourcePath = srcPath; 4160 destinationPath = dstPath; 4161 } 4162 4163 Block getSourceBlock() { 4164 return sourceBlock; 4165 } 4166 4167 Block getDestinationBlock() { 4168 return destinationBlock; 4169 } 4170 4171 Path getSourcePath() { 4172 return sourcePath; 4173 } 4174 4175 Path getDestinationPath() { 4176 return destinationPath; 4177 } 4178 4179 boolean isPathActive() { 4180 return pathActive; 4181 } 4182 4183 void setTurnoutList(List<LayoutTrackExpectedState<LayoutTurnout>> turnouts) { 4184 if (!_turnouts.isEmpty()) { 4185 Set<Turnout> en = _turnouts.keySet(); 4186 en.forEach((listTurnout) -> listTurnout.removePropertyChangeListener(this)); 4187 } 4188 4189 // If we have no turnouts in this path, then this path is always active 4190 if (turnouts.isEmpty()) { 4191 pathActive = true; 4192 setRoutesValid(sourceBlock, true); 4193 setRoutesValid(destinationBlock, true); 4194 return; 4195 } 4196 _turnouts = new HashMap<>(turnouts.size()); 4197 for (LayoutTrackExpectedState<LayoutTurnout> turnout : turnouts) { 4198 if (turnout.getObject() instanceof LayoutSlip) { 4199 int slipState = turnout.getExpectedState(); 4200 LayoutSlip ls = (LayoutSlip) turnout.getObject(); 4201 int taState = ls.getTurnoutState(slipState); 4202 _turnouts.put(ls.getTurnout(), taState); 4203 ls.getTurnout().addPropertyChangeListener(this, ls.getTurnoutName(), "Layout Block Routing"); 4204 4205 int tbState = ls.getTurnoutBState(slipState); 4206 _turnouts.put(ls.getTurnoutB(), tbState); 4207 ls.getTurnoutB().addPropertyChangeListener(this, ls.getTurnoutBName(), "Layout Block Routing"); 4208 } else { 4209 LayoutTurnout lt = turnout.getObject(); 4210 if (lt.getTurnout() != null) { 4211 _turnouts.put(lt.getTurnout(), turnout.getExpectedState()); 4212 lt.getTurnout().addPropertyChangeListener(this, lt.getTurnoutName(), "Layout Block Routing"); 4213 } else { 4214 log.error("{} has no physical turnout allocated, block = {}", lt, block.getDisplayName()); 4215 } 4216 } 4217 } 4218 } 4219 4220 @Override 4221 public void propertyChange(PropertyChangeEvent e) { 4222 if (e.getPropertyName().equals("KnownState")) { 4223 Turnout srcTurnout = (Turnout) e.getSource(); 4224 int newVal = (Integer) e.getNewValue(); 4225 int values = _turnouts.get(srcTurnout); 4226 boolean allset = false; 4227 pathActive = false; 4228 4229 if (newVal == values) { 4230 allset = true; 4231 4232 if (_turnouts.size() > 1) { 4233 for (Map.Entry<Turnout, Integer> entry : _turnouts.entrySet()) { 4234 if (srcTurnout != entry.getKey()) { 4235 int state = entry.getKey().getState(); 4236 if (state != entry.getValue()) { 4237 allset = false; 4238 break; 4239 } 4240 } 4241 } 4242 } 4243 } 4244 updateActiveThroughPaths(this, allset); 4245 pathActive = allset; 4246 } 4247 } 4248 } 4249 4250 @Nonnull 4251 List<Block> getThroughPathSourceByDestination(Block dest) { 4252 List<Block> a = new ArrayList<>(); 4253 4254 for (ThroughPaths throughPath : throughPaths) { 4255 if (throughPath.getDestinationBlock() == dest) { 4256 a.add(throughPath.getSourceBlock()); 4257 } 4258 } 4259 return a; 4260 } 4261 4262 @Nonnull 4263 List<Block> getThroughPathDestinationBySource(Block source) { 4264 List<Block> a = new ArrayList<>(); 4265 4266 for (ThroughPaths throughPath : throughPaths) { 4267 if (throughPath.getSourceBlock() == source) { 4268 a.add(throughPath.getDestinationBlock()); 4269 } 4270 } 4271 return a; 4272 } 4273 4274 /** 4275 * When a route is created, check to see if the through path that this route 4276 * relates to is active. 4277 * @param r The route to check 4278 * @return true if that route is active 4279 */ 4280 boolean checkIsRouteOnValidThroughPath(Routes r) { 4281 for (ThroughPaths t : throughPaths) { 4282 if (t.isPathActive()) { 4283 if (t.getDestinationBlock() == r.getNextBlock()) { 4284 return true; 4285 } 4286 if (t.getSourceBlock() == r.getNextBlock()) { 4287 return true; 4288 } 4289 } 4290 } 4291 return false; 4292 } 4293 4294 /** 4295 * Go through all the routes and refresh the valid flag. 4296 */ 4297 public void refreshValidRoutes() { 4298 for (int i = 0; i < throughPaths.size(); i++) { 4299 ThroughPaths t = throughPaths.get(i); 4300 setRoutesValid(t.getDestinationBlock(), t.isPathActive()); 4301 setRoutesValid(t.getSourceBlock(), t.isPathActive()); 4302 firePropertyChange("path", null, i); 4303 } 4304 } 4305 4306 // We keep a track of what is paths are active, only so that we can easily mark 4307 // which routes are also potentially valid 4308 List<ThroughPaths> activePaths; 4309 4310 void updateActiveThroughPaths(ThroughPaths tp, boolean active) { 4311 updateRouteLog.debug("We have been notified that a through path has changed state"); 4312 4313 if (activePaths == null) { 4314 activePaths = new ArrayList<>(); 4315 } 4316 4317 if (active) { 4318 activePaths.add(tp); 4319 setRoutesValid(tp.getSourceBlock(), active); 4320 setRoutesValid(tp.getDestinationBlock(), active); 4321 } else { 4322 // We need to check if either our source or des is in use by another path. 4323 activePaths.remove(tp); 4324 boolean SourceInUse = false; 4325 boolean DestinationInUse = false; 4326 4327 for (ThroughPaths activePath : activePaths) { 4328 Block testSour = activePath.getSourceBlock(); 4329 Block testDest = activePath.getDestinationBlock(); 4330 if ((testSour == tp.getSourceBlock()) || (testDest == tp.getSourceBlock())) { 4331 SourceInUse = true; 4332 } 4333 if ((testSour == tp.getDestinationBlock()) || (testDest == tp.getDestinationBlock())) { 4334 DestinationInUse = true; 4335 } 4336 } 4337 4338 if (!SourceInUse) { 4339 setRoutesValid(tp.getSourceBlock(), active); 4340 } 4341 4342 if (!DestinationInUse) { 4343 setRoutesValid(tp.getDestinationBlock(), active); 4344 } 4345 } 4346 4347 for (int i = 0; i < throughPaths.size(); i++) { 4348 // This is processed simply for the throughpath table. 4349 if (tp == throughPaths.get(i)) { 4350 firePropertyChange("path", null, i); 4351 } 4352 } 4353 } 4354 4355 /** 4356 * Set the valid flag for routes that are on a valid through path. 4357 * @param nxtHopActive the start of the route 4358 * @param state the state to set into the valid flag 4359 */ 4360 void setRoutesValid(Block nxtHopActive, boolean state) { 4361 List<Routes> rtr = getRouteByNeighbour(nxtHopActive); 4362 rtr.forEach((rt) -> rt.setValidCurrentRoute(state)); 4363 } 4364 4365 @Override 4366 public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { 4367 if ("CanDelete".equals(evt.getPropertyName())) { // NOI18N 4368 if (evt.getOldValue() instanceof Sensor) { 4369 if (evt.getOldValue().equals(getOccupancySensor())) { 4370 throw new PropertyVetoException(getDisplayName(), evt); 4371 } 4372 } 4373 4374 if (evt.getOldValue() instanceof Memory) { 4375 if (evt.getOldValue().equals(getMemory())) { 4376 throw new PropertyVetoException(getDisplayName(), evt); 4377 } 4378 } 4379 } else if ("DoDelete".equals(evt.getPropertyName())) { // NOI18N 4380 // Do nothing at this stage 4381 if (evt.getOldValue() instanceof Sensor) { 4382 if (evt.getOldValue().equals(getOccupancySensor())) { 4383 setOccupancySensorName(null); 4384 } 4385 } 4386 4387 if (evt.getOldValue() instanceof Memory) { 4388 if (evt.getOldValue().equals(getMemory())) { 4389 setMemoryName(null); 4390 } 4391 } 4392 } 4393 } 4394 4395 @Override 4396 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 4397 List<NamedBeanUsageReport> report = new ArrayList<>(); 4398 if (bean != null) { 4399 if (bean.equals(getBlock())) { 4400 report.add(new NamedBeanUsageReport("LayoutBlockBlock")); // NOI18N 4401 } 4402 if (bean.equals(getMemory())) { 4403 report.add(new NamedBeanUsageReport("LayoutBlockMemory")); // NOI18N 4404 } 4405 if (bean.equals(getOccupancySensor())) { 4406 report.add(new NamedBeanUsageReport("LayoutBlockSensor")); // NOI18N 4407 } 4408 for (int i = 0; i < getNumberOfNeighbours(); i++) { 4409 if (bean.equals(getNeighbourAtIndex(i))) { 4410 report.add(new NamedBeanUsageReport("LayoutBlockNeighbor", "Neighbor")); // NOI18N 4411 } 4412 } 4413 } 4414 return report; 4415 } 4416 4417 @Override 4418 public String getBeanType() { 4419 return Bundle.getMessage("BeanNameLayoutBlock"); 4420 } 4421 4422 private static final Logger log = LoggerFactory.getLogger(LayoutBlock.class); 4423 private static final Logger searchRouteLog = LoggerFactory.getLogger(LayoutBlock.class.getName()+".SearchRouteLogging"); 4424 private static final Logger updateRouteLog = LoggerFactory.getLogger(LayoutBlock.class.getName()+".UpdateRouteLogging"); 4425 private static final Logger addRouteLog = LoggerFactory.getLogger(LayoutBlock.class.getName()+".AddRouteLogging"); 4426 private static final Logger deleteRouteLog = LoggerFactory.getLogger(LayoutBlock.class.getName()+".DeleteRouteLogging"); 4427 4428}