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