001package jmri.implementation; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.beans.*; 006import java.util.ArrayList; 007import java.util.List; 008 009import javax.annotation.CheckForNull; 010import javax.annotation.Nonnull; 011 012import jmri.*; 013 014import jmri.jmrit.display.EditorManager; 015import jmri.jmrit.display.layoutEditor.ConnectivityUtil; // normally these would be rolloed 016import jmri.jmrit.display.layoutEditor.HitPointType; // up into jmri.jmrit.display.layoutEditor.* 017import jmri.jmrit.display.layoutEditor.LayoutBlock; // but during the LE migration it's 018import jmri.jmrit.display.layoutEditor.LayoutBlockManager; // useful to be able to see 019import jmri.jmrit.display.layoutEditor.LayoutEditor; // what specific classe are used. 020import jmri.jmrit.display.layoutEditor.LayoutSlip; 021import jmri.jmrit.display.layoutEditor.LayoutTurnout; 022import jmri.jmrit.display.layoutEditor.LevelXing; 023import jmri.jmrit.display.layoutEditor.PositionablePoint; 024import jmri.jmrit.display.layoutEditor.TrackNode; 025import jmri.jmrit.display.layoutEditor.TrackSegment; 026 027import jmri.util.NonNullArrayList; 028 029/** 030 * Sections represent a group of one or more connected Blocks that may be 031 * allocated to a train traveling in a given direction. 032 * <p> 033 * A Block may be in multiple Sections. All Blocks contained in a given section 034 * must be unique. Blocks are kept in order--the first block is connected to the 035 * second, the second is connected to the third, etc. 036 * <p> 037 * A Block in a Section must be connected to the Block before it (if there is 038 * one) and to the Block after it (if there is one), but may not be connected to 039 * any other Block in the Section. This restriction is enforced when a Section 040 * is created, and checked when a Section is loaded from disk. 041 * <p> 042 * A Section has a "direction" defined by the sequence in which Blocks are added 043 * to the Section. A train may run through a Section in either the forward 044 * direction (from first block to last block) or reverse direction (from last 045 * block to first block). 046 * <p> 047 * A Section has one or more EntryPoints. Each EntryPoint is a Path of one of 048 * the Blocks in the Section that defines a connection to a Block outside of the 049 * Section. EntryPoints are grouped into two lists: "forwardEntryPoints" - entry 050 * through which will result in a train traveling in the "forward" direction 051 * "reverseEntryPoints" - entry through which will result in a train traveling 052 * in the "reverse" direction Note that "forwardEntryPoints" are also reverse 053 * exit points, and vice versa. 054 * <p> 055 * A Section has one of the following states" FREE - available for allocation by 056 * a dispatcher FORWARD - allocated for travel in the forward direction REVERSE 057 * - allocated for travel in the reverse direction 058 * <p> 059 * A Section has an occupancy. A Section is OCCUPIED if any of its Blocks is 060 * OCCUPIED. A Section is UNOCCUPIED if all of its Blocks are UNOCCUPIED 061 * <p> 062 * A Section of may be allocated to only one train at a time, even if the trains 063 * are travelling in the same direction. If a Section has sufficient space for 064 * multiple trains travelling in the same direction it should be broken up into 065 * multiple Sections so the trains can follow each other through the original 066 * Section. 067 * <p> 068 * A Section may not contain any reverse loops. The track that is reversed in a 069 * reverse loop must be in a separate Section. 070 * <p> 071 * Each Section optionally carries two direction sensors, one for the forward 072 * direction and one for the reverse direction. These sensors force signals for 073 * travel in their respective directions to "RED" when they are active. When the 074 * Section is free, both the sensors are Active. These internal sensors follow 075 * the state of the Section, permitting signals to function normally in the 076 * direction of allocation. 077 * <p> 078 * Each Section optionally carries two stopping sensors, one for the forward 079 * direction and one for the reverse direction. These sensors change to active 080 * when a train traversing the Section triggers its sensing device. Stopping 081 * sensors are physical layout sensors, and may be either point sensors or 082 * occupancy sensors for short blocks at the end of the Section. A stopping 083 * sensor is used during automatic running to stop a train that has reached the 084 * end of its allocated Section. This is needed, for example, to allow a train 085 * to enter a passing siding and clear the track behind it. When not running 086 * automatically, these sensors may be used to light panel lights to notify the 087 * dispatcher that the train has reached the end of the Section. 088 * <p> 089 * This Section implementation provides for delayed initialization of blocks and 090 * direction sensors to be independent of order of items in panel files. 091 * 092 * @author Dave Duchamp Copyright (C) 2008,2010 093 */ 094public class DefaultSection extends AbstractNamedBean implements Section { 095 096 private static final NamedBean.DisplayOptions USERSYS = NamedBean.DisplayOptions.USERNAME_SYSTEMNAME; 097 098 public DefaultSection(String systemName, String userName) { 099 super(systemName, userName); 100 } 101 102 public DefaultSection(String systemName) { 103 super(systemName); 104 } 105 106 /** 107 * Persistent instance variables (saved between runs) 108 */ 109 private String mForwardBlockingSensorName = ""; 110 private String mReverseBlockingSensorName = ""; 111 private String mForwardStoppingSensorName = ""; 112 private String mReverseStoppingSensorName = ""; 113 private final List<Block> mBlockEntries = new NonNullArrayList<>(); 114 private final List<EntryPoint> mForwardEntryPoints = new NonNullArrayList<>(); 115 private final List<EntryPoint> mReverseEntryPoints = new NonNullArrayList<>(); 116 117 /** 118 * Operational instance variables (not saved between runs). 119 */ 120 private int mState = FREE; 121 private int mOccupancy = UNOCCUPIED; 122 private boolean mOccupancyInitialized = false; 123 private Block mFirstBlock = null; 124 private Block mLastBlock = null; 125 126 private NamedBeanHandle<Sensor> mForwardBlockingNamedSensor = null; 127 private NamedBeanHandle<Sensor> mReverseBlockingNamedSensor = null; 128 private NamedBeanHandle<Sensor> mForwardStoppingNamedSensor = null; 129 private NamedBeanHandle<Sensor> mReverseStoppingNamedSensor = null; 130 131 private final List<PropertyChangeListener> mBlockListeners = new ArrayList<>(); 132 protected NamedBeanHandleManager nbhm = InstanceManager.getDefault(NamedBeanHandleManager.class); 133 134 /** 135 * Get the state of the Section. 136 * UNKNOWN, FORWARD, REVERSE, FREE 137 * 138 * @return the section state 139 */ 140 @Override 141 public int getState() { 142 return mState; 143 } 144 145 /** 146 * Set the state of the Section. 147 * FREE, FORWARD or REVERSE. 148 * <br> 149 * UNKNOWN state not accepted here. 150 * @param state the state to set 151 */ 152 @Override 153 public void setState(int state) { 154 if ((state == Section.FREE) || (state == Section.FORWARD) || (state == Section.REVERSE)) { 155 int old = mState; 156 mState = state; 157 firePropertyChange(PROPERTY_STATE, old, mState); 158 // update the forward/reverse blocking sensors as needed 159 Sensor fbSensor = getForwardBlockingSensor(); 160 Sensor rbSensor = getReverseBlockingSensor(); 161 162 switch (state) { 163 case FORWARD: 164 try { 165 if ((fbSensor != null) && (fbSensor.getState() != Sensor.INACTIVE)) { 166 fbSensor.setState(Sensor.INACTIVE); 167 } 168 if ((rbSensor != null) && (rbSensor.getState() != Sensor.ACTIVE)) { 169 rbSensor.setKnownState(Sensor.ACTIVE); 170 } 171 } catch (JmriException reason) { 172 log.error("Exception when setting Sensors for Section {}", getDisplayName(USERSYS)); 173 } 174 break; 175 case REVERSE: 176 try { 177 if ((rbSensor != null) && (rbSensor.getState() != Sensor.INACTIVE)) { 178 rbSensor.setKnownState(Sensor.INACTIVE); 179 } 180 if ((fbSensor != null) && (fbSensor.getState() != Sensor.ACTIVE)) { 181 fbSensor.setKnownState(Sensor.ACTIVE); 182 } 183 } catch (JmriException reason) { 184 log.error("Exception when setting Sensors for Section {}", getDisplayName(USERSYS)); 185 } 186 break; 187 case FREE: 188 try { 189 if ((fbSensor != null) && (fbSensor.getState() != Sensor.ACTIVE)) { 190 fbSensor.setKnownState(Sensor.ACTIVE); 191 } 192 if ((rbSensor != null) && (rbSensor.getState() != Sensor.ACTIVE)) { 193 rbSensor.setKnownState(Sensor.ACTIVE); 194 } 195 } catch (JmriException reason) { 196 log.error("Exception when setting Sensors for Section {}", getDisplayName(USERSYS)); 197 } 198 break; 199 default: 200 break; 201 } 202 } else { 203 log.error("Attempt to set state of Section {} to illegal value - {}", getDisplayName(USERSYS), state); 204 } 205 } 206 207 /** 208 * Get the occupancy of a Section. 209 * 210 * @return {@link #OCCUPIED}, {@link #UNOCCUPIED}, or the state of the first 211 * block that is neither occupied or unoccupied 212 */ 213 @Override 214 public int getOccupancy() { 215 if (mOccupancyInitialized) { 216 return mOccupancy; 217 } 218 // initialize occupancy 219 mOccupancy = UNOCCUPIED; 220 for (Block block : mBlockEntries) { 221 if (block.getState() == OCCUPIED) { 222 mOccupancy = OCCUPIED; 223 } else if (block.getState() != UNOCCUPIED) { 224 log.warn("Occupancy of block {} is not OCCUPIED or UNOCCUPIED in Section - {}", 225 block.getDisplayName(USERSYS), getDisplayName(USERSYS)); 226 return (block.getState()); 227 } 228 } 229 mOccupancyInitialized = true; 230 return mOccupancy; 231 } 232 233 private void setOccupancy(int occupancy) { 234 int old = mOccupancy; 235 mOccupancy = occupancy; 236 firePropertyChange(PROPERTY_OCCUPANCY, old, mOccupancy); 237 } 238 239 @Override 240 public String getForwardBlockingSensorName() { 241 if (mForwardBlockingNamedSensor != null) { 242 return mForwardBlockingNamedSensor.getName(); 243 } 244 return mForwardBlockingSensorName; 245 } 246 247 @Override 248 public Sensor getForwardBlockingSensor() { 249 if (mForwardBlockingNamedSensor != null) { 250 return mForwardBlockingNamedSensor.getBean(); 251 } 252 if ((mForwardBlockingSensorName != null) 253 && (!mForwardBlockingSensorName.isEmpty())) { 254 Sensor s = InstanceManager.sensorManagerInstance(). 255 getSensor(mForwardBlockingSensorName); 256 if (s == null) { 257 log.error("Missing FB Sensor - {} - when initializing Section - {}", 258 mForwardBlockingSensorName, getDisplayName(USERSYS)); 259 return null; 260 } 261 mForwardBlockingNamedSensor = nbhm.getNamedBeanHandle(mForwardBlockingSensorName, s); 262 return s; 263 } 264 return null; 265 } 266 267 @Override 268 public Sensor setForwardBlockingSensorName(String forwardSensor) { 269 if ((forwardSensor == null) || (forwardSensor.length() <= 0)) { 270 mForwardBlockingSensorName = ""; 271 mForwardBlockingNamedSensor = null; 272 return null; 273 } 274 tempSensorName = forwardSensor; 275 Sensor s = validateSensor(); 276 if (s == null) { 277 // sensor name not correct or not in sensor table 278 log.error("Sensor name - {} invalid when setting forward sensor in Section {}", 279 forwardSensor, getDisplayName(USERSYS)); 280 return null; 281 } 282 mForwardBlockingNamedSensor = nbhm.getNamedBeanHandle(tempSensorName, s); 283 mForwardBlockingSensorName = tempSensorName; 284 return s; 285 } 286 287 @Override 288 public void delayedSetForwardBlockingSensorName(String forwardSensor) { 289 mForwardBlockingSensorName = forwardSensor; 290 } 291 292 @Override 293 public String getReverseBlockingSensorName() { 294 if (mReverseBlockingNamedSensor != null) { 295 return mReverseBlockingNamedSensor.getName(); 296 } 297 return mReverseBlockingSensorName; 298 } 299 300 @Override 301 public Sensor setReverseBlockingSensorName(String reverseSensor) { 302 if ((reverseSensor == null) || (reverseSensor.length() <= 0)) { 303 mReverseBlockingNamedSensor = null; 304 mReverseBlockingSensorName = ""; 305 return null; 306 } 307 tempSensorName = reverseSensor; 308 Sensor s = validateSensor(); 309 if (s == null) { 310 // sensor name not correct or not in sensor table 311 log.error("Sensor name - {} invalid when setting reverse sensor in Section {}", 312 reverseSensor, getDisplayName(USERSYS)); 313 return null; 314 } 315 mReverseBlockingNamedSensor = nbhm.getNamedBeanHandle(tempSensorName, s); 316 mReverseBlockingSensorName = tempSensorName; 317 return s; 318 } 319 320 @Override 321 public void delayedSetReverseBlockingSensorName(String reverseSensor) { 322 mReverseBlockingSensorName = reverseSensor; 323 } 324 325 @Override 326 public Sensor getReverseBlockingSensor() { 327 if (mReverseBlockingNamedSensor != null) { 328 return mReverseBlockingNamedSensor.getBean(); 329 } 330 if ((mReverseBlockingSensorName != null) 331 && (!mReverseBlockingSensorName.isEmpty())) { 332 Sensor s = InstanceManager.sensorManagerInstance(). 333 getSensor(mReverseBlockingSensorName); 334 if (s == null) { 335 log.error("Missing Sensor - {} - when initializing Section - {}", 336 mReverseBlockingSensorName, getDisplayName(USERSYS)); 337 return null; 338 } 339 mReverseBlockingNamedSensor = nbhm.getNamedBeanHandle(mReverseBlockingSensorName, s); 340 return s; 341 } 342 return null; 343 } 344 345 @Override 346 public Block getLastBlock() { 347 return mLastBlock; 348 } 349 350 private String tempSensorName = ""; 351 352 @CheckForNull 353 private Sensor validateSensor() { 354 // check if anything entered 355 if (tempSensorName.length() < 1) { 356 // no sensor specified 357 return null; 358 } 359 // get the sensor corresponding to this name 360 Sensor s = InstanceManager.sensorManagerInstance().getSensor(tempSensorName); 361 if (s == null) { 362 return null; 363 } 364 if (!tempSensorName.equals(s.getUserName()) && s.getUserName() != null) { 365 tempSensorName = s.getUserName(); 366 } 367 return s; 368 } 369 370 @Override 371 public String getForwardStoppingSensorName() { 372 if (mForwardStoppingNamedSensor != null) { 373 return mForwardStoppingNamedSensor.getName(); 374 } 375 return mForwardStoppingSensorName; 376 } 377 378 @Override 379 @CheckForNull 380 public Sensor getForwardStoppingSensor() { 381 if (mForwardStoppingNamedSensor != null) { 382 return mForwardStoppingNamedSensor.getBean(); 383 } 384 if ((mForwardStoppingSensorName != null) 385 && (!mForwardStoppingSensorName.isEmpty())) { 386 Sensor s = InstanceManager.sensorManagerInstance(). 387 getSensor(mForwardStoppingSensorName); 388 if (s == null) { 389 log.error("Missing Sensor - {} - when initializing Section - {}", 390 mForwardStoppingSensorName, getDisplayName(USERSYS)); 391 return null; 392 } 393 mForwardStoppingNamedSensor = nbhm.getNamedBeanHandle(mForwardStoppingSensorName, s); 394 return s; 395 } 396 return null; 397 } 398 399 @Override 400 public Sensor setForwardStoppingSensorName(String forwardSensor) { 401 if ((forwardSensor == null) || (forwardSensor.length() <= 0)) { 402 mForwardStoppingNamedSensor = null; 403 mForwardStoppingSensorName = ""; 404 return null; 405 } 406 tempSensorName = forwardSensor; 407 Sensor s = validateSensor(); 408 if (s == null) { 409 // sensor name not correct or not in sensor table 410 log.error("Sensor name - {} invalid when setting forward sensor in Section {}", 411 forwardSensor, getDisplayName(USERSYS)); 412 return null; 413 } 414 mForwardStoppingNamedSensor = nbhm.getNamedBeanHandle(tempSensorName, s); 415 mForwardStoppingSensorName = tempSensorName; 416 return s; 417 } 418 419 @Override 420 public void delayedSetForwardStoppingSensorName(String forwardSensor) { 421 mForwardStoppingSensorName = forwardSensor; 422 } 423 424 @Override 425 public String getReverseStoppingSensorName() { 426 if (mReverseStoppingNamedSensor != null) { 427 return mReverseStoppingNamedSensor.getName(); 428 } 429 return mReverseStoppingSensorName; 430 } 431 432 @Override 433 @CheckForNull 434 public Sensor setReverseStoppingSensorName(String reverseSensor) { 435 if ((reverseSensor == null) || (reverseSensor.length() <= 0)) { 436 mReverseStoppingNamedSensor = null; 437 mReverseStoppingSensorName = ""; 438 return null; 439 } 440 tempSensorName = reverseSensor; 441 Sensor s = validateSensor(); 442 if (s == null) { 443 // sensor name not correct or not in sensor table 444 log.error("Sensor name - {} invalid when setting reverse sensor in Section {}", 445 reverseSensor, getDisplayName(USERSYS)); 446 return null; 447 } 448 mReverseStoppingNamedSensor = nbhm.getNamedBeanHandle(tempSensorName, s); 449 mReverseStoppingSensorName = tempSensorName; 450 return s; 451 } 452 453 @Override 454 public void delayedSetReverseStoppingSensorName(String reverseSensor) { 455 mReverseStoppingSensorName = reverseSensor; 456 } 457 458 @Override 459 @CheckForNull 460 public Sensor getReverseStoppingSensor() { 461 if (mReverseStoppingNamedSensor != null) { 462 return mReverseStoppingNamedSensor.getBean(); 463 } 464 if ((mReverseStoppingSensorName != null) 465 && (!mReverseStoppingSensorName.isEmpty())) { 466 Sensor s = InstanceManager.sensorManagerInstance(). 467 getSensor(mReverseStoppingSensorName); 468 if (s == null) { 469 log.error("Missing Sensor - {} - when initializing Section - {}", 470 mReverseStoppingSensorName, getDisplayName(USERSYS)); 471 return null; 472 } 473 mReverseStoppingNamedSensor = nbhm.getNamedBeanHandle(mReverseStoppingSensorName, s); 474 return s; 475 } 476 return null; 477 } 478 479 /** 480 * Add a Block to the Section. Block and sequence number must be unique 481 * within the Section. Block sequence numbers are set automatically as 482 * blocks are added. 483 * 484 * @param b the block to add 485 * @return true if Block was added or false if Block does not connect to the 486 * current Block, or the Block is not unique. 487 */ 488 @Override 489 public boolean addBlock(Block b) { 490 // validate that this entry is unique, if not first. 491 if (mBlockEntries.isEmpty()) { 492 mFirstBlock = b; 493 } else { 494 // check that block is unique 495 for (Block block : mBlockEntries) { 496 if (block == b) { 497 return false; // already present 498 } // Note: connectivity to current block is assumed to have been checked 499 } 500 } 501 502 // a lot of this code searches for blocks by their user name. 503 // warn if there isn't one. 504 if (b.getUserName() == null) { 505 log.warn("Block {} does not have a user name, may not work correctly in Section {}", 506 b.getDisplayName(USERSYS), getDisplayName(USERSYS)); 507 } 508 // add Block to the Block list 509 mBlockEntries.add(b); 510 mLastBlock = b; 511 // check occupancy 512 if (b.getState() == OCCUPIED) { 513 if (mOccupancy != OCCUPIED) { 514 setOccupancy(OCCUPIED); 515 } 516 } 517 PropertyChangeListener listener = e -> handleBlockChange(); 518 519 b.addPropertyChangeListener(listener); 520 mBlockListeners.add(listener); 521 return true; 522 } 523 private boolean initializationNeeded = false; 524 private final List<String> blockNameList = new ArrayList<>(); 525 526 @Override 527 public void delayedAddBlock(String blockName) { 528 initializationNeeded = true; 529 blockNameList.add(blockName); 530 } 531 532 private void initializeBlocks() { 533 for (int i = 0; i < blockNameList.size(); i++) { 534 Block b = InstanceManager.getDefault(BlockManager.class).getBlock(blockNameList.get(i)); 535 if (b == null) { 536 log.error("Missing Block - {} - when initializing Section - {}", 537 blockNameList.get(i), getDisplayName(USERSYS)); 538 } else { 539 if (mBlockEntries.isEmpty()) { 540 mFirstBlock = b; 541 } 542 mBlockEntries.add(b); 543 mLastBlock = b; 544 PropertyChangeListener listener = e -> handleBlockChange(); 545 b.addPropertyChangeListener(listener); 546 mBlockListeners.add(listener); 547 } 548 } 549 initializationNeeded = false; 550 } 551 552 /** 553 * Handle change in occupancy of a Block in the Section. 554 */ 555 private void handleBlockChange() { 556 int o = UNOCCUPIED; 557 for (Block block : mBlockEntries) { 558 if (block.getState() == OCCUPIED) { 559 o = OCCUPIED; 560 break; 561 } 562 } 563 if (mOccupancy != o) { 564 setOccupancy(o); 565 } 566 } 567 568 /** 569 * Get a list of blocks in this section 570 * 571 * @return a list of blocks 572 */ 573 @Override 574 @Nonnull 575 public List<Block> getBlockList() { 576 if (initializationNeeded) { 577 initializeBlocks(); 578 } 579 return new ArrayList<>(mBlockEntries); 580 } 581 582 /** 583 * Gets the number of Blocks in this Section 584 * 585 * @return the number of blocks 586 */ 587 @Override 588 public int getNumBlocks() { 589 if (initializationNeeded) { 590 initializeBlocks(); 591 } 592 return mBlockEntries.size(); 593 } 594 595 /** 596 * Get the scale length of Section. Length of the Section is calculated by 597 * summing the lengths of all Blocks in the section. If all Block lengths 598 * have not been entered, length will not be correct. 599 * 600 * @param meters true to return length in meters, false to use feet 601 * @param scale the scale; one of {@link jmri.Scale} 602 * @return the scale length 603 */ 604 @Override 605 public float getLengthF(boolean meters, Scale scale) { 606 if (initializationNeeded) { 607 initializeBlocks(); 608 } 609 float length = 0.0f; 610 for (Block block : mBlockEntries) { 611 length = length + block.getLengthMm(); 612 } 613 length = length / (float) (scale.getScaleFactor()); 614 if (meters) { 615 return (length * 0.001f); 616 } 617 return (length * 0.00328084f); 618 } 619 620 @Override 621 public int getLengthI(boolean meters, Scale scale) { 622 return ((int) ((getLengthF(meters, scale) + 0.5f))); 623 } 624 625 /** 626 * Gets the actual length of the Section without any scaling 627 * 628 * @return the real length in millimeters 629 */ 630 @Override 631 public int getActualLength() { 632 if (initializationNeeded) { 633 initializeBlocks(); 634 } 635 int len = 0; 636 for (Block b : mBlockEntries) { 637 len = len + ((int) b.getLengthMm()); 638 } 639 return len; 640 } 641 642 /** 643 * Get Block by its Sequence number in the Section. 644 * 645 * @param seqNumber the sequence number 646 * @return the block or null if the sequence number is invalid 647 */ 648 @Override 649 @CheckForNull 650 public Block getBlockBySequenceNumber(int seqNumber) { 651 if (initializationNeeded) { 652 initializeBlocks(); 653 } 654 if ((seqNumber < mBlockEntries.size()) && (seqNumber >= 0)) { 655 return mBlockEntries.get(seqNumber); 656 } 657 return null; 658 } 659 660 /** 661 * Get the sequence number of a Block. 662 * 663 * @param b the block to get the sequence of 664 * @return the sequence number of b or -1 if b is not in the Section 665 */ 666 @Override 667 public int getBlockSequenceNumber(Block b) { 668 for (int i = 0; i < mBlockEntries.size(); i++) { 669 if (b == mBlockEntries.get(i)) { 670 return i; 671 } 672 } 673 return -1; 674 } 675 676 /** 677 * Remove all Blocks, Block Listeners, and Entry Points 678 */ 679 @Override 680 public void removeAllBlocksFromSection() { 681 for (int i = mBlockEntries.size(); i > 0; i--) { 682 Block b = mBlockEntries.get(i - 1); 683 b.removePropertyChangeListener(mBlockListeners.get(i - 1)); 684 mBlockListeners.remove(i - 1); 685 mBlockEntries.remove(i - 1); 686 } 687 for (int i = mForwardEntryPoints.size(); i > 0; i--) { 688 mForwardEntryPoints.remove(i - 1); 689 } 690 for (int i = mReverseEntryPoints.size(); i > 0; i--) { 691 mReverseEntryPoints.remove(i - 1); 692 } 693 initializationNeeded = false; 694 } 695 /** 696 * Gets Blocks in order. If state is FREE or FORWARD, returns Blocks in 697 * forward order. If state is REVERSE, returns Blocks in reverse order. 698 * First call getEntryBlock, then call getNextBlock until null is returned. 699 */ 700 private int blockIndex = 0; // index of last block returned 701 702 @Override 703 @CheckForNull 704 public Block getEntryBlock() { 705 if (initializationNeeded) { 706 initializeBlocks(); 707 } 708 if (mBlockEntries.size() <= 0) { 709 return null; 710 } 711 if (mState == REVERSE) { 712 blockIndex = mBlockEntries.size(); 713 } else { 714 blockIndex = 1; 715 } 716 return mBlockEntries.get(blockIndex - 1); 717 } 718 719 @Override 720 @CheckForNull 721 public Block getNextBlock() { 722 if (initializationNeeded) { 723 initializeBlocks(); 724 } 725 if (mState == REVERSE) { 726 blockIndex--; 727 } else { 728 blockIndex++; 729 } 730 if ((blockIndex > mBlockEntries.size()) || (blockIndex <= 0)) { 731 return null; 732 } 733 return mBlockEntries.get(blockIndex - 1); 734 } 735 736 @Override 737 @CheckForNull 738 public Block getExitBlock() { 739 if (initializationNeeded) { 740 initializeBlocks(); 741 } 742 if (mBlockEntries.size() <= 0) { 743 return null; 744 } 745 if (mState == REVERSE) { 746 blockIndex = 1; 747 } else { 748 blockIndex = mBlockEntries.size(); 749 } 750 return mBlockEntries.get(blockIndex - 1); 751 } 752 753 @Override 754 public boolean containsBlock(Block b) { 755 for (Block block : mBlockEntries) { 756 if (b == block) { 757 return true; 758 } 759 } 760 return false; 761 } 762 763 @Override 764 public boolean connectsToBlock(Block b) { 765 if (mForwardEntryPoints.stream().anyMatch((ep) -> (ep.getFromBlock() == b))) { 766 return true; 767 } 768 return mReverseEntryPoints.stream().anyMatch((ep) -> (ep.getFromBlock() == b)); 769 } 770 771 @Override 772 public String getBeginBlockName() { 773 if (initializationNeeded) { 774 initializeBlocks(); 775 } 776 if (mFirstBlock == null) { 777 return "unknown"; 778 } 779 return mFirstBlock.getDisplayName(); 780 } 781 782 @Override 783 public String getEndBlockName() { 784 if (initializationNeeded) { 785 initializeBlocks(); 786 } 787 if (mLastBlock == null) { 788 return "unknown"; 789 } 790 return mLastBlock.getDisplayName(); 791 } 792 793 @Override 794 public void addToForwardList(EntryPoint ep) { 795 if (ep != null) { 796 mForwardEntryPoints.add(ep); 797 } 798 } 799 800 @Override 801 public void addToReverseList(EntryPoint ep) { 802 if (ep != null) { 803 mReverseEntryPoints.add(ep); 804 } 805 } 806 807 @Override 808 public void removeEntryPoint(EntryPoint ep) { 809 for (int i = mForwardEntryPoints.size(); i > 0; i--) { 810 if (mForwardEntryPoints.get(i - 1) == ep) { 811 mForwardEntryPoints.remove(i - 1); 812 } 813 } 814 for (int i = mReverseEntryPoints.size(); i > 0; i--) { 815 if (mReverseEntryPoints.get(i - 1) == ep) { 816 mReverseEntryPoints.remove(i - 1); 817 } 818 } 819 } 820 821 @Nonnull 822 @Override 823 public List<EntryPoint> getForwardEntryPointList() { 824 return new ArrayList<>(this.mForwardEntryPoints); 825 } 826 827 @Nonnull 828 @Override 829 public List<EntryPoint> getReverseEntryPointList() { 830 return new ArrayList<>(this.mReverseEntryPoints); 831 } 832 833 @Nonnull 834 @Override 835 public List<EntryPoint> getEntryPointList() { 836 List<EntryPoint> list = new ArrayList<>(this.mForwardEntryPoints); 837 list.addAll(this.mReverseEntryPoints); 838 return list; 839 } 840 841 @Override 842 public boolean isForwardEntryPoint(EntryPoint ep) { 843 for (int i = 0; i < mForwardEntryPoints.size(); i++) { 844 if (ep == mForwardEntryPoints.get(i)) { 845 return true; 846 } 847 } 848 return false; 849 } 850 851 @Override 852 public boolean isReverseEntryPoint(EntryPoint ep) { 853 for (int i = 0; i < mReverseEntryPoints.size(); i++) { 854 if (ep == mReverseEntryPoints.get(i)) { 855 return true; 856 } 857 } 858 return false; 859 } 860 861 /** 862 * Get the EntryPoint for entry from the specified Section for travel in 863 * specified direction. 864 * 865 * @param s the section 866 * @param dir the direction of travel; one of {@link #FORWARD} or 867 * {@link #REVERSE} 868 * @return the entry point or null if not found 869 */ 870 @Override 871 @CheckForNull 872 public EntryPoint getEntryPointFromSection(Section s, int dir) { 873 if (dir == FORWARD) { 874 for (EntryPoint ep : mForwardEntryPoints) { 875 if (s.containsBlock(ep.getFromBlock())) { 876 return ep; 877 } 878 } 879 } else if (dir == REVERSE) { 880 for (EntryPoint ep : mReverseEntryPoints) { 881 if (s.containsBlock(ep.getFromBlock())) { 882 return ep; 883 } 884 } 885 } 886 return null; 887 } 888 889 /** 890 * Get the EntryPoint for exit to specified Section for travel in the 891 * specified direction. 892 * 893 * @param s the section 894 * @param dir the direction of travel; one of {@link #FORWARD} or 895 * {@link #REVERSE} 896 * @return the entry point or null if not found 897 */ 898 @Override 899 @CheckForNull 900 public EntryPoint getExitPointToSection(Section s, int dir) { 901 if (s == null) { 902 return null; 903 } 904 if (dir == REVERSE) { 905 for (EntryPoint ep : mForwardEntryPoints) { 906 if (s.containsBlock(ep.getFromBlock())) { 907 return ep; 908 } 909 } 910 } else if (dir == FORWARD) { 911 for (EntryPoint ep : mReverseEntryPoints) { 912 if (s.containsBlock(ep.getFromBlock())) { 913 return ep; 914 } 915 } 916 } 917 return null; 918 } 919 920 /** 921 * Get the EntryPoint for entry from the specified Block for travel in the 922 * specified direction. 923 * 924 * @param b the block 925 * @param dir the direction of travel; one of {@link #FORWARD} or 926 * {@link #REVERSE} 927 * @return the entry point or null if not found 928 */ 929 @Override 930 @CheckForNull 931 public EntryPoint getEntryPointFromBlock(Block b, int dir) { 932 if (dir == FORWARD) { 933 for (EntryPoint ep : mForwardEntryPoints) { 934 if (b == ep.getFromBlock()) { 935 return ep; 936 } 937 } 938 } else if (dir == REVERSE) { 939 for (EntryPoint ep : mReverseEntryPoints) { 940 if (b == ep.getFromBlock()) { 941 return ep; 942 } 943 } 944 } 945 return null; 946 } 947 948 /** 949 * Get the EntryPoint for exit to the specified Block for travel in the 950 * specified direction. 951 * 952 * @param b the block 953 * @param dir the direction of travel; one of {@link #FORWARD} or 954 * {@link #REVERSE} 955 * @return the entry point or null if not found 956 */ 957 @Override 958 @CheckForNull 959 public EntryPoint getExitPointToBlock(Block b, int dir) { 960 if (dir == REVERSE) { 961 for (EntryPoint ep : mForwardEntryPoints) { 962 if (b == ep.getFromBlock()) { 963 return ep; 964 } 965 } 966 } else if (dir == FORWARD) { 967 for (EntryPoint ep : mReverseEntryPoints) { 968 if (b == ep.getFromBlock()) { 969 return ep; 970 } 971 } 972 } 973 return null; 974 } 975 976 /** 977 * Returns EntryPoint.FORWARD if proceeding from the throat to the other end 978 * is movement in the forward direction. Returns EntryPoint.REVERSE if 979 * proceeding from the throat to the other end is movement in the reverse 980 * direction. Returns EntryPoint.UNKNOWN if cannot determine direction. This 981 * should only happen if blocks are not set up correctly--if all connections 982 * go to the same Block, or not all Blocks set. An error message is logged 983 * if EntryPoint.UNKNOWN is returned. 984 */ 985 private int getDirectionStandardTurnout(LayoutTurnout t, ConnectivityUtil cUtil) { 986 LayoutBlock aBlock = ((TrackSegment) t.getConnectA()).getLayoutBlock(); 987 LayoutBlock bBlock = ((TrackSegment) t.getConnectB()).getLayoutBlock(); 988 LayoutBlock cBlock = ((TrackSegment) t.getConnectC()).getLayoutBlock(); 989 if ((aBlock == null) || (bBlock == null) || (cBlock == null)) { 990 log.error("All blocks not assigned for track segments connecting to turnout - {}.", 991 t.getTurnout().getDisplayName(USERSYS)); 992 return EntryPoint.UNKNOWN; 993 } 994 Block exBlock = checkDualDirection(aBlock, bBlock, cBlock); 995 if ((exBlock != null) || ((aBlock == bBlock) && (aBlock == cBlock))) { 996 // using Entry Points directly will lead to a problem, try following track - first from A following B 997 int dir = EntryPoint.UNKNOWN; 998 Block tBlock = null; 999 TrackNode tn = new TrackNode(t, HitPointType.TURNOUT_A, (TrackSegment) t.getConnectA(), 1000 false, Turnout.CLOSED); 1001 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1002 tn = cUtil.getNextNode(tn, 0); 1003 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock); 1004 } 1005 if (tBlock == null) { 1006 // try from A following C 1007 tn = new TrackNode(t, HitPointType.TURNOUT_A, (TrackSegment) t.getConnectA(), 1008 false, Turnout.THROWN); 1009 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1010 tn = cUtil.getNextNode(tn, 0); 1011 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock); 1012 } 1013 } 1014 if (tBlock != null) { 1015 String userName = tBlock.getUserName(); 1016 if (userName != null) { 1017 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1018 if (lb != null) { 1019 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb); 1020 } 1021 } 1022 } 1023 if (dir == EntryPoint.UNKNOWN) { 1024 // try from B following A 1025 tBlock = null; 1026 tn = new TrackNode(t, HitPointType.TURNOUT_B, (TrackSegment) t.getConnectB(), 1027 false, Turnout.CLOSED); 1028 while ((tBlock == null) && (tn != null && (!tn.reachedEndOfTrack()))) { 1029 tn = cUtil.getNextNode(tn, 0); 1030 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock); 1031 } 1032 if (tBlock != null) { 1033 String userName = tBlock.getUserName(); 1034 if (userName != null) { 1035 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1036 if (lb != null) { 1037 dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb); 1038 } 1039 } 1040 } 1041 } 1042 if (dir == EntryPoint.UNKNOWN) { 1043 log.error("Block definition ambiguity - cannot determine direction of Turnout {} in Section {}", 1044 t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS)); 1045 } 1046 return dir; 1047 } 1048 if ((aBlock != bBlock) && containsBlock(aBlock.getBlock()) && containsBlock(bBlock.getBlock())) { 1049 // both blocks are different, but are in this Section 1050 if (getBlockSequenceNumber(aBlock.getBlock()) < getBlockSequenceNumber(bBlock.getBlock())) { 1051 return EntryPoint.FORWARD; 1052 } else { 1053 return EntryPoint.REVERSE; 1054 } 1055 } else if ((aBlock != cBlock) && containsBlock(aBlock.getBlock()) && containsBlock(cBlock.getBlock())) { 1056 // both blocks are different, but are in this Section 1057 if (getBlockSequenceNumber(aBlock.getBlock()) < getBlockSequenceNumber(cBlock.getBlock())) { 1058 return EntryPoint.FORWARD; 1059 } else { 1060 return EntryPoint.REVERSE; 1061 } 1062 } 1063 LayoutBlock tBlock = t.getLayoutBlock(); 1064 if (tBlock == null) { 1065 log.error("Block not assigned for turnout {}", t.getTurnout().getDisplayName(USERSYS)); 1066 return EntryPoint.UNKNOWN; 1067 } 1068 if (containsBlock(aBlock.getBlock()) && (!containsBlock(bBlock.getBlock()))) { 1069 // aBlock is in Section, bBlock is not 1070 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock); 1071 if (dir != EntryPoint.UNKNOWN) { 1072 return dir; 1073 } 1074 if ((tBlock != bBlock) && (!containsBlock(tBlock.getBlock()))) { 1075 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, tBlock); 1076 if (dir != EntryPoint.UNKNOWN) { 1077 return dir; 1078 } 1079 } 1080 } 1081 if (containsBlock(aBlock.getBlock()) && (!containsBlock(cBlock.getBlock()))) { 1082 // aBlock is in Section, cBlock is not 1083 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock); 1084 if (dir != EntryPoint.UNKNOWN) { 1085 return dir; 1086 } 1087 if ((tBlock != cBlock) && (!containsBlock(tBlock.getBlock()))) { 1088 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, tBlock); 1089 if (dir != EntryPoint.UNKNOWN) { 1090 return dir; 1091 } 1092 } 1093 } 1094 if ((containsBlock(bBlock.getBlock()) || containsBlock(cBlock.getBlock())) 1095 && (!containsBlock(aBlock.getBlock()))) { 1096 // bBlock or cBlock is in Section, aBlock is not 1097 int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock); 1098 if (dir != EntryPoint.UNKNOWN) { 1099 return dir; 1100 } 1101 if ((tBlock != aBlock) && (!containsBlock(tBlock.getBlock()))) { 1102 dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, tBlock); 1103 if (dir != EntryPoint.UNKNOWN) { 1104 return dir; 1105 } 1106 } 1107 } 1108 if (!containsBlock(aBlock.getBlock()) && !containsBlock(bBlock.getBlock()) && !containsBlock(cBlock.getBlock()) && containsBlock(tBlock.getBlock())) { 1109 //is the turnout in a section of its own? 1110 int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock); 1111 return dir; 1112 } 1113 1114 // should never get here 1115 log.error("Unexpected error in getDirectionStandardTurnout when working with turnout {}", 1116 t.getTurnout().getDisplayName(USERSYS)); 1117 return EntryPoint.UNKNOWN; 1118 } 1119 1120 /** 1121 * Returns EntryPoint.FORWARD if proceeding from A to B (or D to C) is 1122 * movement in the forward direction. Returns EntryPoint.REVERSE if 1123 * proceeding from A to B (or D to C) is movement in the reverse direction. 1124 * Returns EntryPoint.UNKNOWN if cannot determine direction. This should 1125 * only happen if blocks are not set up correctly--if all connections go to 1126 * the same Block, or not all Blocks set. An error message is logged if 1127 * EntryPoint.UNKNOWN is returned. 1128 */ 1129 private int getDirectionXoverTurnout(LayoutTurnout t, ConnectivityUtil cUtil) { 1130 LayoutBlock aBlock = ((TrackSegment) t.getConnectA()).getLayoutBlock(); 1131 LayoutBlock bBlock = ((TrackSegment) t.getConnectB()).getLayoutBlock(); 1132 LayoutBlock cBlock = ((TrackSegment) t.getConnectC()).getLayoutBlock(); 1133 LayoutBlock dBlock = ((TrackSegment) t.getConnectD()).getLayoutBlock(); 1134 if ((aBlock == null) || (bBlock == null) || (cBlock == null) || (dBlock == null)) { 1135 log.error("All blocks not assigned for track segments connecting to crossover turnout - {}.", 1136 t.getTurnout().getDisplayName(USERSYS)); 1137 return EntryPoint.UNKNOWN; 1138 } 1139 if ((aBlock == bBlock) && (aBlock == cBlock) && (aBlock == dBlock)) { 1140 log.error("Block setup problem - All track segments connecting to crossover turnout - {} are assigned to the same Block.", 1141 t.getTurnout().getDisplayName(USERSYS)); 1142 return EntryPoint.UNKNOWN; 1143 } 1144 if ((containsBlock(aBlock.getBlock())) || (containsBlock(bBlock.getBlock()))) { 1145 LayoutBlock exBlock = null; 1146 if (aBlock == bBlock) { 1147 if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) && (cBlock == dBlock)) { 1148 exBlock = cBlock; 1149 } 1150 } 1151 if (exBlock != null) { 1152 // set direction by tracking from a or b 1153 int dir = EntryPoint.UNKNOWN; 1154 Block tBlock = null; 1155 TrackNode tn = new TrackNode(t, HitPointType.TURNOUT_A, (TrackSegment) t.getConnectA(), 1156 false, Turnout.CLOSED); 1157 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1158 tn = cUtil.getNextNode(tn, 0); 1159 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock()); 1160 } 1161 if (tBlock != null) { 1162 String userName = tBlock.getUserName(); 1163 if (userName != null) { 1164 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1165 if (lb != null) { 1166 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb); 1167 } 1168 } 1169 } else { // no tBlock found on leg A 1170 tn = new TrackNode(t, HitPointType.TURNOUT_B, (TrackSegment) t.getConnectB(), 1171 false, Turnout.CLOSED); 1172 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1173 tn = cUtil.getNextNode(tn, 0); 1174 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock()); 1175 } 1176 if (tBlock != null) { 1177 String userName = tBlock.getUserName(); 1178 if (userName != null) { 1179 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1180 if (lb != null) { 1181 dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb); 1182 } 1183 } 1184 } 1185 } 1186 if (dir == EntryPoint.UNKNOWN) { 1187 log.error("Block definition ambiguity - cannot determine direction of crossover Turnout {} in Section {}", 1188 t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS)); 1189 } 1190 return dir; 1191 } 1192 if ((aBlock != bBlock) && containsBlock(aBlock.getBlock()) && containsBlock(bBlock.getBlock())) { 1193 if (getBlockSequenceNumber(aBlock.getBlock()) < getBlockSequenceNumber(bBlock.getBlock())) { 1194 return EntryPoint.FORWARD; 1195 } else { 1196 return EntryPoint.REVERSE; 1197 } 1198 } 1199 if (containsBlock(aBlock.getBlock()) && (!containsBlock(bBlock.getBlock()))) { 1200 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock); 1201 if (dir != EntryPoint.UNKNOWN) { 1202 return dir; 1203 } 1204 } 1205 if (containsBlock(bBlock.getBlock()) && (!containsBlock(aBlock.getBlock()))) { 1206 int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock); 1207 if (dir != EntryPoint.UNKNOWN) { 1208 return dir; 1209 } 1210 } 1211 if ((t.getTurnoutType() != LayoutTurnout.TurnoutType.LH_XOVER) && containsBlock(aBlock.getBlock()) 1212 && (!containsBlock(cBlock.getBlock()))) { 1213 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock); 1214 if (dir != EntryPoint.UNKNOWN) { 1215 return dir; 1216 } 1217 } 1218 if ((t.getTurnoutType() != LayoutTurnout.TurnoutType.RH_XOVER) && containsBlock(bBlock.getBlock()) 1219 && (!containsBlock(dBlock.getBlock()))) { 1220 int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, dBlock); 1221 if (dir != EntryPoint.UNKNOWN) { 1222 return dir; 1223 } 1224 } 1225 } 1226 if ((containsBlock(dBlock.getBlock())) || (containsBlock(cBlock.getBlock()))) { 1227 LayoutBlock exBlock = null; 1228 if (dBlock == cBlock) { 1229 if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) && (bBlock == aBlock)) { 1230 exBlock = aBlock; 1231 } 1232 } 1233 if (exBlock != null) { 1234 // set direction by tracking from c or d 1235 int dir = EntryPoint.UNKNOWN; 1236 Block tBlock = null; 1237 TrackNode tn = new TrackNode(t, HitPointType.TURNOUT_D, (TrackSegment) t.getConnectD(), 1238 false, Turnout.CLOSED); 1239 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1240 tn = cUtil.getNextNode(tn, 0); 1241 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock()); 1242 } 1243 if (tBlock != null) { 1244 String userName = tBlock.getUserName(); 1245 if (userName != null) { 1246 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1247 if (lb != null) { 1248 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb); 1249 } 1250 } 1251 } else { 1252 tn = new TrackNode(t, HitPointType.TURNOUT_C, (TrackSegment) t.getConnectC(), 1253 false, Turnout.CLOSED); 1254 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1255 tn = cUtil.getNextNode(tn, 0); 1256 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock()); 1257 } 1258 if (tBlock != null) { 1259 String userName = tBlock.getUserName(); 1260 if (userName != null) { 1261 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1262 if (lb != null) { 1263 dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb); 1264 } 1265 } 1266 } 1267 } 1268 if (dir == EntryPoint.UNKNOWN) { 1269 log.error("Block definition ambiguity - cannot determine direction of crossover Turnout {} in Section {}.", 1270 t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS)); 1271 } 1272 return dir; 1273 } 1274 if ((dBlock != cBlock) && containsBlock(dBlock.getBlock()) && containsBlock(cBlock.getBlock())) { 1275 if (getBlockSequenceNumber(dBlock.getBlock()) < getBlockSequenceNumber(cBlock.getBlock())) { 1276 return EntryPoint.FORWARD; 1277 } else { 1278 return EntryPoint.REVERSE; 1279 } 1280 } 1281 if (containsBlock(dBlock.getBlock()) && (!containsBlock(cBlock.getBlock()))) { 1282 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock); 1283 if (dir != EntryPoint.UNKNOWN) { 1284 return dir; 1285 } 1286 } 1287 if (containsBlock(cBlock.getBlock()) && (!containsBlock(dBlock.getBlock()))) { 1288 int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, dBlock); 1289 if (dir != EntryPoint.UNKNOWN) { 1290 return dir; 1291 } 1292 } 1293 if ((t.getTurnoutType() != LayoutTurnout.TurnoutType.RH_XOVER) && containsBlock(dBlock.getBlock()) 1294 && (!containsBlock(bBlock.getBlock()))) { 1295 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock); 1296 if (dir != EntryPoint.UNKNOWN) { 1297 return dir; 1298 } 1299 } 1300 if ((t.getTurnoutType() != LayoutTurnout.TurnoutType.LH_XOVER) && containsBlock(cBlock.getBlock()) 1301 && (!containsBlock(aBlock.getBlock()))) { 1302 int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock); 1303 if (dir != EntryPoint.UNKNOWN) { 1304 return dir; 1305 } 1306 } 1307 } 1308 log.error("Entry point checks failed - cannot determine direction of crossover Turnout {} in Section {}.", 1309 t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS)); 1310 return EntryPoint.UNKNOWN; 1311 } 1312 1313 /** 1314 * Returns EntryPoint.FORWARD if proceeding from A to C or D (or B to D or 1315 * C) is movement in the forward direction. Returns EntryPoint.REVERSE if 1316 * proceeding from C or D to A (or D or C to B) is movement in the reverse 1317 * direction. Returns EntryPoint.UNKNOWN if cannot determine direction. This 1318 * should only happen if blocks are not set up correctly--if all connections 1319 * go to the same Block, or not all Blocks set. An error message is logged 1320 * if EntryPoint.UNKNOWN is returned. 1321 * 1322 * @param t Actually of type LayoutSlip, this is the track segment to check. 1323 */ 1324 private int getDirectionSlip(LayoutTurnout t, ConnectivityUtil cUtil) { 1325 LayoutBlock aBlock = ((TrackSegment) t.getConnectA()).getLayoutBlock(); 1326 LayoutBlock bBlock = ((TrackSegment) t.getConnectB()).getLayoutBlock(); 1327 LayoutBlock cBlock = ((TrackSegment) t.getConnectC()).getLayoutBlock(); 1328 LayoutBlock dBlock = ((TrackSegment) t.getConnectD()).getLayoutBlock(); 1329 if ((aBlock == null) || (bBlock == null) || (cBlock == null) || (dBlock == null)) { 1330 log.error("All blocks not assigned for track segments connecting to crossover turnout - {}.", 1331 t.getTurnout().getDisplayName(USERSYS)); 1332 return EntryPoint.UNKNOWN; 1333 } 1334 if ((aBlock == bBlock) && (aBlock == cBlock) && (aBlock == dBlock)) { 1335 log.error("Block setup problem - All track segments connecting to crossover turnout - {} are assigned to the same Block.", 1336 t.getTurnout().getDisplayName(USERSYS)); 1337 return EntryPoint.UNKNOWN; 1338 } 1339 if ((containsBlock(aBlock.getBlock())) || (containsBlock(cBlock.getBlock()))) { 1340 LayoutBlock exBlock = null; 1341 if (aBlock == cBlock) { 1342 if ((t.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) && (bBlock == dBlock)) { 1343 exBlock = bBlock; 1344 } 1345 } 1346 if (exBlock != null) { 1347 // set direction by tracking from a or b 1348 int dir = EntryPoint.UNKNOWN; 1349 Block tBlock = null; 1350 TrackNode tn = new TrackNode(t, HitPointType.SLIP_A, (TrackSegment) t.getConnectA(), 1351 false, LayoutTurnout.STATE_AC); 1352 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1353 tn = cUtil.getNextNode(tn, 0); 1354 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock()); 1355 } 1356 if (tBlock != null) { 1357 String userName = tBlock.getUserName(); 1358 if (userName != null) { 1359 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1360 if (lb != null) { 1361 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb); 1362 } 1363 } 1364 } else { 1365 tn = new TrackNode(t, HitPointType.SLIP_C, (TrackSegment) t.getConnectC(), 1366 false, LayoutTurnout.STATE_AC); 1367 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1368 tn = cUtil.getNextNode(tn, 0); 1369 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock()); 1370 } 1371 if (tBlock != null) { 1372 String userName = tBlock.getUserName(); 1373 if (userName != null) { 1374 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1375 if (lb != null) { 1376 dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb); 1377 } 1378 } 1379 } 1380 } 1381 if (dir == EntryPoint.UNKNOWN) { 1382 log.error("Block definition ambiguity - cannot determine direction of crossover slip {} in Section {}.", 1383 t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS)); 1384 } 1385 return dir; 1386 } 1387 if ((aBlock != cBlock) && containsBlock(aBlock.getBlock()) && containsBlock(cBlock.getBlock())) { 1388 if (getBlockSequenceNumber(aBlock.getBlock()) < getBlockSequenceNumber(cBlock.getBlock())) { 1389 return EntryPoint.FORWARD; 1390 } else { 1391 return EntryPoint.REVERSE; 1392 } 1393 } 1394 if (containsBlock(aBlock.getBlock()) && (!containsBlock(cBlock.getBlock()))) { 1395 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock); 1396 if (dir != EntryPoint.UNKNOWN) { 1397 return dir; 1398 } 1399 } 1400 if (containsBlock(cBlock.getBlock()) && (!containsBlock(aBlock.getBlock()))) { 1401 int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock); 1402 if (dir != EntryPoint.UNKNOWN) { 1403 return dir; 1404 } 1405 } 1406 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, dBlock); 1407 if (dir != EntryPoint.UNKNOWN) { 1408 return dir; 1409 } 1410 } 1411 1412 if ((containsBlock(dBlock.getBlock())) || (containsBlock(bBlock.getBlock()))) { 1413 LayoutBlock exBlock = null; 1414 if (dBlock == bBlock) { 1415 if ((t.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) && (cBlock == aBlock)) { 1416 exBlock = aBlock; 1417 } 1418 } 1419 if (exBlock != null) { 1420 // set direction by tracking from c or d 1421 int dir = EntryPoint.UNKNOWN; 1422 Block tBlock = null; 1423 TrackNode tn = new TrackNode(t, HitPointType.SLIP_D, (TrackSegment) t.getConnectD(), 1424 false, LayoutTurnout.STATE_BD); 1425 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1426 tn = cUtil.getNextNode(tn, 0); 1427 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock()); 1428 } 1429 if (tBlock != null) { 1430 String userName = tBlock.getUserName(); 1431 if (userName != null) { 1432 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1433 if (lb != null) { 1434 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb); 1435 } 1436 } 1437 } else { 1438 tn = new TrackNode(t, HitPointType.TURNOUT_B, (TrackSegment) t.getConnectB(), 1439 false, LayoutTurnout.STATE_BD); 1440 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1441 tn = cUtil.getNextNode(tn, 0); 1442 tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock()); 1443 } 1444 if (tBlock != null) { 1445 String userName = tBlock.getUserName(); 1446 if (userName != null) { 1447 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1448 if (lb != null) { 1449 dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb); 1450 } 1451 } 1452 } 1453 } 1454 if (dir == EntryPoint.UNKNOWN) { 1455 log.error("Block definition ambiguity - cannot determine direction of slip {} in Section {}.", 1456 t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS)); 1457 } 1458 return dir; 1459 } 1460 if ((dBlock != bBlock) && containsBlock(dBlock.getBlock()) && containsBlock(bBlock.getBlock())) { 1461 if (getBlockSequenceNumber(dBlock.getBlock()) < getBlockSequenceNumber(bBlock.getBlock())) { 1462 return EntryPoint.FORWARD; 1463 } else { 1464 return EntryPoint.REVERSE; 1465 } 1466 } 1467 if (containsBlock(dBlock.getBlock()) && (!containsBlock(bBlock.getBlock()))) { 1468 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock); 1469 if (dir != EntryPoint.UNKNOWN) { 1470 return dir; 1471 } 1472 } 1473 if (containsBlock(bBlock.getBlock()) && (!containsBlock(dBlock.getBlock()))) { 1474 int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, dBlock); 1475 if (dir != EntryPoint.UNKNOWN) { 1476 return dir; 1477 } 1478 } 1479 if (t.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) { 1480 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, aBlock); 1481 if (dir != EntryPoint.UNKNOWN) { 1482 return dir; 1483 } 1484 } 1485 } 1486 //If all else fails the slip must be in a block of its own so we shall work it out from there. 1487 if (t.getLayoutBlock() != aBlock) { 1488 //Block is not the same as that connected to A 1489 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, aBlock); 1490 if (dir != EntryPoint.UNKNOWN) { 1491 return dir; 1492 } 1493 } 1494 if (t.getLayoutBlock() != bBlock) { 1495 //Block is not the same as that connected to B 1496 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock); 1497 if (dir != EntryPoint.UNKNOWN) { 1498 return dir; 1499 } 1500 } 1501 if (t.getLayoutBlock() != cBlock) { 1502 //Block is not the same as that connected to C 1503 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock); 1504 if (dir != EntryPoint.UNKNOWN) { 1505 return dir; 1506 } 1507 } 1508 if (t.getLayoutBlock() != dBlock) { 1509 //Block is not the same as that connected to D 1510 int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, dBlock); 1511 if (dir != EntryPoint.UNKNOWN) { 1512 return dir; 1513 } 1514 } 1515 return EntryPoint.UNKNOWN; 1516 } 1517 1518 private boolean placeSensorInCrossover(String b1Name, String b2Name, String c1Name, String c2Name, 1519 int direction, ConnectivityUtil cUtil) { 1520 SignalHead b1Head = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(b1Name); 1521 SignalHead b2Head = null; 1522 SignalHead c1Head = null; 1523 SignalHead c2Head = null; 1524 boolean success = true; 1525 if ((b2Name != null) && (!b2Name.isEmpty())) { 1526 b2Head = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(b2Name); 1527 } 1528 if ((c1Name != null) && (!c1Name.isEmpty())) { 1529 c1Head = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(c1Name); 1530 } 1531 if ((c2Name != null) && (!c2Name.isEmpty())) { 1532 c2Head = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(c2Name); 1533 } 1534 if (b2Head != null) { 1535 if (!checkDirectionSensor(b1Head, direction, ConnectivityUtil.OVERALL, cUtil)) { 1536 success = false; 1537 } 1538 } else { 1539 if (!checkDirectionSensor(b1Head, direction, ConnectivityUtil.CONTINUING, cUtil)) { 1540 success = false; 1541 } 1542 } 1543 if (c2Head != null) { 1544 if (!checkDirectionSensor(c2Head, direction, ConnectivityUtil.OVERALL, cUtil)) { 1545 success = false; 1546 } 1547 } else if (c1Head != null) { 1548 if (!checkDirectionSensor(c1Head, direction, ConnectivityUtil.DIVERGING, cUtil)) { 1549 success = false; 1550 } 1551 } 1552 return success; 1553 } 1554 1555 private int checkLists(List<EntryPoint> forwardList, List<EntryPoint> reverseList, LayoutBlock lBlock) { 1556 for (int i = 0; i < forwardList.size(); i++) { 1557 if (forwardList.get(i).getFromBlock() == lBlock.getBlock()) { 1558 return EntryPoint.FORWARD; 1559 } 1560 } 1561 for (int i = 0; i < reverseList.size(); i++) { 1562 if (reverseList.get(i).getFromBlock() == lBlock.getBlock()) { 1563 return EntryPoint.REVERSE; 1564 } 1565 } 1566 return EntryPoint.UNKNOWN; 1567 } 1568 1569 @CheckForNull 1570 private Block checkDualDirection(LayoutBlock aBlock, LayoutBlock bBlock, LayoutBlock cBlock) { 1571 for (int i = 0; i < mForwardEntryPoints.size(); i++) { 1572 Block b = mForwardEntryPoints.get(i).getFromBlock(); 1573 for (int j = 0; j < mReverseEntryPoints.size(); j++) { 1574 if (mReverseEntryPoints.get(j).getFromBlock() == b) { 1575 // possible dual direction 1576 if (aBlock.getBlock() == b) { 1577 return b; 1578 } else if (bBlock.getBlock() == b) { 1579 return b; 1580 } else if ((cBlock.getBlock() == b) && (aBlock == bBlock)) { 1581 return b; 1582 } 1583 } 1584 } 1585 } 1586 return null; 1587 } 1588 1589 /** 1590 * Returns the direction for proceeding from LayoutBlock b to LayoutBlock a. 1591 * LayoutBlock a must be in the Section. LayoutBlock b may be in this 1592 * Section or may be an Entry Point to the Section. 1593 */ 1594 private int getDirectionForBlocks(LayoutBlock a, LayoutBlock b) { 1595 if (containsBlock(b.getBlock())) { 1596 // both blocks are within this Section 1597 if (getBlockSequenceNumber(a.getBlock()) > getBlockSequenceNumber(b.getBlock())) { 1598 return EntryPoint.FORWARD; 1599 } else { 1600 return EntryPoint.REVERSE; 1601 } 1602 } 1603 // bBlock must be an entry point 1604 for (int i = 0; i < mForwardEntryPoints.size(); i++) { 1605 if (mForwardEntryPoints.get(i).getFromBlock() == b.getBlock()) { 1606 return EntryPoint.FORWARD; 1607 } 1608 } 1609 for (int j = 0; j < mReverseEntryPoints.size(); j++) { 1610 if (mReverseEntryPoints.get(j).getFromBlock() == b.getBlock()) { 1611 return EntryPoint.REVERSE; 1612 } 1613 } 1614 // should never get here 1615 log.error("Unexpected error in getDirectionForBlocks when working with LevelCrossing in Section {}", 1616 getDisplayName(USERSYS)); 1617 return EntryPoint.UNKNOWN; 1618 } 1619 1620 /** 1621 * @return 'true' if successfully checked direction sensor by follow 1622 * connectivity from specified track node; 'false' if an error 1623 * occurred 1624 */ 1625 private boolean setDirectionSensorByConnectivity(TrackNode tNode, TrackNode altNode, SignalHead sh, 1626 Block cBlock, ConnectivityUtil cUtil) { 1627 boolean successful = false; 1628 TrackNode tn = tNode; 1629 if ((tn != null) && (sh != null)) { 1630 Block tBlock = null; 1631 LayoutBlock lb; 1632 int dir = EntryPoint.UNKNOWN; 1633 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1634 tn = cUtil.getNextNode(tn, 0); 1635 tBlock = (tn == null) ? null : cUtil.getExitBlockForTrackNode(tn, null); 1636 } 1637 if (tBlock != null) { 1638 String userName = tBlock.getUserName(); 1639 if (userName != null) { 1640 lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1641 if (lb != null) { 1642 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb); 1643 } 1644 } 1645 } else { 1646 tn = altNode; 1647 while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) { 1648 tn = cUtil.getNextNode(tn, 0); 1649 tBlock = (tn == null) ? null : cUtil.getExitBlockForTrackNode(tn, null); 1650 } 1651 if (tBlock != null) { 1652 String userName = tBlock.getUserName(); 1653 if (userName != null) { 1654 lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 1655 if (lb != null) { 1656 dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb); 1657 if (dir == EntryPoint.REVERSE) { 1658 dir = EntryPoint.FORWARD; 1659 } else if (dir == EntryPoint.FORWARD) { 1660 dir = EntryPoint.REVERSE; 1661 } 1662 } 1663 } 1664 } 1665 } 1666 if (dir != EntryPoint.UNKNOWN) { 1667 if (checkDirectionSensor(sh, dir, ConnectivityUtil.OVERALL, cUtil)) { 1668 successful = true; 1669 } 1670 } else { 1671 log.error("Trouble following track in Block {} in Section {}.", 1672 cBlock.getDisplayName(USERSYS), getDisplayName(USERSYS)); 1673 } 1674 } 1675 return successful; 1676 } 1677 1678 /** 1679 * Place direction sensors in SSL for all Signal Heads in this Section if 1680 * the Sensors are not already present in the SSL. 1681 * <p> 1682 * Only anchor point block boundaries that have assigned signals are 1683 * considered. Only turnouts that have assigned signals are considered. Only 1684 * level crossings that have assigned signals are considered. Turnouts and 1685 * anchor points without signals are counted, and reported in warning 1686 * messages during this procedure, if there are any missing signals. 1687 * <p> 1688 * If this method has trouble, an error message is placed in the log 1689 * describing the trouble. 1690 * 1691 * @return the number or errors placing sensors; 1 is returned if no direction sensor is defined for this section 1692 */ 1693 @Override 1694 public int placeDirectionSensors() { 1695 int missingSignalsBB = 0; 1696 int missingSignalsTurnouts = 0; 1697 int missingSignalsLevelXings = 0; 1698 int errorCount = 0; 1699 1700 var editorManager = InstanceManager.getDefault(EditorManager.class); 1701 if (editorManager.getAll(LayoutEditor.class).isEmpty()) { 1702 log.error("No Layout Editor panels on call to 'placeDirectionSensors'"); 1703 return 1; 1704 } 1705 1706 if (initializationNeeded) { 1707 initializeBlocks(); 1708 } 1709 if ((mForwardBlockingSensorName == null) || (mForwardBlockingSensorName.isEmpty()) 1710 || (mReverseBlockingSensorName == null) || (mReverseBlockingSensorName.isEmpty())) { 1711 log.error("Missing direction sensor in Section {}", getDisplayName(USERSYS)); 1712 return 1; 1713 } 1714 LayoutBlockManager layoutBlockManager = InstanceManager.getDefault(LayoutBlockManager.class); 1715 SignalHeadManager signalHeadManager = InstanceManager.getDefault(SignalHeadManager.class); 1716 LayoutEditor panel; 1717 ConnectivityUtil cUtil; 1718 LayoutBlock lBlock; 1719 for (Block cBlock : mBlockEntries) { 1720 String userName = cBlock.getUserName(); 1721 if (userName == null) { 1722 log.error("No user name for block '{}' in 'placeDirectionSensors'", cBlock); 1723 continue; 1724 } 1725 1726 lBlock = layoutBlockManager.getByUserName(userName); 1727 if (lBlock == null) { 1728 log.error("No layout block for block '{}' in 'placeDirectionSensors'", cBlock.getDisplayName()); 1729 continue; 1730 } 1731 1732 // get the panel and cutil for this Block 1733 panel = lBlock.getMaxConnectedPanel(); 1734 if (panel == null) { 1735 log.error("Unable to get a panel for '{}' in 'placeDirectionSensors'", cBlock.getDisplayName()); 1736 continue; 1737 } 1738 cUtil = new ConnectivityUtil(panel); 1739 1740 List<PositionablePoint> anchorList = cUtil.getAnchorBoundariesThisBlock(cBlock); 1741 for (int j = 0; j < anchorList.size(); j++) { 1742 PositionablePoint p = anchorList.get(j); 1743 if ((!p.getEastBoundSignal().isEmpty()) && (!p.getWestBoundSignal().isEmpty())) { 1744 // have a signalled block boundary 1745 SignalHead sh = cUtil.getSignalHeadAtAnchor(p, cBlock, false); 1746 if (sh == null) { 1747 log.warn("Unexpected missing signal head at boundary of Block {}", cBlock.getDisplayName(USERSYS)); 1748 errorCount++; 1749 } else { 1750 int direction = cUtil.getDirectionFromAnchor(mForwardEntryPoints, 1751 mReverseEntryPoints, p); 1752 if (direction == EntryPoint.UNKNOWN) { 1753 // anchor is at a Block boundary within the Section 1754 sh = cUtil.getSignalHeadAtAnchor(p, cBlock, true); 1755 Block otherBlock = ((p.getConnect1()).getLayoutBlock()).getBlock(); 1756 if (otherBlock == cBlock) { 1757 otherBlock = ((p.getConnect2()).getLayoutBlock()).getBlock(); 1758 } 1759 if (getBlockSequenceNumber(cBlock) < getBlockSequenceNumber(otherBlock)) { 1760 direction = EntryPoint.FORWARD; 1761 } else { 1762 direction = EntryPoint.REVERSE; 1763 } 1764 } 1765 if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) { 1766 errorCount++; 1767 } 1768 } 1769 } else { 1770 errorCount++; 1771 missingSignalsBB++; 1772 } 1773 } 1774 List<LevelXing> xingList = cUtil.getLevelCrossingsThisBlock(cBlock); 1775 for (int k = 0; k < xingList.size(); k++) { 1776 LevelXing x = xingList.get(k); 1777 LayoutBlock alBlock = ((TrackSegment) x.getConnectA()).getLayoutBlock(); 1778 LayoutBlock blBlock = ((TrackSegment) x.getConnectB()).getLayoutBlock(); 1779 LayoutBlock clBlock = ((TrackSegment) x.getConnectC()).getLayoutBlock(); 1780 LayoutBlock dlBlock = ((TrackSegment) x.getConnectD()).getLayoutBlock(); 1781 if (cUtil.isInternalLevelXingAC(x, cBlock)) { 1782 // have an internal AC level crossing - is it signaled? 1783 if (!x.getSignalAName().isEmpty() || (!x.getSignalCName().isEmpty())) { 1784 // have a signaled AC level crossing internal to this block 1785 if (!x.getSignalAName().isEmpty()) { 1786 // there is a signal at A in the level crossing 1787 TrackNode tn = new TrackNode(x, HitPointType.LEVEL_XING_A, 1788 (TrackSegment) x.getConnectA(), false, 0); 1789 TrackNode altNode = new TrackNode(x, HitPointType.LEVEL_XING_C, 1790 (TrackSegment) x.getConnectC(), false, 0); 1791 SignalHead sh = signalHeadManager.getSignalHead(x.getSignalAName()); 1792 if (!setDirectionSensorByConnectivity(tn, altNode, sh, cBlock, cUtil)) { 1793 errorCount++; 1794 } 1795 } 1796 if (!x.getSignalCName().isEmpty()) { 1797 // there is a signal at C in the level crossing 1798 TrackNode tn = new TrackNode(x, HitPointType.LEVEL_XING_C, 1799 (TrackSegment) x.getConnectC(), false, 0); 1800 TrackNode altNode = new TrackNode(x, HitPointType.LEVEL_XING_A, 1801 (TrackSegment) x.getConnectA(), false, 0); 1802 SignalHead sh = signalHeadManager.getSignalHead(x.getSignalCName()); 1803 if (!setDirectionSensorByConnectivity(tn, altNode, sh, cBlock, cUtil)) { 1804 errorCount++; 1805 } 1806 } 1807 } 1808 } else if (alBlock == lBlock) { 1809 // have a level crossing with AC spanning a block boundary, with A in this Block 1810 int direction = getDirectionForBlocks(alBlock, clBlock); 1811 if (direction != EntryPoint.UNKNOWN) { 1812 if (!x.getSignalCName().isEmpty()) { 1813 SignalHead sh = signalHeadManager.getSignalHead(x.getSignalCName()); 1814 if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) { 1815 errorCount++; 1816 } 1817 } 1818 } else { 1819 errorCount++; 1820 } 1821 } else if (clBlock == lBlock) { 1822 // have a level crossing with AC spanning a block boundary, with C in this Block 1823 int direction = getDirectionForBlocks(clBlock, alBlock); 1824 if (direction != EntryPoint.UNKNOWN) { 1825 if (!x.getSignalAName().isEmpty()) { 1826 SignalHead sh = signalHeadManager.getSignalHead(x.getSignalAName()); 1827 if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) { 1828 errorCount++; 1829 } 1830 } 1831 } else { 1832 errorCount++; 1833 } 1834 } 1835 if (cUtil.isInternalLevelXingBD(x, cBlock)) { 1836 // have an internal BD level crossing - is it signaled? 1837 if ((!x.getSignalBName().isEmpty()) || (!x.getSignalDName().isEmpty())) { 1838 // have a signaled BD level crossing internal to this block 1839 if (!x.getSignalBName().isEmpty()) { 1840 // there is a signal at B in the level crossing 1841 TrackNode tn = new TrackNode(x, HitPointType.LEVEL_XING_B, 1842 (TrackSegment) x.getConnectB(), false, 0); 1843 TrackNode altNode = new TrackNode(x, HitPointType.LEVEL_XING_D, 1844 (TrackSegment) x.getConnectD(), false, 0); 1845 SignalHead sh = signalHeadManager.getSignalHead(x.getSignalBName()); 1846 if (!setDirectionSensorByConnectivity(tn, altNode, sh, cBlock, cUtil)) { 1847 errorCount++; 1848 } 1849 } 1850 if (!x.getSignalDName().isEmpty()) { 1851 // there is a signal at C in the level crossing 1852 TrackNode tn = new TrackNode(x, HitPointType.LEVEL_XING_D, 1853 (TrackSegment) x.getConnectD(), false, 0); 1854 TrackNode altNode = new TrackNode(x, HitPointType.LEVEL_XING_B, 1855 (TrackSegment) x.getConnectB(), false, 0); 1856 SignalHead sh = signalHeadManager.getSignalHead(x.getSignalDName()); 1857 if (!setDirectionSensorByConnectivity(tn, altNode, sh, cBlock, cUtil)) { 1858 errorCount++; 1859 } 1860 } 1861 } 1862 } else if (blBlock == lBlock) { 1863 // have a level crossing with BD spanning a block boundary, with B in this Block 1864 int direction = getDirectionForBlocks(blBlock, dlBlock); 1865 if (direction != EntryPoint.UNKNOWN) { 1866 if (!x.getSignalDName().isEmpty()) { 1867 SignalHead sh = signalHeadManager.getSignalHead(x.getSignalDName()); 1868 if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) { 1869 errorCount++; 1870 } 1871 } 1872 } else { 1873 errorCount++; 1874 } 1875 } else if (dlBlock == lBlock) { 1876 // have a level crossing with BD spanning a block boundary, with D in this Block 1877 int direction = getDirectionForBlocks(dlBlock, blBlock); 1878 if (direction != EntryPoint.UNKNOWN) { 1879 if (!x.getSignalBName().isEmpty()) { 1880 SignalHead sh = signalHeadManager.getSignalHead(x.getSignalBName()); 1881 if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) { 1882 errorCount++; 1883 } 1884 } 1885 } else { 1886 errorCount++; 1887 } 1888 } 1889 } 1890 List<LayoutTurnout> turnoutList = cUtil.getLayoutTurnoutsThisBlock(cBlock); 1891 for (int m = 0; m < turnoutList.size(); m++) { 1892 LayoutTurnout t = turnoutList.get(m); 1893 if (cUtil.layoutTurnoutHasRequiredSignals(t)) { 1894 // have a signalled turnout 1895 if ((t.getLinkType() == LayoutTurnout.LinkType.NO_LINK) 1896 && ((t.getTurnoutType() == LayoutTurnout.TurnoutType.RH_TURNOUT) 1897 || (t.getTurnoutType() == LayoutTurnout.TurnoutType.LH_TURNOUT) 1898 || (t.getTurnoutType() == LayoutTurnout.TurnoutType.WYE_TURNOUT))) { 1899 // standard turnout - nothing special 1900 // Note: direction is for proceeding from the throat to either other track 1901 int direction = getDirectionStandardTurnout(t, cUtil); 1902 int altDirection = EntryPoint.FORWARD; 1903 if (direction == EntryPoint.FORWARD) { 1904 altDirection = EntryPoint.REVERSE; 1905 } 1906 if (direction == EntryPoint.UNKNOWN) { 1907 errorCount++; 1908 } else { 1909 SignalHead aHead = signalHeadManager.getSignalHead(t.getSignalA1Name()); 1910 SignalHead a2Head = null; 1911 String a2Name = t.getSignalA2Name(); // returns "" for empty name, never null 1912 if (!a2Name.isEmpty()) { 1913 a2Head = signalHeadManager.getSignalHead(a2Name); 1914 } 1915 SignalHead bHead = signalHeadManager.getSignalHead(t.getSignalB1Name()); 1916 SignalHead cHead = signalHeadManager.getSignalHead(t.getSignalC1Name()); 1917 if (t.getLayoutBlock().getBlock() == cBlock) { 1918 // turnout is in this block, set direction sensors on all signal heads 1919 // Note: need allocation to traverse this turnout 1920 if (!checkDirectionSensor(aHead, direction, 1921 ConnectivityUtil.OVERALL, cUtil)) { 1922 errorCount++; 1923 } 1924 if (a2Head != null) { 1925 if (!checkDirectionSensor(a2Head, direction, 1926 ConnectivityUtil.OVERALL, cUtil)) { 1927 errorCount++; 1928 } 1929 } 1930 if (!checkDirectionSensor(bHead, altDirection, 1931 ConnectivityUtil.OVERALL, cUtil)) { 1932 errorCount++; 1933 } 1934 if (!checkDirectionSensor(cHead, altDirection, 1935 ConnectivityUtil.OVERALL, cUtil)) { 1936 errorCount++; 1937 } 1938 } else { 1939 if (((TrackSegment) t.getConnectA()).getLayoutBlock().getBlock() == cBlock) { 1940 // throat Track Segment is in this Block 1941 if (!checkDirectionSensor(bHead, altDirection, 1942 ConnectivityUtil.OVERALL, cUtil)) { 1943 errorCount++; 1944 } 1945 if (!checkDirectionSensor(cHead, altDirection, 1946 ConnectivityUtil.OVERALL, cUtil)) { 1947 errorCount++; 1948 } 1949 } else if (((t.getContinuingSense() == Turnout.CLOSED) 1950 && (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock)) 1951 || ((t.getContinuingSense() == Turnout.THROWN) 1952 && (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock))) { 1953 // continuing track segment is in this block, normal continuing sense - or - 1954 // diverging track segment is in this block, reverse continuing sense. 1955 if (a2Head == null) { 1956 // single head at throat 1957 if (!checkDirectionSensor(aHead, direction, 1958 ConnectivityUtil.CONTINUING, cUtil)) { 1959 errorCount++; 1960 } 1961 } else { 1962 // two heads at throat 1963 if (!checkDirectionSensor(aHead, direction, 1964 ConnectivityUtil.OVERALL, cUtil)) { 1965 errorCount++; 1966 } 1967 } 1968 if (!checkDirectionSensor(bHead, altDirection, 1969 ConnectivityUtil.OVERALL, cUtil)) { 1970 errorCount++; 1971 } 1972 } else if (((t.getContinuingSense() == Turnout.CLOSED) 1973 && (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock)) 1974 || ((t.getContinuingSense() == Turnout.THROWN) 1975 && (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock))) { 1976 // diverging track segment is in this block, normal continuing sense - or - 1977 // continuing track segment is in this block, reverse continuing sense. 1978 if (a2Head == null) { 1979 // single head at throat 1980 if (!checkDirectionSensor(aHead, direction, 1981 ConnectivityUtil.DIVERGING, cUtil)) { 1982 errorCount++; 1983 } 1984 } else { 1985 // two heads at throat 1986 if (!checkDirectionSensor(a2Head, direction, 1987 ConnectivityUtil.OVERALL, cUtil)) { 1988 errorCount++; 1989 } 1990 } 1991 if (!checkDirectionSensor(cHead, altDirection, 1992 ConnectivityUtil.OVERALL, cUtil)) { 1993 errorCount++; 1994 } 1995 } 1996 } 1997 } 1998 } else if (t.getLinkType() != LayoutTurnout.LinkType.NO_LINK) { 1999 // special linked turnout 2000 LayoutTurnout tLinked = getLayoutTurnoutFromTurnoutName(t.getLinkedTurnoutName(), panel); 2001 if (tLinked == null) { 2002 log.error("null Layout Turnout linked to turnout {}", t.getTurnout().getDisplayName(USERSYS)); 2003 } else if (t.getLinkType() == LayoutTurnout.LinkType.THROAT_TO_THROAT) { 2004 SignalHead b1Head = signalHeadManager.getSignalHead(t.getSignalB1Name()); 2005 SignalHead b2Head = null; 2006 String hName = t.getSignalB2Name(); // returns "" for empty name, never null 2007 if (!hName.isEmpty()) { 2008 b2Head = signalHeadManager.getSignalHead(hName); 2009 } 2010 SignalHead c1Head = signalHeadManager.getSignalHead(t.getSignalC1Name()); 2011 SignalHead c2Head = null; 2012 hName = t.getSignalC2Name(); // returns "" for empty name, never null 2013 if (!hName.isEmpty()) { 2014 c2Head = signalHeadManager.getSignalHead(hName); 2015 } 2016 int direction = getDirectionStandardTurnout(t, cUtil); 2017 int altDirection = EntryPoint.FORWARD; 2018 if (direction == EntryPoint.FORWARD) { 2019 altDirection = EntryPoint.REVERSE; 2020 } 2021 if (direction != EntryPoint.UNKNOWN) { 2022 if (t.getLayoutBlock().getBlock() == cBlock) { 2023 // turnout is in this block, set direction sensors on all signal heads 2024 // Note: need allocation to traverse this turnout 2025 if (!checkDirectionSensor(b1Head, altDirection, 2026 ConnectivityUtil.OVERALL, cUtil)) { 2027 errorCount++; 2028 } 2029 if (b2Head != null) { 2030 if (!checkDirectionSensor(b2Head, altDirection, 2031 ConnectivityUtil.OVERALL, cUtil)) { 2032 errorCount++; 2033 } 2034 } 2035 if (!checkDirectionSensor(c1Head, altDirection, 2036 ConnectivityUtil.OVERALL, cUtil)) { 2037 errorCount++; 2038 } 2039 if (c2Head != null) { 2040 if (!checkDirectionSensor(c2Head, altDirection, 2041 ConnectivityUtil.OVERALL, cUtil)) { 2042 errorCount++; 2043 } 2044 } 2045 } else { 2046 // turnout is not in this block, switch to heads of linked turnout 2047 b1Head = signalHeadManager.getSignalHead(tLinked.getSignalB1Name()); 2048 hName = tLinked.getSignalB2Name(); // returns "" for empty name, never null 2049 b2Head = null; 2050 if (!hName.isEmpty()) { 2051 b2Head = signalHeadManager.getSignalHead(hName); 2052 } 2053 c1Head = signalHeadManager.getSignalHead(tLinked.getSignalC1Name()); 2054 c2Head = null; 2055 hName = tLinked.getSignalC2Name(); // returns "" for empty name, never null 2056 if (!hName.isEmpty()) { 2057 c2Head = signalHeadManager.getSignalHead(hName); 2058 } 2059 if (((t.getContinuingSense() == Turnout.CLOSED) 2060 && (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock)) 2061 || ((t.getContinuingSense() == Turnout.THROWN) 2062 && (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock))) { 2063 // continuing track segment is in this block 2064 if (b2Head != null) { 2065 if (!checkDirectionSensor(b1Head, direction, 2066 ConnectivityUtil.OVERALL, cUtil)) { 2067 errorCount++; 2068 } 2069 } else { 2070 if (!checkDirectionSensor(b1Head, direction, 2071 ConnectivityUtil.CONTINUING, cUtil)) { 2072 errorCount++; 2073 } 2074 } 2075 if (c2Head != null) { 2076 if (!checkDirectionSensor(c1Head, direction, 2077 ConnectivityUtil.OVERALL, cUtil)) { 2078 errorCount++; 2079 } 2080 } else { 2081 if (!checkDirectionSensor(c1Head, direction, 2082 ConnectivityUtil.CONTINUING, cUtil)) { 2083 errorCount++; 2084 } 2085 } 2086 } else if (((t.getContinuingSense() == Turnout.CLOSED) 2087 && (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock)) 2088 || ((t.getContinuingSense() == Turnout.THROWN) 2089 && (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock))) { 2090 // diverging track segment is in this block 2091 if (b2Head != null) { 2092 if (!checkDirectionSensor(b2Head, direction, 2093 ConnectivityUtil.OVERALL, cUtil)) { 2094 errorCount++; 2095 } 2096 } else { 2097 if (!checkDirectionSensor(b1Head, direction, 2098 ConnectivityUtil.DIVERGING, cUtil)) { 2099 errorCount++; 2100 } 2101 } 2102 if (c2Head != null) { 2103 if (!checkDirectionSensor(c2Head, direction, 2104 ConnectivityUtil.OVERALL, cUtil)) { 2105 errorCount++; 2106 } 2107 } else { 2108 if (!checkDirectionSensor(c1Head, direction, 2109 ConnectivityUtil.DIVERGING, cUtil)) { 2110 errorCount++; 2111 } 2112 } 2113 } 2114 } 2115 } 2116 } else if (t.getLinkType() == LayoutTurnout.LinkType.FIRST_3_WAY) { 2117 SignalHead a1Head = signalHeadManager.getSignalHead(t.getSignalA1Name()); 2118 SignalHead a2Head = null; 2119 String hName = t.getSignalA2Name(); 2120 if (!hName.isEmpty()) { 2121 a2Head = signalHeadManager.getSignalHead(hName); 2122 } 2123 SignalHead a3Head = null; 2124 hName = t.getSignalA3Name(); // returns "" for empty name, never null 2125 if (!hName.isEmpty()) { 2126 a3Head = signalHeadManager.getSignalHead(hName); 2127 } 2128 SignalHead cHead = signalHeadManager.getSignalHead(t.getSignalC1Name()); 2129 int direction = getDirectionStandardTurnout(t, cUtil); 2130 int altDirection = EntryPoint.FORWARD; 2131 if (direction == EntryPoint.FORWARD) { 2132 altDirection = EntryPoint.REVERSE; 2133 } 2134 if (direction != EntryPoint.UNKNOWN) { 2135 if (t.getLayoutBlock().getBlock() == cBlock) { 2136 // turnout is in this block, set direction sensors on all signal heads 2137 // Note: need allocation to traverse this turnout 2138 if (!checkDirectionSensor(a1Head, direction, 2139 ConnectivityUtil.OVERALL, cUtil)) { 2140 errorCount++; 2141 } 2142 if ((a2Head != null) && (a3Head != null)) { 2143 if (!checkDirectionSensor(a2Head, direction, 2144 ConnectivityUtil.OVERALL, cUtil)) { 2145 errorCount++; 2146 } 2147 if (!checkDirectionSensor(a3Head, direction, 2148 ConnectivityUtil.OVERALL, cUtil)) { 2149 errorCount++; 2150 } 2151 } 2152 if (!checkDirectionSensor(cHead, altDirection, 2153 ConnectivityUtil.OVERALL, cUtil)) { 2154 errorCount++; 2155 } 2156 } else { 2157 // turnout is not in this block 2158 if (((TrackSegment) t.getConnectA()).getLayoutBlock().getBlock() == cBlock) { 2159 // throat Track Segment is in this Block 2160 if (!checkDirectionSensor(cHead, altDirection, 2161 ConnectivityUtil.OVERALL, cUtil)) { 2162 errorCount++; 2163 } 2164 } else if (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock) { 2165 // diverging track segment is in this Block 2166 if (a2Head != null) { 2167 if (!checkDirectionSensor(a2Head, direction, 2168 ConnectivityUtil.OVERALL, cUtil)) { 2169 errorCount++; 2170 } 2171 } else { 2172 if (!checkDirectionSensor(a1Head, direction, 2173 ConnectivityUtil.DIVERGING, cUtil)) { 2174 errorCount++; 2175 } 2176 } 2177 } 2178 } 2179 } 2180 } else if (t.getLinkType() == LayoutTurnout.LinkType.SECOND_3_WAY) { 2181 SignalHead bHead = signalHeadManager.getSignalHead(t.getSignalB1Name()); 2182 SignalHead cHead = signalHeadManager.getSignalHead(t.getSignalC1Name()); 2183 SignalHead a1Head = signalHeadManager.getSignalHead(tLinked.getSignalA1Name()); 2184 SignalHead a3Head = null; 2185 String hName = tLinked.getSignalA3Name(); 2186 if (!hName.isEmpty()) { 2187 a3Head = signalHeadManager.getSignalHead(hName); 2188 } 2189 int direction = getDirectionStandardTurnout(t, cUtil); 2190 int altDirection = EntryPoint.FORWARD; 2191 if (direction == EntryPoint.FORWARD) { 2192 altDirection = EntryPoint.REVERSE; 2193 } 2194 if (direction != EntryPoint.UNKNOWN) { 2195 if (t.getLayoutBlock().getBlock() == cBlock) { 2196 // turnout is in this block, set direction sensors on b and c signal heads 2197 // Note: need allocation to traverse this turnout 2198 if (!checkDirectionSensor(bHead, altDirection, 2199 ConnectivityUtil.OVERALL, cUtil)) { 2200 errorCount++; 2201 } 2202 if (!checkDirectionSensor(cHead, altDirection, 2203 ConnectivityUtil.OVERALL, cUtil)) { 2204 errorCount++; 2205 } 2206 } 2207 if (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock) { 2208 // diverging track segment is in this Block 2209 if (a3Head != null) { 2210 if (!checkDirectionSensor(a3Head, direction, 2211 ConnectivityUtil.OVERALL, cUtil)) { 2212 errorCount++; 2213 } 2214 } else { 2215 log.warn("Turnout {} - SSL for head {} cannot handle direction sensor for second diverging track.", 2216 tLinked.getTurnout().getDisplayName(USERSYS),( a1Head==null ? "Unknown" : a1Head.getDisplayName(USERSYS))); 2217 errorCount++; 2218 } 2219 } else if (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock) { 2220 // continuing track segment is in this Block 2221 if (a3Head != null) { 2222 if (!checkDirectionSensor(a1Head, direction, 2223 ConnectivityUtil.OVERALL, cUtil)) { 2224 errorCount++; 2225 } 2226 } else { 2227 if (!checkDirectionSensor(a1Head, direction, 2228 ConnectivityUtil.CONTINUING, cUtil)) { 2229 errorCount++; 2230 } 2231 } 2232 } 2233 } 2234 } 2235 } else if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER) 2236 || (t.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER) 2237 || (t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)) { 2238 // crossover turnout 2239 // Note: direction is for proceeding from A to B (or D to C) 2240 int direction = getDirectionXoverTurnout(t, cUtil); 2241 int altDirection = EntryPoint.FORWARD; 2242 if (direction == EntryPoint.FORWARD) { 2243 altDirection = EntryPoint.REVERSE; 2244 } 2245 if (direction == EntryPoint.UNKNOWN) { 2246 errorCount++; 2247 } else { 2248 if (((TrackSegment) t.getConnectA()).getLayoutBlock().getBlock() == cBlock) { 2249 if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) 2250 || (t.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)) { 2251 if (!placeSensorInCrossover(t.getSignalB1Name(), t.getSignalB2Name(), 2252 t.getSignalC1Name(), t.getSignalC2Name(), altDirection, cUtil)) { 2253 errorCount++; 2254 } 2255 } else { 2256 if (!placeSensorInCrossover(t.getSignalB1Name(), t.getSignalB2Name(), 2257 null, null, altDirection, cUtil)) { 2258 errorCount++; 2259 } 2260 } 2261 } 2262 if (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock) { 2263 if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) 2264 || (t.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER)) { 2265 if (!placeSensorInCrossover(t.getSignalA1Name(), t.getSignalA2Name(), 2266 t.getSignalD1Name(), t.getSignalD2Name(), direction, cUtil)) { 2267 errorCount++; 2268 } 2269 } else { 2270 if (!placeSensorInCrossover(t.getSignalA1Name(), t.getSignalA2Name(), 2271 null, null, direction, cUtil)) { 2272 errorCount++; 2273 } 2274 } 2275 } 2276 if (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock) { 2277 if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) 2278 || (t.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)) { 2279 if (!placeSensorInCrossover(t.getSignalD1Name(), t.getSignalD2Name(), 2280 t.getSignalA1Name(), t.getSignalA2Name(), direction, cUtil)) { 2281 errorCount++; 2282 } 2283 } else { 2284 if (!placeSensorInCrossover(t.getSignalD1Name(), t.getSignalD2Name(), 2285 null, null, direction, cUtil)) { 2286 errorCount++; 2287 } 2288 } 2289 } 2290 if (((TrackSegment) t.getConnectD()).getLayoutBlock().getBlock() == cBlock) { 2291 if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) 2292 || (t.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER)) { 2293 if (!placeSensorInCrossover(t.getSignalC1Name(), t.getSignalC2Name(), 2294 t.getSignalB1Name(), t.getSignalB2Name(), altDirection, cUtil)) { 2295 errorCount++; 2296 } 2297 } else { 2298 if (!placeSensorInCrossover(t.getSignalC1Name(), t.getSignalC2Name(), 2299 null, null, altDirection, cUtil)) { 2300 errorCount++; 2301 } 2302 } 2303 } 2304 } 2305 } else if (t.isTurnoutTypeSlip()) { 2306 int direction = getDirectionSlip(t, cUtil); 2307 int altDirection = EntryPoint.FORWARD; 2308 if (direction == EntryPoint.FORWARD) { 2309 altDirection = EntryPoint.REVERSE; 2310 } 2311 if (direction == EntryPoint.UNKNOWN) { 2312 errorCount++; 2313 } else { 2314 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalA1Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) { 2315 errorCount++; 2316 } 2317 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalA2Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) { 2318 errorCount++; 2319 } 2320 if (t.getTurnoutType() == LayoutSlip.TurnoutType.SINGLE_SLIP) { 2321 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalB1Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) { 2322 errorCount++; 2323 } 2324 } else { 2325 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalB1Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) { 2326 errorCount++; 2327 } 2328 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalB2Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) { 2329 errorCount++; 2330 } 2331 } 2332 if (t.getTurnoutType() == LayoutSlip.TurnoutType.SINGLE_SLIP) { 2333 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalC1Name()), direction, ConnectivityUtil.OVERALL, cUtil)) { 2334 errorCount++; 2335 } 2336 } else { 2337 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalC1Name()), direction, ConnectivityUtil.OVERALL, cUtil)) { 2338 errorCount++; 2339 } 2340 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalC2Name()), direction, ConnectivityUtil.OVERALL, cUtil)) { 2341 errorCount++; 2342 } 2343 } 2344 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalD1Name()), direction, ConnectivityUtil.OVERALL, cUtil)) { 2345 errorCount++; 2346 } 2347 if (!checkDirectionSensor(signalHeadManager.getSignalHead(t.getSignalD2Name()), direction, ConnectivityUtil.OVERALL, cUtil)) { 2348 errorCount++; 2349 } 2350 } 2351 } else { 2352 log.error("Unknown turnout type for turnout {} in Section {}.", 2353 t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS)); 2354 errorCount++; 2355 } 2356 } else { 2357 // signal heads missing in turnout 2358 missingSignalsTurnouts++; 2359 } 2360 } 2361 } 2362 // set up missing signal head message, if any 2363 if ((missingSignalsBB + missingSignalsTurnouts + missingSignalsLevelXings) > 0) { 2364 String s = ""; 2365 if (missingSignalsBB > 0) { 2366 s = ", " + (missingSignalsBB) + " anchor point signal heads missing"; 2367 } 2368 if (missingSignalsTurnouts > 0) { 2369 s = ", " + (missingSignalsTurnouts) + " turnouts missing signals"; 2370 } 2371 if (missingSignalsLevelXings > 0) { 2372 s = ", " + (missingSignalsLevelXings) + " level crossings missing signals"; 2373 } 2374 log.warn("Section - {} {}",getDisplayName(USERSYS),s); 2375 } 2376 2377 return errorCount; 2378 } 2379 2380 private boolean checkDirectionSensor(SignalHead sh, int direction, int where, 2381 ConnectivityUtil cUtil) { 2382 String sensorName = ""; 2383 if (direction == EntryPoint.FORWARD) { 2384 sensorName = getForwardBlockingSensorName(); 2385 } else if (direction == EntryPoint.REVERSE) { 2386 sensorName = getReverseBlockingSensorName(); 2387 } 2388 return (cUtil.addSensorToSignalHeadLogic(sensorName, sh, where)); 2389 } 2390 2391 private LayoutTurnout getLayoutTurnoutFromTurnoutName(String turnoutName, LayoutEditor panel) { 2392 Turnout t = InstanceManager.turnoutManagerInstance().getTurnout(turnoutName); 2393 if (t == null) { 2394 return null; 2395 } 2396 for (LayoutTurnout lt : panel.getLayoutTurnouts()) { 2397 if (lt.getTurnout() == t) { 2398 return lt; 2399 } 2400 } 2401 return null; 2402 } 2403 2404 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "was previously marked with @SuppressWarnings, reason unknown") 2405 private List<EntryPoint> getListOfForwardBlockEntryPoints(Block b) { 2406 if (initializationNeeded) { 2407 initializeBlocks(); 2408 } 2409 List<EntryPoint> a = new ArrayList<>(); 2410 for (int i = 0; i < mForwardEntryPoints.size(); i++) { 2411 if (b == (mForwardEntryPoints.get(i)).getBlock()) { 2412 a.add(mForwardEntryPoints.get(i)); 2413 } 2414 } 2415 return a; 2416 } 2417 2418 /** 2419 * Validate the Section. This checks block connectivity, warns of redundant 2420 * EntryPoints, and otherwise checks internal consistency of the Section. An 2421 * appropriate error message is logged if a problem is found. This method 2422 * assumes that Block Paths are correctly initialized. 2423 * 2424 * @return an error description or empty string if there are no errors 2425 */ 2426 @Override 2427 public String validate() { 2428 if (initializationNeeded) { 2429 initializeBlocks(); 2430 } 2431 2432 // validate Paths and Bean Settings if a Layout Editor panel is available 2433 for (int i = 0; i < (mBlockEntries.size() - 1); i++) { 2434 Block test = getBlockBySequenceNumber(i); 2435 if (test == null){ 2436 log.error("Block {} not found in Block Entries. Paths not checked.",i ); 2437 break; 2438 } 2439 String userName = test.getUserName(); 2440 if (userName != null) { 2441 LayoutBlock lBlock = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 2442 if (lBlock == null) { 2443 log.error("Layout Block {} not found. Paths not checked.", userName); 2444 } else { 2445 lBlock.updatePaths(); 2446 } 2447 } 2448 } 2449 2450 // check connectivity between internal blocks 2451 if (mBlockEntries.size() > 1) { 2452 for (int i = 0; i < (mBlockEntries.size() - 1); i++) { 2453 Block thisBlock = getBlockBySequenceNumber(i); 2454 Block nextBlock = getBlockBySequenceNumber(i + 1); 2455 if ( thisBlock == null || nextBlock == null ) { 2456 return "Sequential blocks " + i + " " + thisBlock + " or " 2457 + i+1 + " " + nextBlock + " are empty in Block List."; 2458 } 2459 if (!connected(thisBlock, nextBlock)) { 2460 String s = "Sequential Blocks - " + thisBlock.getDisplayName(USERSYS) 2461 + ", " + nextBlock.getDisplayName(USERSYS) 2462 + " - are not connected in Section " + getDisplayName(USERSYS) + "."; 2463 return s; 2464 } 2465 if (!connected(nextBlock, thisBlock)) { 2466 String s = "Sequential Blocks - " + thisBlock.getDisplayName(USERSYS) 2467 + ", " + nextBlock.getDisplayName(USERSYS) 2468 + " - Paths are not consistent - Section " + getDisplayName(USERSYS) + "."; 2469 return s; 2470 } 2471 } 2472 } 2473 // validate entry points 2474 if ((mForwardEntryPoints.isEmpty()) && (mReverseEntryPoints.isEmpty())) { 2475 return "Section " + getDisplayName(USERSYS) + "has no Entry Points."; 2476 } 2477 if (!mForwardEntryPoints.isEmpty()) { 2478 for (int i = 0; i < mForwardEntryPoints.size(); i++) { 2479 EntryPoint ep = mForwardEntryPoints.get(i); 2480 if (!containsBlock(ep.getBlock())) { 2481 String s = "Entry Point Block, " + ep.getBlock().getDisplayName(USERSYS) 2482 + ", is not a Block in Section " + getDisplayName(USERSYS) + "."; 2483 return s; 2484 } 2485 if (!connectsToBlock(ep.getFromBlock())) { 2486 String s = "Entry Point From Block, " + ep.getBlock().getDisplayName(USERSYS) 2487 + ", is not connected to a Block in Section " + getDisplayName(USERSYS) + "."; 2488 return s; 2489 } 2490 if (!ep.isForwardType()) { 2491 String s = "Direction of FORWARD Entry Point From Block " 2492 + ep.getFromBlock().getDisplayName(USERSYS) + " to Section " 2493 + getDisplayName(USERSYS) + " is incorrectly set."; 2494 return s; 2495 } 2496 if (!connected(ep.getBlock(), ep.getFromBlock())) { 2497 String s = "Entry Point Blocks, " + ep.getBlock().getDisplayName(USERSYS) 2498 + " and " + ep.getFromBlock().getDisplayName(USERSYS) 2499 + ", are not connected in Section " + getDisplayName(USERSYS) + "."; 2500 return s; 2501 } 2502 } 2503 } 2504 if (!mReverseEntryPoints.isEmpty()) { 2505 for (int i = 0; i < mReverseEntryPoints.size(); i++) { 2506 EntryPoint ep = mReverseEntryPoints.get(i); 2507 if (!containsBlock(ep.getBlock())) { 2508 String s = "Entry Point Block, " + ep.getBlock().getDisplayName(USERSYS) 2509 + ", is not a Block in Section " + getDisplayName(USERSYS) + "."; 2510 return s; 2511 } 2512 if (!connectsToBlock(ep.getFromBlock())) { 2513 String s = "Entry Point From Block, " + ep.getBlock().getDisplayName(USERSYS) 2514 + ", is not connected to a Block in Section " + getDisplayName(USERSYS) + "."; 2515 return s; 2516 } 2517 if (!ep.isReverseType()) { 2518 String s = "Direction of REVERSE Entry Point From Block " 2519 + ep.getFromBlock().getDisplayName(USERSYS) + " to Section " 2520 + getDisplayName(USERSYS) + " is incorrectly set."; 2521 return s; 2522 } 2523 if (!connected(ep.getBlock(), ep.getFromBlock())) { 2524 String s = "Entry Point Blocks, " + ep.getBlock().getDisplayName(USERSYS) 2525 + " and " + ep.getFromBlock().getDisplayName(USERSYS) 2526 + ", are not connected in Section " + getDisplayName(USERSYS) + "."; 2527 return s; 2528 } 2529 } 2530 } 2531 return ""; 2532 } 2533 2534 private boolean connected(Block b1, Block b2) { 2535 if ((b1 != null) && (b2 != null)) { 2536 List<Path> paths = b1.getPaths(); 2537 for (int i = 0; i < paths.size(); i++) { 2538 if (paths.get(i).getBlock() == b2) { 2539 return true; 2540 } 2541 } 2542 } 2543 return false; 2544 } 2545 2546 /** 2547 * Set/reset the display to use alternate color for unoccupied blocks in 2548 * this section. If Layout Editor panel is not present, Layout Blocks will 2549 * not be present, and nothing will be set. 2550 * 2551 * @param set true to use alternate unoccupied color; false otherwise 2552 */ 2553 @Override 2554 public void setAlternateColor(boolean set) { 2555 for (Block b : mBlockEntries) { 2556 String userName = b.getUserName(); 2557 if (userName != null) { 2558 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 2559 if (lb != null) { 2560 lb.setUseExtraColor(set); 2561 } 2562 } 2563 } 2564 } 2565 2566 /** 2567 * Set/reset the display to use alternate color for unoccupied blocks in 2568 * this Section. If the Section already contains an active block, then the 2569 * alternative color will be set from the active block, if no active block 2570 * is found or we are clearing the alternative color then all the blocks in 2571 * the Section will be set. If Layout Editor panel is not present, Layout 2572 * Blocks will not be present, and nothing will be set. 2573 * 2574 * @param set true to use alternate unoccupied color; false otherwise 2575 */ 2576 @Override 2577 public void setAlternateColorFromActiveBlock(boolean set) { 2578 LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class); 2579 boolean beenSet = false; 2580 if (!set || getState() == FREE || getState() == UNKNOWN) { 2581 setAlternateColor(set); 2582 } else if (getState() == FORWARD) { 2583 for (Block b : mBlockEntries) { 2584 if (b.getState() == Block.OCCUPIED) { 2585 beenSet = true; 2586 } 2587 if (beenSet) { 2588 String userName = b.getUserName(); 2589 if (userName != null) { 2590 LayoutBlock lb = lbm.getByUserName(userName); 2591 if (lb != null) { 2592 lb.setUseExtraColor(set); 2593 } 2594 } 2595 } 2596 } 2597 } else if (getState() == REVERSE) { 2598 for (Block b : mBlockEntries) { 2599 if (b.getState() == Block.OCCUPIED) { 2600 beenSet = true; 2601 } 2602 if (beenSet) { 2603 String userName = b.getUserName(); 2604 if (userName != null) { 2605 LayoutBlock lb = lbm.getByUserName(userName); 2606 if (lb != null) { 2607 lb.setUseExtraColor(set); 2608 } 2609 } 2610 } 2611 } 2612 } 2613 if (!beenSet) { 2614 setAlternateColor(set); 2615 } 2616 } 2617 2618 /** 2619 * Set the block values for blocks in this Section. 2620 * 2621 * @param name the value to set all blocks to 2622 */ 2623 @Override 2624 public void setNameInBlocks(String name) { 2625 for (Block b : mBlockEntries) { 2626 b.setValue(name); 2627 } 2628 } 2629 2630 /** 2631 * Set the block values for blocks in this Section. 2632 * 2633 * @param value the name to set block values to 2634 */ 2635 @Override 2636 public void setNameInBlocks(Object value) { 2637 for (Block b : mBlockEntries) { 2638 b.setValue(value); 2639 } 2640 } 2641 2642 @Override 2643 public void setNameFromActiveBlock(Object value) { 2644 boolean beenSet = false; 2645 if (value == null || getState() == FREE || getState() == UNKNOWN) { 2646 setNameInBlocks(value); 2647 } else if (getState() == FORWARD) { 2648 for (Block b : mBlockEntries) { 2649 if (b.getState() == Block.OCCUPIED) { 2650 beenSet = true; 2651 } 2652 if (beenSet) { 2653 b.setValue(value); 2654 } 2655 } 2656 } else if (getState() == REVERSE) { 2657 for (Block b : mBlockEntries) { 2658 if (b.getState() == Block.OCCUPIED) { 2659 beenSet = true; 2660 } 2661 if (beenSet) { 2662 b.setValue(value); 2663 } 2664 } 2665 } 2666 if (!beenSet) { 2667 setNameInBlocks(value); 2668 } 2669 } 2670 2671 /** 2672 * Clear the block values for blocks in this Section. 2673 */ 2674 @Override 2675 public void clearNameInUnoccupiedBlocks() { 2676 for (Block b : mBlockEntries) { 2677 if (b.getState() == Block.UNOCCUPIED) { 2678 b.setValue(null); 2679 } 2680 } 2681 } 2682 2683 /** 2684 * Suppress the update of a memory variable when a block goes to unoccupied, 2685 * so the text set above doesn't get wiped out. 2686 * 2687 * @param set true to suppress the update; false otherwise 2688 */ 2689 @Override 2690 public void suppressNameUpdate(boolean set) { 2691 for (Block b : mBlockEntries) { 2692 String userName = b.getUserName(); 2693 if (userName != null) { 2694 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName); 2695 if (lb != null) { 2696 lb.setSuppressNameUpdate(set); 2697 } 2698 } 2699 } 2700 } 2701 2702 private SectionType sectionType = USERDEFINED; 2703 2704 /** 2705 * Set Section Type. 2706 * <ul> 2707 * <li>USERDEFINED - Default Save all the information. 2708 * <li>SIGNALMASTLOGIC - Save only the name, blocks will be added by the SignalMast logic. 2709 * <li>DYNAMICADHOC - Created on an as required basis, not to be saved. 2710 * </ul> 2711 * @param type constant of section type. 2712 */ 2713 @Override 2714 public void setSectionType(SectionType type) { 2715 sectionType = type; 2716 } 2717 2718 /** 2719 * Get Section Type. 2720 * Defaults to USERDEFINED. 2721 * @return constant of section type. 2722 */ 2723 @Override 2724 public SectionType getSectionType() { 2725 return sectionType; 2726 } 2727 2728 @Override 2729 public String getBeanType() { 2730 return Bundle.getMessage("BeanNameSection"); 2731 } 2732 2733 @Override 2734 public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { 2735 if ("CanDelete".equals(evt.getPropertyName())) { // NOI18N 2736 NamedBean nb = (NamedBean) evt.getOldValue(); 2737 if (nb instanceof Sensor) { 2738 if (nb.equals(getForwardBlockingSensor())) { 2739 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 2740 throw new PropertyVetoException(Bundle.getMessage("VetoBlockingSensor", nb.getBeanType(), Bundle.getMessage("Forward"), Bundle.getMessage("Blocking"), getDisplayName()), e); // NOI18N 2741 } 2742 if (nb.equals(getForwardStoppingSensor())) { 2743 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 2744 throw new PropertyVetoException(Bundle.getMessage("VetoBlockingSensor", nb.getBeanType(), Bundle.getMessage("Forward"), Bundle.getMessage("Stopping"), getDisplayName()), e); 2745 } 2746 if (nb.equals(getReverseBlockingSensor())) { 2747 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 2748 throw new PropertyVetoException(Bundle.getMessage("VetoBlockingSensor", nb.getBeanType(), Bundle.getMessage("Reverse"), Bundle.getMessage("Blocking"), getDisplayName()), e); 2749 } 2750 if (nb.equals(getReverseStoppingSensor())) { 2751 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 2752 throw new PropertyVetoException(Bundle.getMessage("VetoBlockingSensor", nb.getBeanType(), Bundle.getMessage("Reverse"), Bundle.getMessage("Stopping"), getDisplayName()), e); 2753 } 2754 } 2755 if (nb instanceof Block) { 2756 Block check = (Block)nb; 2757 if (getBlockList().contains(check)) { 2758 PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null); 2759 throw new PropertyVetoException(Bundle.getMessage("VetoBlockInSection", getDisplayName()), e); 2760 } 2761 } 2762 } 2763 // "DoDelete" case, if needed, should be handled here. 2764 } 2765 2766 @Override 2767 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 2768 List<NamedBeanUsageReport> report = new ArrayList<>(); 2769 if (bean != null) { 2770 getBlockList().forEach((block) -> { 2771 if (bean.equals(block)) { 2772 report.add(new NamedBeanUsageReport("SectionBlock")); 2773 } 2774 }); 2775 if (bean.equals(getForwardBlockingSensor())) { 2776 report.add(new NamedBeanUsageReport("SectionSensorForwardBlocking")); 2777 } 2778 if (bean.equals(getForwardStoppingSensor())) { 2779 report.add(new NamedBeanUsageReport("SectionSensorForwardStopping")); 2780 } 2781 if (bean.equals(getReverseBlockingSensor())) { 2782 report.add(new NamedBeanUsageReport("SectionSensorReverseBlocking")); 2783 } 2784 if (bean.equals(getReverseStoppingSensor())) { 2785 report.add(new NamedBeanUsageReport("SectionSensorReverseStopping")); 2786 } 2787 } 2788 return report; 2789 } 2790 2791 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultSection.class); 2792 2793}