java/src/jmri/implementation/DefaultSection.java
package jmri.implementation;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.beans.*;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jmri.*;
import jmri.jmrit.display.EditorManager;
import jmri.jmrit.display.layoutEditor.ConnectivityUtil; // normally these would be rolloed
import jmri.jmrit.display.layoutEditor.HitPointType; // up into jmri.jmrit.display.layoutEditor.*
import jmri.jmrit.display.layoutEditor.LayoutBlock; // but during the LE migration it's
import jmri.jmrit.display.layoutEditor.LayoutBlockManager; // useful to be able to see
import jmri.jmrit.display.layoutEditor.LayoutEditor; // what specific classe are used.
import jmri.jmrit.display.layoutEditor.LayoutSlip;
import jmri.jmrit.display.layoutEditor.LayoutTurnout;
import jmri.jmrit.display.layoutEditor.LevelXing;
import jmri.jmrit.display.layoutEditor.PositionablePoint;
import jmri.jmrit.display.layoutEditor.TrackNode;
import jmri.jmrit.display.layoutEditor.TrackSegment;
import jmri.util.NonNullArrayList;
/**
* Sections represent a group of one or more connected Blocks that may be
* allocated to a train traveling in a given direction.
* <p>
* A Block may be in multiple Sections. All Blocks contained in a given section
* must be unique. Blocks are kept in order--the first block is connected to the
* second, the second is connected to the third, etc.
* <p>
* A Block in a Section must be connected to the Block before it (if there is
* one) and to the Block after it (if there is one), but may not be connected to
* any other Block in the Section. This restriction is enforced when a Section
* is created, and checked when a Section is loaded from disk.
* <p>
* A Section has a "direction" defined by the sequence in which Blocks are added
* to the Section. A train may run through a Section in either the forward
* direction (from first block to last block) or reverse direction (from last
* block to first block).
* <p>
* A Section has one or more EntryPoints. Each EntryPoint is a Path of one of
* the Blocks in the Section that defines a connection to a Block outside of the
* Section. EntryPoints are grouped into two lists: "forwardEntryPoints" - entry
* through which will result in a train traveling in the "forward" direction
* "reverseEntryPoints" - entry through which will result in a train traveling
* in the "reverse" direction Note that "forwardEntryPoints" are also reverse
* exit points, and vice versa.
* <p>
* A Section has one of the following states" FREE - available for allocation by
* a dispatcher FORWARD - allocated for travel in the forward direction REVERSE
* - allocated for travel in the reverse direction
* <p>
* A Section has an occupancy. A Section is OCCUPIED if any of its Blocks is
* OCCUPIED. A Section is UNOCCUPIED if all of its Blocks are UNOCCUPIED
* <p>
* A Section of may be allocated to only one train at a time, even if the trains
* are travelling in the same direction. If a Section has sufficient space for
* multiple trains travelling in the same direction it should be broken up into
* multiple Sections so the trains can follow each other through the original
* Section.
* <p>
* A Section may not contain any reverse loops. The track that is reversed in a
* reverse loop must be in a separate Section.
* <p>
* Each Section optionally carries two direction sensors, one for the forward
* direction and one for the reverse direction. These sensors force signals for
* travel in their respective directions to "RED" when they are active. When the
* Section is free, both the sensors are Active. These internal sensors follow
* the state of the Section, permitting signals to function normally in the
* direction of allocation.
* <p>
* Each Section optionally carries two stopping sensors, one for the forward
* direction and one for the reverse direction. These sensors change to active
* when a train traversing the Section triggers its sensing device. Stopping
* sensors are physical layout sensors, and may be either point sensors or
* occupancy sensors for short blocks at the end of the Section. A stopping
* sensor is used during automatic running to stop a train that has reached the
* end of its allocated Section. This is needed, for example, to allow a train
* to enter a passing siding and clear the track behind it. When not running
* automatically, these sensors may be used to light panel lights to notify the
* dispatcher that the train has reached the end of the Section.
* <p>
* This Section implementation provides for delayed initialization of blocks and
* direction sensors to be independent of order of items in panel files.
*
* @author Dave Duchamp Copyright (C) 2008,2010
*/
public class DefaultSection extends AbstractNamedBean implements Section {
private static final NamedBean.DisplayOptions USERSYS = NamedBean.DisplayOptions.USERNAME_SYSTEMNAME;
public DefaultSection(String systemName, String userName) {
super(systemName, userName);
}
public DefaultSection(String systemName) {
super(systemName);
}
/**
* Persistent instance variables (saved between runs)
*/
private String mForwardBlockingSensorName = "";
private String mReverseBlockingSensorName = "";
private String mForwardStoppingSensorName = "";
private String mReverseStoppingSensorName = "";
private final List<Block> mBlockEntries = new NonNullArrayList<>();
private final List<EntryPoint> mForwardEntryPoints = new NonNullArrayList<>();
private final List<EntryPoint> mReverseEntryPoints = new NonNullArrayList<>();
/**
* Operational instance variables (not saved between runs).
*/
private int mState = FREE;
private int mOccupancy = UNOCCUPIED;
private boolean mOccupancyInitialized = false;
private Block mFirstBlock = null;
private Block mLastBlock = null;
private NamedBeanHandle<Sensor> mForwardBlockingNamedSensor = null;
private NamedBeanHandle<Sensor> mReverseBlockingNamedSensor = null;
private NamedBeanHandle<Sensor> mForwardStoppingNamedSensor = null;
private NamedBeanHandle<Sensor> mReverseStoppingNamedSensor = null;
private final List<PropertyChangeListener> mBlockListeners = new ArrayList<>();
protected jmri.NamedBeanHandleManager nbhm = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class);
/**
* Get the state of the Section.
* UNKNOWN, FORWARD, REVERSE, FREE
*
* @return the section state
*/
@Override
public int getState() {
return mState;
}
/**
* Set the state of the Section.
* FREE, FORWARD or REVERSE.
* <br>
* UNKNOWN state not accepted here.
* @param state the state to set
*/
@Override
public void setState(int state) {
if ((state == Section.FREE) || (state == Section.FORWARD) || (state == Section.REVERSE)) {
int old = mState;
mState = state;
firePropertyChange("state", old, mState);
// update the forward/reverse blocking sensors as needed
switch (state) {
case FORWARD:
try {
if ((getForwardBlockingSensor() != null) && (getForwardBlockingSensor().getState() != Sensor.INACTIVE)) {
getForwardBlockingSensor().setState(Sensor.INACTIVE);
}
if ((getReverseBlockingSensor() != null) && (getReverseBlockingSensor().getState() != Sensor.ACTIVE)) {
getReverseBlockingSensor().setKnownState(Sensor.ACTIVE);
}
} catch (jmri.JmriException reason) {
log.error("Exception when setting Sensors for Section {}", getDisplayName(USERSYS));
}
break;
case REVERSE:
try {
if ((getReverseBlockingSensor() != null) && (getReverseBlockingSensor().getState() != Sensor.INACTIVE)) {
getReverseBlockingSensor().setKnownState(Sensor.INACTIVE);
}
if ((getForwardBlockingSensor() != null) && (getForwardBlockingSensor().getState() != Sensor.ACTIVE)) {
getForwardBlockingSensor().setKnownState(Sensor.ACTIVE);
}
} catch (jmri.JmriException reason) {
log.error("Exception when setting Sensors for Section {}", getDisplayName(USERSYS));
}
break;
case FREE:
try {
if ((getForwardBlockingSensor() != null) && (getForwardBlockingSensor().getState() != Sensor.ACTIVE)) {
getForwardBlockingSensor().setKnownState(Sensor.ACTIVE);
}
if ((getReverseBlockingSensor() != null) && (getReverseBlockingSensor().getState() != Sensor.ACTIVE)) {
getReverseBlockingSensor().setKnownState(Sensor.ACTIVE);
}
} catch (jmri.JmriException reason) {
log.error("Exception when setting Sensors for Section {}", getDisplayName(USERSYS));
}
break;
default:
break;
}
} else {
log.error("Attempt to set state of Section {} to illegal value - {}", getDisplayName(USERSYS), state);
}
}
/**
* Get the occupancy of a Section.
*
* @return {@link #OCCUPIED}, {@link #UNOCCUPIED}, or the state of the first
* block that is neither occupied or unoccupied
*/
@Override
public int getOccupancy() {
if (mOccupancyInitialized) {
return mOccupancy;
}
// initialize occupancy
mOccupancy = UNOCCUPIED;
for (Block block : mBlockEntries) {
if (block.getState() == OCCUPIED) {
mOccupancy = OCCUPIED;
} else if (block.getState() != UNOCCUPIED) {
log.warn("Occupancy of block {} is not OCCUPIED or UNOCCUPIED in Section - {}",
block.getDisplayName(USERSYS), getDisplayName(USERSYS));
return (block.getState());
}
}
mOccupancyInitialized = true;
return mOccupancy;
}
private void setOccupancy(int occupancy) {
int old = mOccupancy;
mOccupancy = occupancy;
firePropertyChange("occupancy", old, mOccupancy);
}
@Override
public String getForwardBlockingSensorName() {
if (mForwardBlockingNamedSensor != null) {
return mForwardBlockingNamedSensor.getName();
}
return mForwardBlockingSensorName;
}
@Override
public Sensor getForwardBlockingSensor() {
if (mForwardBlockingNamedSensor != null) {
return mForwardBlockingNamedSensor.getBean();
}
if ((mForwardBlockingSensorName != null)
&& (!mForwardBlockingSensorName.isEmpty())) {
Sensor s = InstanceManager.sensorManagerInstance().
getSensor(mForwardBlockingSensorName);
if (s == null) {
log.error("Missing Sensor - {} - when initializing Section - {}",
mForwardBlockingSensorName, getDisplayName(USERSYS));
return null;
}
mForwardBlockingNamedSensor = nbhm.getNamedBeanHandle(mForwardBlockingSensorName, s);
return s;
}
return null;
}
@Override
public Sensor setForwardBlockingSensorName(String forwardSensor) {
if ((forwardSensor == null) || (forwardSensor.length() <= 0)) {
mForwardBlockingSensorName = "";
mForwardBlockingNamedSensor = null;
return null;
}
tempSensorName = forwardSensor;
Sensor s = validateSensor();
if (s == null) {
// sensor name not correct or not in sensor table
log.error("Sensor name - {} invalid when setting forward sensor in Section {}",
forwardSensor, getDisplayName(USERSYS));
return null;
}
mForwardBlockingNamedSensor = nbhm.getNamedBeanHandle(tempSensorName, s);
mForwardBlockingSensorName = tempSensorName;
return s;
}
@Override
public void delayedSetForwardBlockingSensorName(String forwardSensor) {
mForwardBlockingSensorName = forwardSensor;
}
@Override
public String getReverseBlockingSensorName() {
if (mReverseBlockingNamedSensor != null) {
return mReverseBlockingNamedSensor.getName();
}
return mReverseBlockingSensorName;
}
@Override
public Sensor setReverseBlockingSensorName(String reverseSensor) {
if ((reverseSensor == null) || (reverseSensor.length() <= 0)) {
mReverseBlockingNamedSensor = null;
mReverseBlockingSensorName = "";
return null;
}
tempSensorName = reverseSensor;
Sensor s = validateSensor();
if (s == null) {
// sensor name not correct or not in sensor table
log.error("Sensor name - {} invalid when setting reverse sensor in Section {}",
reverseSensor, getDisplayName(USERSYS));
return null;
}
mReverseBlockingNamedSensor = nbhm.getNamedBeanHandle(tempSensorName, s);
mReverseBlockingSensorName = tempSensorName;
return s;
}
@Override
public void delayedSetReverseBlockingSensorName(String reverseSensor) {
mReverseBlockingSensorName = reverseSensor;
}
@Override
public Sensor getReverseBlockingSensor() {
if (mReverseBlockingNamedSensor != null) {
return mReverseBlockingNamedSensor.getBean();
}
if ((mReverseBlockingSensorName != null)
&& (!mReverseBlockingSensorName.isEmpty())) {
Sensor s = InstanceManager.sensorManagerInstance().
getSensor(mReverseBlockingSensorName);
if (s == null) {
log.error("Missing Sensor - {} - when initializing Section - {}",
mReverseBlockingSensorName, getDisplayName(USERSYS));
return null;
}
mReverseBlockingNamedSensor = nbhm.getNamedBeanHandle(mReverseBlockingSensorName, s);
return s;
}
return null;
}
@Override
public Block getLastBlock() {
return mLastBlock;
}
private String tempSensorName = "";
@CheckForNull
private Sensor validateSensor() {
// check if anything entered
if (tempSensorName.length() < 1) {
// no sensor specified
return null;
}
// get the sensor corresponding to this name
Sensor s = InstanceManager.sensorManagerInstance().getSensor(tempSensorName);
if (s == null) {
return null;
}
if (!tempSensorName.equals(s.getUserName()) && s.getUserName() != null) {
tempSensorName = s.getUserName();
}
return s;
}
@Override
public String getForwardStoppingSensorName() {
if (mForwardStoppingNamedSensor != null) {
return mForwardStoppingNamedSensor.getName();
}
return mForwardStoppingSensorName;
}
@Override
@CheckForNull
public Sensor getForwardStoppingSensor() {
if (mForwardStoppingNamedSensor != null) {
return mForwardStoppingNamedSensor.getBean();
}
if ((mForwardStoppingSensorName != null)
&& (!mForwardStoppingSensorName.isEmpty())) {
Sensor s = InstanceManager.sensorManagerInstance().
getSensor(mForwardStoppingSensorName);
if (s == null) {
log.error("Missing Sensor - {} - when initializing Section - {}",
mForwardStoppingSensorName, getDisplayName(USERSYS));
return null;
}
mForwardStoppingNamedSensor = nbhm.getNamedBeanHandle(mForwardStoppingSensorName, s);
return s;
}
return null;
}
@Override
public Sensor setForwardStoppingSensorName(String forwardSensor) {
if ((forwardSensor == null) || (forwardSensor.length() <= 0)) {
mForwardStoppingNamedSensor = null;
mForwardStoppingSensorName = "";
return null;
}
tempSensorName = forwardSensor;
Sensor s = validateSensor();
if (s == null) {
// sensor name not correct or not in sensor table
log.error("Sensor name - {} invalid when setting forward sensor in Section {}",
forwardSensor, getDisplayName(USERSYS));
return null;
}
mForwardStoppingNamedSensor = nbhm.getNamedBeanHandle(tempSensorName, s);
mForwardStoppingSensorName = tempSensorName;
return s;
}
@Override
public void delayedSetForwardStoppingSensorName(String forwardSensor) {
mForwardStoppingSensorName = forwardSensor;
}
@Override
public String getReverseStoppingSensorName() {
if (mReverseStoppingNamedSensor != null) {
return mReverseStoppingNamedSensor.getName();
}
return mReverseStoppingSensorName;
}
@Override
@CheckForNull
public Sensor setReverseStoppingSensorName(String reverseSensor) {
if ((reverseSensor == null) || (reverseSensor.length() <= 0)) {
mReverseStoppingNamedSensor = null;
mReverseStoppingSensorName = "";
return null;
}
tempSensorName = reverseSensor;
Sensor s = validateSensor();
if (s == null) {
// sensor name not correct or not in sensor table
log.error("Sensor name - {} invalid when setting reverse sensor in Section {}",
reverseSensor, getDisplayName(USERSYS));
return null;
}
mReverseStoppingNamedSensor = nbhm.getNamedBeanHandle(tempSensorName, s);
mReverseStoppingSensorName = tempSensorName;
return s;
}
@Override
public void delayedSetReverseStoppingSensorName(String reverseSensor) {
mReverseStoppingSensorName = reverseSensor;
}
@Override
@CheckForNull
public Sensor getReverseStoppingSensor() {
if (mReverseStoppingNamedSensor != null) {
return mReverseStoppingNamedSensor.getBean();
}
if ((mReverseStoppingSensorName != null)
&& (!mReverseStoppingSensorName.isEmpty())) {
Sensor s = InstanceManager.sensorManagerInstance().
getSensor(mReverseStoppingSensorName);
if (s == null) {
log.error("Missing Sensor - {} - when initializing Section - {}",
mReverseStoppingSensorName, getDisplayName(USERSYS));
return null;
}
mReverseStoppingNamedSensor = nbhm.getNamedBeanHandle(mReverseStoppingSensorName, s);
return s;
}
return null;
}
/**
* Add a Block to the Section. Block and sequence number must be unique
* within the Section. Block sequence numbers are set automatically as
* blocks are added.
*
* @param b the block to add
* @return true if Block was added or false if Block does not connect to the
* current Block, or the Block is not unique.
*/
@Override
public boolean addBlock(Block b) {
// validate that this entry is unique, if not first.
if (mBlockEntries.isEmpty()) {
mFirstBlock = b;
} else {
// check that block is unique
for (Block block : mBlockEntries) {
if (block == b) {
return false; // already present
} // Note: connectivity to current block is assumed to have been checked
}
}
// a lot of this code searches for blocks by their user name.
// warn if there isn't one.
if (b.getUserName() == null) {
log.warn("Block {} does not have a user name, may not work correctly in Section {}",
b.getDisplayName(USERSYS), getDisplayName(USERSYS));
}
// add Block to the Block list
mBlockEntries.add(b);
mLastBlock = b;
// check occupancy
if (b.getState() == OCCUPIED) {
if (mOccupancy != OCCUPIED) {
setOccupancy(OCCUPIED);
}
}
PropertyChangeListener listener = (PropertyChangeEvent e) -> {
handleBlockChange(e);
};
b.addPropertyChangeListener(listener);
mBlockListeners.add(listener);
return true;
}
private boolean initializationNeeded = false;
private final List<String> blockNameList = new ArrayList<>();
@Override
public void delayedAddBlock(String blockName) {
initializationNeeded = true;
blockNameList.add(blockName);
}
private void initializeBlocks() {
for (int i = 0; i < blockNameList.size(); i++) {
Block b = InstanceManager.getDefault(jmri.BlockManager.class).getBlock(blockNameList.get(i));
if (b == null) {
log.error("Missing Block - {} - when initializing Section - {}",
blockNameList.get(i), getDisplayName(USERSYS));
} else {
if (mBlockEntries.isEmpty()) {
mFirstBlock = b;
}
mBlockEntries.add(b);
mLastBlock = b;
PropertyChangeListener listener = (PropertyChangeEvent e) -> {
handleBlockChange(e);
};
b.addPropertyChangeListener(listener);
mBlockListeners.add(listener);
}
}
initializationNeeded = false;
}
/**
* Handle change in occupancy of a Block in the Section.
*
* @param e event with change
*/
void handleBlockChange(PropertyChangeEvent e) {
int o = UNOCCUPIED;
for (Block block : mBlockEntries) {
if (block.getState() == OCCUPIED) {
o = OCCUPIED;
break;
}
}
if (mOccupancy != o) {
setOccupancy(o);
}
}
/**
* Get a list of blocks in this section
*
* @return a list of blocks
*/
@Override
@Nonnull
public List<Block> getBlockList() {
if (initializationNeeded) {
initializeBlocks();
}
return new ArrayList<>(mBlockEntries);
}
/**
* Gets the number of Blocks in this Section
*
* @return the number of blocks
*/
@Override
public int getNumBlocks() {
if (initializationNeeded) {
initializeBlocks();
}
return mBlockEntries.size();
}
/**
* Get the scale length of Section. Length of the Section is calculated by
* summing the lengths of all Blocks in the section. If all Block lengths
* have not been entered, length will not be correct.
*
* @param meters true to return length in meters, false to use feet
* @param scale the scale; one of {@link jmri.Scale}
* @return the scale length
*/
@Override
public float getLengthF(boolean meters, Scale scale) {
if (initializationNeeded) {
initializeBlocks();
}
float length = 0.0f;
for (Block block : mBlockEntries) {
length = length + block.getLengthMm();
}
length = length / (float) (scale.getScaleFactor());
if (meters) {
return (length * 0.001f);
}
return (length * 0.00328084f);
}
@Override
public int getLengthI(boolean meters, Scale scale) {
return ((int) ((getLengthF(meters, scale) + 0.5f)));
}
/**
* Gets the actual length of the Section without any scaling
*
* @return the real length in millimeters
*/
@Override
public int getActualLength() {
if (initializationNeeded) {
initializeBlocks();
}
int len = 0;
for (Block b : mBlockEntries) {
len = len + ((int) b.getLengthMm());
}
return len;
}
/**
* Get Block by its Sequence number in the Section.
*
* @param seqNumber the sequence number
* @return the block or null if the sequence number is invalid
*/
@Override
@CheckForNull
public Block getBlockBySequenceNumber(int seqNumber) {
if (initializationNeeded) {
initializeBlocks();
}
if ((seqNumber < mBlockEntries.size()) && (seqNumber >= 0)) {
return mBlockEntries.get(seqNumber);
}
return null;
}
/**
* Get the sequence number of a Block.
*
* @param b the block to get the sequence of
* @return the sequence number of b or -1 if b is not in the Section
*/
@Override
public int getBlockSequenceNumber(Block b) {
for (int i = 0; i < mBlockEntries.size(); i++) {
if (b == mBlockEntries.get(i)) {
return i;
}
}
return -1;
}
/**
* Remove all Blocks, Block Listeners, and Entry Points
*/
@Override
public void removeAllBlocksFromSection() {
for (int i = mBlockEntries.size(); i > 0; i--) {
Block b = mBlockEntries.get(i - 1);
b.removePropertyChangeListener(mBlockListeners.get(i - 1));
mBlockListeners.remove(i - 1);
mBlockEntries.remove(i - 1);
}
for (int i = mForwardEntryPoints.size(); i > 0; i--) {
mForwardEntryPoints.remove(i - 1);
}
for (int i = mReverseEntryPoints.size(); i > 0; i--) {
mReverseEntryPoints.remove(i - 1);
}
initializationNeeded = false;
}
/**
* Gets Blocks in order. If state is FREE or FORWARD, returns Blocks in
* forward order. If state is REVERSE, returns Blocks in reverse order.
* First call getEntryBlock, then call getNextBlock until null is returned.
*/
private int blockIndex = 0; // index of last block returned
@Override
@CheckForNull
public Block getEntryBlock() {
if (initializationNeeded) {
initializeBlocks();
}
if (mBlockEntries.size() <= 0) {
return null;
}
if (mState == REVERSE) {
blockIndex = mBlockEntries.size();
} else {
blockIndex = 1;
}
return mBlockEntries.get(blockIndex - 1);
}
@Override
@CheckForNull
public Block getNextBlock() {
if (initializationNeeded) {
initializeBlocks();
}
if (mState == REVERSE) {
blockIndex--;
} else {
blockIndex++;
}
if ((blockIndex > mBlockEntries.size()) || (blockIndex <= 0)) {
return null;
}
return mBlockEntries.get(blockIndex - 1);
}
@Override
@CheckForNull
public Block getExitBlock() {
if (initializationNeeded) {
initializeBlocks();
}
if (mBlockEntries.size() <= 0) {
return null;
}
if (mState == REVERSE) {
blockIndex = 1;
} else {
blockIndex = mBlockEntries.size();
}
return mBlockEntries.get(blockIndex - 1);
}
@Override
public boolean containsBlock(Block b) {
for (Block block : mBlockEntries) {
if (b == block) {
return true;
}
}
return false;
}
@Override
public boolean connectsToBlock(Block b) {
if (mForwardEntryPoints.stream().anyMatch((ep) -> (ep.getFromBlock() == b))) {
return true;
}
return mReverseEntryPoints.stream().anyMatch((ep) -> (ep.getFromBlock() == b));
}
@Override
public String getBeginBlockName() {
if (initializationNeeded) {
initializeBlocks();
}
if (mFirstBlock == null) {
return "unknown";
}
return mFirstBlock.getDisplayName();
}
@Override
public String getEndBlockName() {
if (initializationNeeded) {
initializeBlocks();
}
if (mLastBlock == null) {
return "unknown";
}
return mLastBlock.getDisplayName();
}
@Override
public void addToForwardList(EntryPoint ep) {
if (ep != null) {
mForwardEntryPoints.add(ep);
}
}
@Override
public void addToReverseList(EntryPoint ep) {
if (ep != null) {
mReverseEntryPoints.add(ep);
}
}
@Override
public void removeEntryPoint(EntryPoint ep) {
for (int i = mForwardEntryPoints.size(); i > 0; i--) {
if (mForwardEntryPoints.get(i - 1) == ep) {
mForwardEntryPoints.remove(i - 1);
}
}
for (int i = mReverseEntryPoints.size(); i > 0; i--) {
if (mReverseEntryPoints.get(i - 1) == ep) {
mReverseEntryPoints.remove(i - 1);
}
}
}
@Override
public List<EntryPoint> getForwardEntryPointList() {
return new ArrayList<>(this.mForwardEntryPoints);
}
@Override
public List<EntryPoint> getReverseEntryPointList() {
return new ArrayList<>(this.mReverseEntryPoints);
}
@Override
public List<EntryPoint> getEntryPointList() {
List<EntryPoint> list = new ArrayList<>(this.mForwardEntryPoints);
list.addAll(this.mReverseEntryPoints);
return list;
}
@Override
public boolean isForwardEntryPoint(EntryPoint ep) {
for (int i = 0; i < mForwardEntryPoints.size(); i++) {
if (ep == mForwardEntryPoints.get(i)) {
return true;
}
}
return false;
}
@Override
public boolean isReverseEntryPoint(EntryPoint ep) {
for (int i = 0; i < mReverseEntryPoints.size(); i++) {
if (ep == mReverseEntryPoints.get(i)) {
return true;
}
}
return false;
}
/**
* Get the EntryPoint for entry from the specified Section for travel in
* specified direction.
*
* @param s the section
* @param dir the direction of travel; one of {@link #FORWARD} or
* {@link #REVERSE}
* @return the entry point or null if not found
*/
@Override
@CheckForNull
public EntryPoint getEntryPointFromSection(Section s, int dir) {
if (dir == FORWARD) {
for (EntryPoint ep : mForwardEntryPoints) {
if (s.containsBlock(ep.getFromBlock())) {
return ep;
}
}
} else if (dir == REVERSE) {
for (EntryPoint ep : mReverseEntryPoints) {
if (s.containsBlock(ep.getFromBlock())) {
return ep;
}
}
}
return null;
}
/**
* Get the EntryPoint for exit to specified Section for travel in the
* specified direction.
*
* @param s the section
* @param dir the direction of travel; one of {@link #FORWARD} or
* {@link #REVERSE}
* @return the entry point or null if not found
*/
@Override
@CheckForNull
public EntryPoint getExitPointToSection(Section s, int dir) {
if (s == null) {
return null;
}
if (dir == REVERSE) {
for (EntryPoint ep : mForwardEntryPoints) {
if (s.containsBlock(ep.getFromBlock())) {
return ep;
}
}
} else if (dir == FORWARD) {
for (EntryPoint ep : mReverseEntryPoints) {
if (s.containsBlock(ep.getFromBlock())) {
return ep;
}
}
}
return null;
}
/**
* Get the EntryPoint for entry from the specified Block for travel in the
* specified direction.
*
* @param b the block
* @param dir the direction of travel; one of {@link #FORWARD} or
* {@link #REVERSE}
* @return the entry point or null if not found
*/
@Override
@CheckForNull
public EntryPoint getEntryPointFromBlock(Block b, int dir) {
if (dir == FORWARD) {
for (EntryPoint ep : mForwardEntryPoints) {
if (b == ep.getFromBlock()) {
return ep;
}
}
} else if (dir == REVERSE) {
for (EntryPoint ep : mReverseEntryPoints) {
if (b == ep.getFromBlock()) {
return ep;
}
}
}
return null;
}
/**
* Get the EntryPoint for exit to the specified Block for travel in the
* specified direction.
*
* @param b the block
* @param dir the direction of travel; one of {@link #FORWARD} or
* {@link #REVERSE}
* @return the entry point or null if not found
*/
@Override
@CheckForNull
public EntryPoint getExitPointToBlock(Block b, int dir) {
if (dir == REVERSE) {
for (EntryPoint ep : mForwardEntryPoints) {
if (b == ep.getFromBlock()) {
return ep;
}
}
} else if (dir == FORWARD) {
for (EntryPoint ep : mReverseEntryPoints) {
if (b == ep.getFromBlock()) {
return ep;
}
}
}
return null;
}
/**
* Returns EntryPoint.FORWARD if proceeding from the throat to the other end
* is movement in the forward direction. Returns EntryPoint.REVERSE if
* proceeding from the throat to the other end is movement in the reverse
* direction. Returns EntryPoint.UNKNOWN if cannot determine direction. This
* should only happen if blocks are not set up correctly--if all connections
* go to the same Block, or not all Blocks set. An error message is logged
* if EntryPoint.UNKNOWN is returned.
*/
private int getDirectionStandardTurnout(LayoutTurnout t, ConnectivityUtil cUtil) {
LayoutBlock aBlock = ((TrackSegment) t.getConnectA()).getLayoutBlock();
LayoutBlock bBlock = ((TrackSegment) t.getConnectB()).getLayoutBlock();
LayoutBlock cBlock = ((TrackSegment) t.getConnectC()).getLayoutBlock();
if ((aBlock == null) || (bBlock == null) || (cBlock == null)) {
log.error("All blocks not assigned for track segments connecting to turnout - {}.",
t.getTurnout().getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
Block exBlock = checkDualDirection(aBlock, bBlock, cBlock);
if ((exBlock != null) || ((aBlock == bBlock) && (aBlock == cBlock))) {
// using Entry Points directly will lead to a problem, try following track - first from A following B
int dir = EntryPoint.UNKNOWN;
Block tBlock = null;
TrackNode tn = new TrackNode(t, HitPointType.TURNOUT_A, (TrackSegment) t.getConnectA(),
false, Turnout.CLOSED);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock);
}
if (tBlock == null) {
// try from A following C
tn = new TrackNode(t, HitPointType.TURNOUT_A, (TrackSegment) t.getConnectA(),
false, Turnout.THROWN);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock);
}
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb);
}
}
}
if (dir == EntryPoint.UNKNOWN) {
// try from B following A
tBlock = null;
tn = new TrackNode(t, HitPointType.TURNOUT_B, (TrackSegment) t.getConnectB(),
false, Turnout.CLOSED);
while ((tBlock == null) && (tn != null && (!tn.reachedEndOfTrack()))) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock);
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb);
}
}
}
}
if (dir == EntryPoint.UNKNOWN) {
log.error("Block definition ambiguity - cannot determine direction of Turnout {} in Section {}",
t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS));
}
return dir;
}
if ((aBlock != bBlock) && containsBlock(aBlock.getBlock()) && containsBlock(bBlock.getBlock())) {
// both blocks are different, but are in this Section
if (getBlockSequenceNumber(aBlock.getBlock()) < getBlockSequenceNumber(bBlock.getBlock())) {
return EntryPoint.FORWARD;
} else {
return EntryPoint.REVERSE;
}
} else if ((aBlock != cBlock) && containsBlock(aBlock.getBlock()) && containsBlock(cBlock.getBlock())) {
// both blocks are different, but are in this Section
if (getBlockSequenceNumber(aBlock.getBlock()) < getBlockSequenceNumber(cBlock.getBlock())) {
return EntryPoint.FORWARD;
} else {
return EntryPoint.REVERSE;
}
}
LayoutBlock tBlock = t.getLayoutBlock();
if (tBlock == null) {
log.error("Block not assigned for turnout {}", t.getTurnout().getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
if (containsBlock(aBlock.getBlock()) && (!containsBlock(bBlock.getBlock()))) {
// aBlock is in Section, bBlock is not
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
if ((tBlock != bBlock) && (!containsBlock(tBlock.getBlock()))) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, tBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
}
if (containsBlock(aBlock.getBlock()) && (!containsBlock(cBlock.getBlock()))) {
// aBlock is in Section, cBlock is not
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
if ((tBlock != cBlock) && (!containsBlock(tBlock.getBlock()))) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, tBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
}
if ((containsBlock(bBlock.getBlock()) || containsBlock(cBlock.getBlock()))
&& (!containsBlock(aBlock.getBlock()))) {
// bBlock or cBlock is in Section, aBlock is not
int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
if ((tBlock != aBlock) && (!containsBlock(tBlock.getBlock()))) {
dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, tBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
}
if (!containsBlock(aBlock.getBlock()) && !containsBlock(bBlock.getBlock()) && !containsBlock(cBlock.getBlock()) && containsBlock(tBlock.getBlock())) {
//is the turnout in a section of its own?
int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock);
return dir;
}
// should never get here
log.error("Unexpected error in getDirectionStandardTurnout when working with turnout {}",
t.getTurnout().getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
/**
* Returns EntryPoint.FORWARD if proceeding from A to B (or D to C) is
* movement in the forward direction. Returns EntryPoint.REVERSE if
* proceeding from A to B (or D to C) is movement in the reverse direction.
* Returns EntryPoint.UNKNOWN if cannot determine direction. This should
* only happen if blocks are not set up correctly--if all connections go to
* the same Block, or not all Blocks set. An error message is logged if
* EntryPoint.UNKNOWN is returned.
*/
private int getDirectionXoverTurnout(LayoutTurnout t, ConnectivityUtil cUtil) {
LayoutBlock aBlock = ((TrackSegment) t.getConnectA()).getLayoutBlock();
LayoutBlock bBlock = ((TrackSegment) t.getConnectB()).getLayoutBlock();
LayoutBlock cBlock = ((TrackSegment) t.getConnectC()).getLayoutBlock();
LayoutBlock dBlock = ((TrackSegment) t.getConnectD()).getLayoutBlock();
if ((aBlock == null) || (bBlock == null) || (cBlock == null) || (dBlock == null)) {
log.error("All blocks not assigned for track segments connecting to crossover turnout - {}.",
t.getTurnout().getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
if ((aBlock == bBlock) && (aBlock == cBlock) && (aBlock == dBlock)) {
log.error("Block setup problem - All track segments connecting to crossover turnout - {} are assigned to the same Block.",
t.getTurnout().getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
if ((containsBlock(aBlock.getBlock())) || (containsBlock(bBlock.getBlock()))) {
LayoutBlock exBlock = null;
if (aBlock == bBlock) {
if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) && (cBlock == dBlock)) {
exBlock = cBlock;
}
}
if (exBlock != null) {
// set direction by tracking from a or b
int dir = EntryPoint.UNKNOWN;
Block tBlock = null;
TrackNode tn = new TrackNode(t, HitPointType.TURNOUT_A, (TrackSegment) t.getConnectA(),
false, Turnout.CLOSED);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock());
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb);
}
}
} else { // no tBlock found on leg A
tn = new TrackNode(t, HitPointType.TURNOUT_B, (TrackSegment) t.getConnectB(),
false, Turnout.CLOSED);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock());
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb);
}
}
}
}
if (dir == EntryPoint.UNKNOWN) {
log.error("Block definition ambiguity - cannot determine direction of crossover Turnout {} in Section {}",
t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS));
}
return dir;
}
if ((aBlock != bBlock) && containsBlock(aBlock.getBlock()) && containsBlock(bBlock.getBlock())) {
if (getBlockSequenceNumber(aBlock.getBlock()) < getBlockSequenceNumber(bBlock.getBlock())) {
return EntryPoint.FORWARD;
} else {
return EntryPoint.REVERSE;
}
}
if (containsBlock(aBlock.getBlock()) && (!containsBlock(bBlock.getBlock()))) {
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if (containsBlock(bBlock.getBlock()) && (!containsBlock(aBlock.getBlock()))) {
int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if ((t.getTurnoutType() != LayoutTurnout.TurnoutType.LH_XOVER) && containsBlock(aBlock.getBlock())
&& (!containsBlock(cBlock.getBlock()))) {
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if ((t.getTurnoutType() != LayoutTurnout.TurnoutType.RH_XOVER) && containsBlock(bBlock.getBlock())
&& (!containsBlock(dBlock.getBlock()))) {
int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, dBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
}
if ((containsBlock(dBlock.getBlock())) || (containsBlock(cBlock.getBlock()))) {
LayoutBlock exBlock = null;
if (dBlock == cBlock) {
if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) && (bBlock == aBlock)) {
exBlock = aBlock;
}
}
if (exBlock != null) {
// set direction by tracking from c or d
int dir = EntryPoint.UNKNOWN;
Block tBlock = null;
TrackNode tn = new TrackNode(t, HitPointType.TURNOUT_D, (TrackSegment) t.getConnectD(),
false, Turnout.CLOSED);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock());
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb);
}
}
} else {
tn = new TrackNode(t, HitPointType.TURNOUT_C, (TrackSegment) t.getConnectC(),
false, Turnout.CLOSED);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock());
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb);
}
}
}
}
if (dir == EntryPoint.UNKNOWN) {
log.error("Block definition ambiguity - cannot determine direction of crossover Turnout {} in Section {}.",
t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS));
}
return dir;
}
if ((dBlock != cBlock) && containsBlock(dBlock.getBlock()) && containsBlock(cBlock.getBlock())) {
if (getBlockSequenceNumber(dBlock.getBlock()) < getBlockSequenceNumber(cBlock.getBlock())) {
return EntryPoint.FORWARD;
} else {
return EntryPoint.REVERSE;
}
}
if (containsBlock(dBlock.getBlock()) && (!containsBlock(cBlock.getBlock()))) {
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if (containsBlock(cBlock.getBlock()) && (!containsBlock(dBlock.getBlock()))) {
int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, dBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if ((t.getTurnoutType() != LayoutTurnout.TurnoutType.RH_XOVER) && containsBlock(dBlock.getBlock())
&& (!containsBlock(bBlock.getBlock()))) {
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if ((t.getTurnoutType() != LayoutTurnout.TurnoutType.LH_XOVER) && containsBlock(cBlock.getBlock())
&& (!containsBlock(aBlock.getBlock()))) {
int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
}
log.error("Entry point checks failed - cannot determine direction of crossover Turnout {} in Section {}.",
t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
/**
* Returns EntryPoint.FORWARD if proceeding from A to C or D (or B to D or
* C) is movement in the forward direction. Returns EntryPoint.REVERSE if
* proceeding from C or D to A (or D or C to B) is movement in the reverse
* direction. Returns EntryPoint.UNKNOWN if cannot determine direction. This
* should only happen if blocks are not set up correctly--if all connections
* go to the same Block, or not all Blocks set. An error message is logged
* if EntryPoint.UNKNOWN is returned.
*
* @param t Actually of type LayoutSlip, this is the track segment to check.
*/
private int getDirectionSlip(LayoutTurnout t, ConnectivityUtil cUtil) {
LayoutBlock aBlock = ((TrackSegment) t.getConnectA()).getLayoutBlock();
LayoutBlock bBlock = ((TrackSegment) t.getConnectB()).getLayoutBlock();
LayoutBlock cBlock = ((TrackSegment) t.getConnectC()).getLayoutBlock();
LayoutBlock dBlock = ((TrackSegment) t.getConnectD()).getLayoutBlock();
if ((aBlock == null) || (bBlock == null) || (cBlock == null) || (dBlock == null)) {
log.error("All blocks not assigned for track segments connecting to crossover turnout - {}.",
t.getTurnout().getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
if ((aBlock == bBlock) && (aBlock == cBlock) && (aBlock == dBlock)) {
log.error("Block setup problem - All track segments connecting to crossover turnout - {} are assigned to the same Block.",
t.getTurnout().getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
if ((containsBlock(aBlock.getBlock())) || (containsBlock(cBlock.getBlock()))) {
LayoutBlock exBlock = null;
if (aBlock == cBlock) {
if ((t.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) && (bBlock == dBlock)) {
exBlock = bBlock;
}
}
if (exBlock != null) {
// set direction by tracking from a or b
int dir = EntryPoint.UNKNOWN;
Block tBlock = null;
TrackNode tn = new TrackNode(t, HitPointType.SLIP_A, (TrackSegment) t.getConnectA(),
false, LayoutTurnout.STATE_AC);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock());
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb);
}
}
} else {
tn = new TrackNode(t, HitPointType.SLIP_C, (TrackSegment) t.getConnectC(),
false, LayoutTurnout.STATE_AC);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock());
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb);
}
}
}
}
if (dir == EntryPoint.UNKNOWN) {
log.error("Block definition ambiguity - cannot determine direction of crossover slip {} in Section {}.",
t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS));
}
return dir;
}
if ((aBlock != cBlock) && containsBlock(aBlock.getBlock()) && containsBlock(cBlock.getBlock())) {
if (getBlockSequenceNumber(aBlock.getBlock()) < getBlockSequenceNumber(cBlock.getBlock())) {
return EntryPoint.FORWARD;
} else {
return EntryPoint.REVERSE;
}
}
if (containsBlock(aBlock.getBlock()) && (!containsBlock(cBlock.getBlock()))) {
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if (containsBlock(cBlock.getBlock()) && (!containsBlock(aBlock.getBlock()))) {
int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, aBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, dBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if ((containsBlock(dBlock.getBlock())) || (containsBlock(bBlock.getBlock()))) {
LayoutBlock exBlock = null;
if (dBlock == bBlock) {
if ((t.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) && (cBlock == aBlock)) {
exBlock = aBlock;
}
}
if (exBlock != null) {
// set direction by tracking from c or d
int dir = EntryPoint.UNKNOWN;
Block tBlock = null;
TrackNode tn = new TrackNode(t, HitPointType.SLIP_D, (TrackSegment) t.getConnectD(),
false, LayoutTurnout.STATE_BD);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock());
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb);
}
}
} else {
tn = new TrackNode(t, HitPointType.TURNOUT_B, (TrackSegment) t.getConnectB(),
false, LayoutTurnout.STATE_BD);
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = cUtil.getExitBlockForTrackNode(tn, exBlock.getBlock());
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, lb);
}
}
}
}
if (dir == EntryPoint.UNKNOWN) {
log.error("Block definition ambiguity - cannot determine direction of slip {} in Section {}.",
t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS));
}
return dir;
}
if ((dBlock != bBlock) && containsBlock(dBlock.getBlock()) && containsBlock(bBlock.getBlock())) {
if (getBlockSequenceNumber(dBlock.getBlock()) < getBlockSequenceNumber(bBlock.getBlock())) {
return EntryPoint.FORWARD;
} else {
return EntryPoint.REVERSE;
}
}
if (containsBlock(dBlock.getBlock()) && (!containsBlock(bBlock.getBlock()))) {
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if (containsBlock(bBlock.getBlock()) && (!containsBlock(dBlock.getBlock()))) {
int dir = checkLists(mForwardEntryPoints, mReverseEntryPoints, dBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if (t.getTurnoutType() == LayoutSlip.TurnoutType.DOUBLE_SLIP) {
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, aBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
}
//If all else fails the slip must be in a block of its own so we shall work it out from there.
if (t.getLayoutBlock() != aBlock) {
//Block is not the same as that connected to A
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, aBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if (t.getLayoutBlock() != bBlock) {
//Block is not the same as that connected to B
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, bBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if (t.getLayoutBlock() != cBlock) {
//Block is not the same as that connected to C
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, cBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
if (t.getLayoutBlock() != dBlock) {
//Block is not the same as that connected to D
int dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, dBlock);
if (dir != EntryPoint.UNKNOWN) {
return dir;
}
}
return EntryPoint.UNKNOWN;
}
private boolean placeSensorInCrossover(String b1Name, String b2Name, String c1Name, String c2Name,
int direction, ConnectivityUtil cUtil) {
SignalHead b1Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(b1Name);
SignalHead b2Head = null;
SignalHead c1Head = null;
SignalHead c2Head = null;
boolean success = true;
if ((b2Name != null) && (!b2Name.isEmpty())) {
b2Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(b2Name);
}
if ((c1Name != null) && (!c1Name.isEmpty())) {
c1Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(c1Name);
}
if ((c2Name != null) && (!c2Name.isEmpty())) {
c2Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(c2Name);
}
if (b2Head != null) {
if (!checkDirectionSensor(b1Head, direction, ConnectivityUtil.OVERALL, cUtil)) {
success = false;
}
} else {
if (!checkDirectionSensor(b1Head, direction, ConnectivityUtil.CONTINUING, cUtil)) {
success = false;
}
}
if (c2Head != null) {
if (!checkDirectionSensor(c2Head, direction, ConnectivityUtil.OVERALL, cUtil)) {
success = false;
}
} else if (c1Head != null) {
if (!checkDirectionSensor(c1Head, direction, ConnectivityUtil.DIVERGING, cUtil)) {
success = false;
}
}
return success;
}
private int checkLists(List<EntryPoint> forwardList, List<EntryPoint> reverseList, LayoutBlock lBlock) {
for (int i = 0; i < forwardList.size(); i++) {
if (forwardList.get(i).getFromBlock() == lBlock.getBlock()) {
return EntryPoint.FORWARD;
}
}
for (int i = 0; i < reverseList.size(); i++) {
if (reverseList.get(i).getFromBlock() == lBlock.getBlock()) {
return EntryPoint.REVERSE;
}
}
return EntryPoint.UNKNOWN;
}
@CheckForNull
private Block checkDualDirection(LayoutBlock aBlock, LayoutBlock bBlock, LayoutBlock cBlock) {
for (int i = 0; i < mForwardEntryPoints.size(); i++) {
Block b = mForwardEntryPoints.get(i).getFromBlock();
for (int j = 0; j < mReverseEntryPoints.size(); j++) {
if (mReverseEntryPoints.get(j).getFromBlock() == b) {
// possible dual direction
if (aBlock.getBlock() == b) {
return b;
} else if (bBlock.getBlock() == b) {
return b;
} else if ((cBlock.getBlock() == b) && (aBlock == bBlock)) {
return b;
}
}
}
}
return null;
}
/**
* Returns the direction for proceeding from LayoutBlock b to LayoutBlock a.
* LayoutBlock a must be in the Section. LayoutBlock b may be in this
* Section or may be an Entry Point to the Section.
*/
private int getDirectionForBlocks(LayoutBlock a, LayoutBlock b) {
if (containsBlock(b.getBlock())) {
// both blocks are within this Section
if (getBlockSequenceNumber(a.getBlock()) > getBlockSequenceNumber(b.getBlock())) {
return EntryPoint.FORWARD;
} else {
return EntryPoint.REVERSE;
}
}
// bBlock must be an entry point
for (int i = 0; i < mForwardEntryPoints.size(); i++) {
if (mForwardEntryPoints.get(i).getFromBlock() == b.getBlock()) {
return EntryPoint.FORWARD;
}
}
for (int j = 0; j < mReverseEntryPoints.size(); j++) {
if (mReverseEntryPoints.get(j).getFromBlock() == b.getBlock()) {
return EntryPoint.REVERSE;
}
}
// should never get here
log.error("Unexpected error in getDirectionForBlocks when working with LevelCrossing in Section {}",
getDisplayName(USERSYS));
return EntryPoint.UNKNOWN;
}
/**
* @return 'true' if successfully checked direction sensor by follow
* connectivity from specified track node; 'false' if an error
* occurred
*/
private boolean setDirectionSensorByConnectivity(TrackNode tNode, TrackNode altNode, SignalHead sh,
Block cBlock, ConnectivityUtil cUtil) {
boolean successful = false;
TrackNode tn = tNode;
if ((tn != null) && (sh != null)) {
Block tBlock = null;
LayoutBlock lb;
int dir = EntryPoint.UNKNOWN;
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = (tn == null) ? null : cUtil.getExitBlockForTrackNode(tn, null);
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb);
}
}
} else {
tn = altNode;
while ((tBlock == null) && (tn != null) && (!tn.reachedEndOfTrack())) {
tn = cUtil.getNextNode(tn, 0);
tBlock = (tn == null) ? null : cUtil.getExitBlockForTrackNode(tn, null);
}
if (tBlock != null) {
String userName = tBlock.getUserName();
if (userName != null) {
lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
dir = checkLists(mReverseEntryPoints, mForwardEntryPoints, lb);
if (dir == EntryPoint.REVERSE) {
dir = EntryPoint.FORWARD;
} else if (dir == EntryPoint.FORWARD) {
dir = EntryPoint.REVERSE;
}
}
}
}
}
if (dir != EntryPoint.UNKNOWN) {
if (checkDirectionSensor(sh, dir, ConnectivityUtil.OVERALL, cUtil)) {
successful = true;
}
} else {
log.error("Trouble following track in Block {} in Section {}.",
cBlock.getDisplayName(USERSYS), getDisplayName(USERSYS));
}
}
return successful;
}
/**
* Place direction sensors in SSL for all Signal Heads in this Section if
* the Sensors are not already present in the SSL.
* <p>
* Only anchor point block boundaries that have assigned signals are
* considered. Only turnouts that have assigned signals are considered. Only
* level crossings that have assigned signals are considered. Turnouts and
* anchor points without signals are counted, and reported in warning
* messages during this procedure, if there are any missing signals.
* <p>
* If this method has trouble, an error message is placed in the log
* describing the trouble.
*
* @return the number or errors placing sensors; 1 is returned if no direction sensor is defined for this section
*/
@Override
public int placeDirectionSensors() {
int missingSignalsBB = 0;
int missingSignalsTurnouts = 0;
int missingSignalsLevelXings = 0;
int errorCount = 0;
var editorManager = InstanceManager.getDefault(EditorManager.class);
if (editorManager.getAll(LayoutEditor.class).isEmpty()) {
log.error("No Layout Editor panels on call to 'placeDirectionSensors'");
return 1;
}
if (initializationNeeded) {
initializeBlocks();
}
if ((mForwardBlockingSensorName == null) || (mForwardBlockingSensorName.isEmpty())
|| (mReverseBlockingSensorName == null) || (mReverseBlockingSensorName.isEmpty())) {
log.error("Missing direction sensor in Section {}", getDisplayName(USERSYS));
return 1;
}
LayoutBlockManager layoutBlockManager = InstanceManager.getDefault(LayoutBlockManager.class);
LayoutEditor panel = null;
ConnectivityUtil cUtil = null;
LayoutBlock lBlock = null;
for (Block cBlock : mBlockEntries) {
String userName = cBlock.getUserName();
if (userName == null) {
log.error("No user name for block '{}' in 'placeDirectionSensors'", cBlock);
continue;
}
lBlock = layoutBlockManager.getByUserName(userName);
if (lBlock == null) {
log.error("No layout block for block '{}' in 'placeDirectionSensors'", cBlock.getDisplayName());
continue;
}
// get the panel and cutil for this Block
panel = lBlock.getMaxConnectedPanel();
if (panel == null) {
log.error("Unable to get a panel for '{}' in 'placeDirectionSensors'", cBlock.getDisplayName());
continue;
}
cUtil = new ConnectivityUtil(panel);
List<PositionablePoint> anchorList = cUtil.getAnchorBoundariesThisBlock(cBlock);
for (int j = 0; j < anchorList.size(); j++) {
PositionablePoint p = anchorList.get(j);
if ((!p.getEastBoundSignal().isEmpty()) && (!p.getWestBoundSignal().isEmpty())) {
// have a signalled block boundary
SignalHead sh = cUtil.getSignalHeadAtAnchor(p, cBlock, false);
if (sh == null) {
log.warn("Unexpected missing signal head at boundary of Block {}", cBlock.getDisplayName(USERSYS));
errorCount++;
} else {
int direction = cUtil.getDirectionFromAnchor(mForwardEntryPoints,
mReverseEntryPoints, p);
if (direction == EntryPoint.UNKNOWN) {
// anchor is at a Block boundary within the Section
sh = cUtil.getSignalHeadAtAnchor(p, cBlock, true);
Block otherBlock = ((p.getConnect1()).getLayoutBlock()).getBlock();
if (otherBlock == cBlock) {
otherBlock = ((p.getConnect2()).getLayoutBlock()).getBlock();
}
if (getBlockSequenceNumber(cBlock) < getBlockSequenceNumber(otherBlock)) {
direction = EntryPoint.FORWARD;
} else {
direction = EntryPoint.REVERSE;
}
}
if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
} else {
errorCount++;
missingSignalsBB++;
}
}
List<LevelXing> xingList = cUtil.getLevelCrossingsThisBlock(cBlock);
for (int k = 0; k < xingList.size(); k++) {
LevelXing x = xingList.get(k);
LayoutBlock alBlock = ((TrackSegment) x.getConnectA()).getLayoutBlock();
LayoutBlock blBlock = ((TrackSegment) x.getConnectB()).getLayoutBlock();
LayoutBlock clBlock = ((TrackSegment) x.getConnectC()).getLayoutBlock();
LayoutBlock dlBlock = ((TrackSegment) x.getConnectD()).getLayoutBlock();
if (cUtil.isInternalLevelXingAC(x, cBlock)) {
// have an internal AC level crossing - is it signaled?
if (!x.getSignalAName().isEmpty() || (!x.getSignalCName().isEmpty())) {
// have a signaled AC level crossing internal to this block
if (!x.getSignalAName().isEmpty()) {
// there is a signal at A in the level crossing
TrackNode tn = new TrackNode(x, HitPointType.LEVEL_XING_A,
(TrackSegment) x.getConnectA(), false, 0);
TrackNode altNode = new TrackNode(x, HitPointType.LEVEL_XING_C,
(TrackSegment) x.getConnectC(), false, 0);
SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
x.getSignalAName());
if (!setDirectionSensorByConnectivity(tn, altNode, sh, cBlock, cUtil)) {
errorCount++;
}
}
if (!x.getSignalCName().isEmpty()) {
// there is a signal at C in the level crossing
TrackNode tn = new TrackNode(x, HitPointType.LEVEL_XING_C,
(TrackSegment) x.getConnectC(), false, 0);
TrackNode altNode = new TrackNode(x, HitPointType.LEVEL_XING_A,
(TrackSegment) x.getConnectA(), false, 0);
SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
x.getSignalCName());
if (!setDirectionSensorByConnectivity(tn, altNode, sh, cBlock, cUtil)) {
errorCount++;
}
}
}
} else if (alBlock == lBlock) {
// have a level crossing with AC spanning a block boundary, with A in this Block
int direction = getDirectionForBlocks(alBlock, clBlock);
if (direction != EntryPoint.UNKNOWN) {
if (!x.getSignalCName().isEmpty()) {
SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
x.getSignalCName());
if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
} else {
errorCount++;
}
} else if (clBlock == lBlock) {
// have a level crossing with AC spanning a block boundary, with C in this Block
int direction = getDirectionForBlocks(clBlock, alBlock);
if (direction != EntryPoint.UNKNOWN) {
if (!x.getSignalAName().isEmpty()) {
SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
x.getSignalAName());
if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
} else {
errorCount++;
}
}
if (cUtil.isInternalLevelXingBD(x, cBlock)) {
// have an internal BD level crossing - is it signaled?
if ((!x.getSignalBName().isEmpty()) || (!x.getSignalDName().isEmpty())) {
// have a signaled BD level crossing internal to this block
if (!x.getSignalBName().isEmpty()) {
// there is a signal at B in the level crossing
TrackNode tn = new TrackNode(x, HitPointType.LEVEL_XING_B,
(TrackSegment) x.getConnectB(), false, 0);
TrackNode altNode = new TrackNode(x, HitPointType.LEVEL_XING_D,
(TrackSegment) x.getConnectD(), false, 0);
SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
x.getSignalBName());
if (!setDirectionSensorByConnectivity(tn, altNode, sh, cBlock, cUtil)) {
errorCount++;
}
}
if (!x.getSignalDName().isEmpty()) {
// there is a signal at C in the level crossing
TrackNode tn = new TrackNode(x, HitPointType.LEVEL_XING_D,
(TrackSegment) x.getConnectD(), false, 0);
TrackNode altNode = new TrackNode(x, HitPointType.LEVEL_XING_B,
(TrackSegment) x.getConnectB(), false, 0);
SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
x.getSignalDName());
if (!setDirectionSensorByConnectivity(tn, altNode, sh, cBlock, cUtil)) {
errorCount++;
}
}
}
} else if (blBlock == lBlock) {
// have a level crossing with BD spanning a block boundary, with B in this Block
int direction = getDirectionForBlocks(blBlock, dlBlock);
if (direction != EntryPoint.UNKNOWN) {
if (!x.getSignalDName().isEmpty()) {
SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
x.getSignalDName());
if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
} else {
errorCount++;
}
} else if (dlBlock == lBlock) {
// have a level crossing with BD spanning a block boundary, with D in this Block
int direction = getDirectionForBlocks(dlBlock, blBlock);
if (direction != EntryPoint.UNKNOWN) {
if (!x.getSignalBName().isEmpty()) {
SignalHead sh = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
x.getSignalBName());
if (!checkDirectionSensor(sh, direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
} else {
errorCount++;
}
}
}
List<LayoutTurnout> turnoutList = cUtil.getLayoutTurnoutsThisBlock(cBlock);
for (int m = 0; m < turnoutList.size(); m++) {
LayoutTurnout t = turnoutList.get(m);
if (cUtil.layoutTurnoutHasRequiredSignals(t)) {
// have a signalled turnout
if ((t.getLinkType() == LayoutTurnout.LinkType.NO_LINK)
&& ((t.getTurnoutType() == LayoutTurnout.TurnoutType.RH_TURNOUT)
|| (t.getTurnoutType() == LayoutTurnout.TurnoutType.LH_TURNOUT)
|| (t.getTurnoutType() == LayoutTurnout.TurnoutType.WYE_TURNOUT))) {
// standard turnout - nothing special
// Note: direction is for proceeding from the throat to either other track
int direction = getDirectionStandardTurnout(t, cUtil);
int altDirection = EntryPoint.FORWARD;
if (direction == EntryPoint.FORWARD) {
altDirection = EntryPoint.REVERSE;
}
if (direction == EntryPoint.UNKNOWN) {
errorCount++;
} else {
SignalHead aHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalA1Name());
SignalHead a2Head = null;
String a2Name = t.getSignalA2Name(); // returns "" for empty name, never null
if (!a2Name.isEmpty()) {
a2Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(a2Name);
}
SignalHead bHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalB1Name());
SignalHead cHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalC1Name());
if (t.getLayoutBlock().getBlock() == cBlock) {
// turnout is in this block, set direction sensors on all signal heads
// Note: need allocation to traverse this turnout
if (!checkDirectionSensor(aHead, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (a2Head != null) {
if (!checkDirectionSensor(a2Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
if (!checkDirectionSensor(bHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (!checkDirectionSensor(cHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (((TrackSegment) t.getConnectA()).getLayoutBlock().getBlock() == cBlock) {
// throat Track Segment is in this Block
if (!checkDirectionSensor(bHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (!checkDirectionSensor(cHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else if (((t.getContinuingSense() == Turnout.CLOSED)
&& (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock))
|| ((t.getContinuingSense() == Turnout.THROWN)
&& (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock))) {
// continuing track segment is in this block, normal continuing sense - or -
// diverging track segment is in this block, reverse continuing sense.
if (a2Head == null) {
// single head at throat
if (!checkDirectionSensor(aHead, direction,
ConnectivityUtil.CONTINUING, cUtil)) {
errorCount++;
}
} else {
// two heads at throat
if (!checkDirectionSensor(aHead, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
if (!checkDirectionSensor(bHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else if (((t.getContinuingSense() == Turnout.CLOSED)
&& (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock))
|| ((t.getContinuingSense() == Turnout.THROWN)
&& (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock))) {
// diverging track segment is in this block, normal continuing sense - or -
// continuing track segment is in this block, reverse continuing sense.
if (a2Head == null) {
// single head at throat
if (!checkDirectionSensor(aHead, direction,
ConnectivityUtil.DIVERGING, cUtil)) {
errorCount++;
}
} else {
// two heads at throat
if (!checkDirectionSensor(a2Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
if (!checkDirectionSensor(cHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
}
}
} else if (t.getLinkType() != LayoutTurnout.LinkType.NO_LINK) {
// special linked turnout
LayoutTurnout tLinked = getLayoutTurnoutFromTurnoutName(t.getLinkedTurnoutName(), panel);
if (tLinked == null) {
log.error("null Layout Turnout linked to turnout {}", t.getTurnout().getDisplayName(USERSYS));
} else if (t.getLinkType() == LayoutTurnout.LinkType.THROAT_TO_THROAT) {
SignalHead b1Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalB1Name());
SignalHead b2Head = null;
String hName = t.getSignalB2Name(); // returns "" for empty name, never null
if (!hName.isEmpty()) {
b2Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(hName);
}
SignalHead c1Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalC1Name());
SignalHead c2Head = null;
hName = t.getSignalC2Name(); // returns "" for empty name, never null
if (!hName.isEmpty()) {
c2Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(hName);
}
int direction = getDirectionStandardTurnout(t, cUtil);
int altDirection = EntryPoint.FORWARD;
if (direction == EntryPoint.FORWARD) {
altDirection = EntryPoint.REVERSE;
}
if (direction != EntryPoint.UNKNOWN) {
if (t.getLayoutBlock().getBlock() == cBlock) {
// turnout is in this block, set direction sensors on all signal heads
// Note: need allocation to traverse this turnout
if (!checkDirectionSensor(b1Head, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (b2Head != null) {
if (!checkDirectionSensor(b2Head, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
if (!checkDirectionSensor(c1Head, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (c2Head != null) {
if (!checkDirectionSensor(c2Head, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
} else {
// turnout is not in this block, switch to heads of linked turnout
b1Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
tLinked.getSignalB1Name());
hName = tLinked.getSignalB2Name(); // returns "" for empty name, never null
b2Head = null;
if (!hName.isEmpty()) {
b2Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(hName);
}
c1Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
tLinked.getSignalC1Name());
c2Head = null;
hName = tLinked.getSignalC2Name(); // returns "" for empty name, never null
if (!hName.isEmpty()) {
c2Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(hName);
}
if (((t.getContinuingSense() == Turnout.CLOSED)
&& (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock))
|| ((t.getContinuingSense() == Turnout.THROWN)
&& (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock))) {
// continuing track segment is in this block
if (b2Head != null) {
if (!checkDirectionSensor(b1Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (!checkDirectionSensor(b1Head, direction,
ConnectivityUtil.CONTINUING, cUtil)) {
errorCount++;
}
}
if (c2Head != null) {
if (!checkDirectionSensor(c1Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (!checkDirectionSensor(c1Head, direction,
ConnectivityUtil.CONTINUING, cUtil)) {
errorCount++;
}
}
} else if (((t.getContinuingSense() == Turnout.CLOSED)
&& (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock))
|| ((t.getContinuingSense() == Turnout.THROWN)
&& (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock))) {
// diverging track segment is in this block
if (b2Head != null) {
if (!checkDirectionSensor(b2Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (!checkDirectionSensor(b1Head, direction,
ConnectivityUtil.DIVERGING, cUtil)) {
errorCount++;
}
}
if (c2Head != null) {
if (!checkDirectionSensor(c2Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (!checkDirectionSensor(c1Head, direction,
ConnectivityUtil.DIVERGING, cUtil)) {
errorCount++;
}
}
}
}
}
} else if (t.getLinkType() == LayoutTurnout.LinkType.FIRST_3_WAY) {
SignalHead a1Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalA1Name());
SignalHead a2Head = null;
String hName = t.getSignalA2Name();
if (!hName.isEmpty()) {
a2Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(hName);
}
SignalHead a3Head = null;
hName = t.getSignalA3Name(); // returns "" for empty name, never null
if (!hName.isEmpty()) {
a3Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(hName);
}
SignalHead cHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalC1Name());
int direction = getDirectionStandardTurnout(t, cUtil);
int altDirection = EntryPoint.FORWARD;
if (direction == EntryPoint.FORWARD) {
altDirection = EntryPoint.REVERSE;
}
if (direction != EntryPoint.UNKNOWN) {
if (t.getLayoutBlock().getBlock() == cBlock) {
// turnout is in this block, set direction sensors on all signal heads
// Note: need allocation to traverse this turnout
if (!checkDirectionSensor(a1Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if ((a2Head != null) && (a3Head != null)) {
if (!checkDirectionSensor(a2Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (!checkDirectionSensor(a3Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
if (!checkDirectionSensor(cHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
// turnout is not in this block
if (((TrackSegment) t.getConnectA()).getLayoutBlock().getBlock() == cBlock) {
// throat Track Segment is in this Block
if (!checkDirectionSensor(cHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else if (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock) {
// diverging track segment is in this Block
if (a2Head != null) {
if (!checkDirectionSensor(a2Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (!checkDirectionSensor(a1Head, direction,
ConnectivityUtil.DIVERGING, cUtil)) {
errorCount++;
}
}
}
}
}
} else if (t.getLinkType() == LayoutTurnout.LinkType.SECOND_3_WAY) {
SignalHead bHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalB1Name());
SignalHead cHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
t.getSignalC1Name());
SignalHead a1Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(
tLinked.getSignalA1Name());
SignalHead a3Head = null;
String hName = tLinked.getSignalA3Name();
if (!hName.isEmpty()) {
a3Head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(hName);
}
int direction = getDirectionStandardTurnout(t, cUtil);
int altDirection = EntryPoint.FORWARD;
if (direction == EntryPoint.FORWARD) {
altDirection = EntryPoint.REVERSE;
}
if (direction != EntryPoint.UNKNOWN) {
if (t.getLayoutBlock().getBlock() == cBlock) {
// turnout is in this block, set direction sensors on b and c signal heads
// Note: need allocation to traverse this turnout
if (!checkDirectionSensor(bHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (!checkDirectionSensor(cHead, altDirection,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
if (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock) {
// diverging track segment is in this Block
if (a3Head != null) {
if (!checkDirectionSensor(a3Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
log.warn("Turnout {} - SSL for head {} cannot handle direction sensor for second diverging track.",
tLinked.getTurnout().getDisplayName(USERSYS),( a1Head==null ? "Unknown" : a1Head.getDisplayName(USERSYS)));
errorCount++;
}
} else if (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock) {
// continuing track segment is in this Block
if (a3Head != null) {
if (!checkDirectionSensor(a1Head, direction,
ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (!checkDirectionSensor(a1Head, direction,
ConnectivityUtil.CONTINUING, cUtil)) {
errorCount++;
}
}
}
}
}
} else if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)
|| (t.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER)
|| (t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)) {
// crossover turnout
// Note: direction is for proceeding from A to B (or D to C)
int direction = getDirectionXoverTurnout(t, cUtil);
int altDirection = EntryPoint.FORWARD;
if (direction == EntryPoint.FORWARD) {
altDirection = EntryPoint.REVERSE;
}
if (direction == EntryPoint.UNKNOWN) {
errorCount++;
} else {
if (((TrackSegment) t.getConnectA()).getLayoutBlock().getBlock() == cBlock) {
if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
|| (t.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)) {
if (!placeSensorInCrossover(t.getSignalB1Name(), t.getSignalB2Name(),
t.getSignalC1Name(), t.getSignalC2Name(), altDirection, cUtil)) {
errorCount++;
}
} else {
if (!placeSensorInCrossover(t.getSignalB1Name(), t.getSignalB2Name(),
null, null, altDirection, cUtil)) {
errorCount++;
}
}
}
if (((TrackSegment) t.getConnectB()).getLayoutBlock().getBlock() == cBlock) {
if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
|| (t.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER)) {
if (!placeSensorInCrossover(t.getSignalA1Name(), t.getSignalA2Name(),
t.getSignalD1Name(), t.getSignalD2Name(), direction, cUtil)) {
errorCount++;
}
} else {
if (!placeSensorInCrossover(t.getSignalA1Name(), t.getSignalA2Name(),
null, null, direction, cUtil)) {
errorCount++;
}
}
}
if (((TrackSegment) t.getConnectC()).getLayoutBlock().getBlock() == cBlock) {
if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
|| (t.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER)) {
if (!placeSensorInCrossover(t.getSignalD1Name(), t.getSignalD2Name(),
t.getSignalA1Name(), t.getSignalA2Name(), direction, cUtil)) {
errorCount++;
}
} else {
if (!placeSensorInCrossover(t.getSignalD1Name(), t.getSignalD2Name(),
null, null, direction, cUtil)) {
errorCount++;
}
}
}
if (((TrackSegment) t.getConnectD()).getLayoutBlock().getBlock() == cBlock) {
if ((t.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER)
|| (t.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER)) {
if (!placeSensorInCrossover(t.getSignalC1Name(), t.getSignalC2Name(),
t.getSignalB1Name(), t.getSignalB2Name(), altDirection, cUtil)) {
errorCount++;
}
} else {
if (!placeSensorInCrossover(t.getSignalC1Name(), t.getSignalC2Name(),
null, null, altDirection, cUtil)) {
errorCount++;
}
}
}
}
} else if (t.isTurnoutTypeSlip()) {
int direction = getDirectionSlip(t, cUtil);
int altDirection = EntryPoint.FORWARD;
if (direction == EntryPoint.FORWARD) {
altDirection = EntryPoint.REVERSE;
}
if (direction == EntryPoint.UNKNOWN) {
errorCount++;
} else {
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalA1Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalA2Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (t.getTurnoutType() == LayoutSlip.TurnoutType.SINGLE_SLIP) {
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalB1Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalB1Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalB2Name()), altDirection, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
if (t.getTurnoutType() == LayoutSlip.TurnoutType.SINGLE_SLIP) {
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalC1Name()), direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
} else {
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalC1Name()), direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalC2Name()), direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalD1Name()), direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
if (!checkDirectionSensor(InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(t.getSignalD2Name()), direction, ConnectivityUtil.OVERALL, cUtil)) {
errorCount++;
}
}
} else {
log.error("Unknown turnout type for turnout {} in Section {}.",
t.getTurnout().getDisplayName(USERSYS), getDisplayName(USERSYS));
errorCount++;
}
} else {
// signal heads missing in turnout
missingSignalsTurnouts++;
}
}
}
// set up missing signal head message, if any
if ((missingSignalsBB + missingSignalsTurnouts + missingSignalsLevelXings) > 0) {
String s = "";
if (missingSignalsBB > 0) {
s = ", " + (missingSignalsBB) + " anchor point signal heads missing";
}
if (missingSignalsTurnouts > 0) {
s = ", " + (missingSignalsTurnouts) + " turnouts missing signals";
}
if (missingSignalsLevelXings > 0) {
s = ", " + (missingSignalsLevelXings) + " level crossings missing signals";
}
log.warn("Section - {} {}",getDisplayName(USERSYS),s);
}
return errorCount;
}
private boolean checkDirectionSensor(SignalHead sh, int direction, int where,
ConnectivityUtil cUtil) {
String sensorName = "";
if (direction == EntryPoint.FORWARD) {
sensorName = getForwardBlockingSensorName();
} else if (direction == EntryPoint.REVERSE) {
sensorName = getReverseBlockingSensorName();
}
return (cUtil.addSensorToSignalHeadLogic(sensorName, sh, where));
}
private LayoutTurnout getLayoutTurnoutFromTurnoutName(String turnoutName, LayoutEditor panel) {
Turnout t = InstanceManager.turnoutManagerInstance().getTurnout(turnoutName);
if (t == null) {
return null;
}
for (LayoutTurnout lt : panel.getLayoutTurnouts()) {
if (lt.getTurnout() == t) {
return lt;
}
}
return null;
}
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "was previously marked with @SuppressWarnings, reason unknown")
private List<EntryPoint> getListOfForwardBlockEntryPoints(Block b) {
if (initializationNeeded) {
initializeBlocks();
}
List<EntryPoint> a = new ArrayList<>();
for (int i = 0; i < mForwardEntryPoints.size(); i++) {
if (b == (mForwardEntryPoints.get(i)).getBlock()) {
a.add(mForwardEntryPoints.get(i));
}
}
return a;
}
/**
* Validate the Section. This checks block connectivity, warns of redundant
* EntryPoints, and otherwise checks internal consistency of the Section. An
* appropriate error message is logged if a problem is found. This method
* assumes that Block Paths are correctly initialized.
*
* @return an error description or empty string if there are no errors
*/
@Override
public String validate() {
if (initializationNeeded) {
initializeBlocks();
}
// validate Paths and Bean Settings if a Layout Editor panel is available
for (int i = 0; i < (mBlockEntries.size() - 1); i++) {
Block test = getBlockBySequenceNumber(i);
if (test == null){
log.error("Block {} not found in Block Entries. Paths not checked.",i );
break;
}
String userName = test.getUserName();
if (userName != null) {
LayoutBlock lBlock = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lBlock == null) {
log.error("Layout Block {} not found. Paths not checked.", userName);
} else {
lBlock.updatePaths();
}
}
}
// check connectivity between internal blocks
if (mBlockEntries.size() > 1) {
for (int i = 0; i < (mBlockEntries.size() - 1); i++) {
Block thisBlock = getBlockBySequenceNumber(i);
Block nextBlock = getBlockBySequenceNumber(i + 1);
if ( thisBlock == null || nextBlock == null ) {
return "Sequential blocks " + i + " " + thisBlock + " or "
+ i+1 + " " + nextBlock + " are empty in Block List.";
}
if (!connected(thisBlock, nextBlock)) {
String s = "Sequential Blocks - " + thisBlock.getDisplayName(USERSYS)
+ ", " + nextBlock.getDisplayName(USERSYS)
+ " - are not connected in Section " + getDisplayName(USERSYS) + ".";
return s;
}
if (!connected(nextBlock, thisBlock)) {
String s = "Sequential Blocks - " + thisBlock.getDisplayName(USERSYS)
+ ", " + nextBlock.getDisplayName(USERSYS)
+ " - Paths are not consistent - Section " + getDisplayName(USERSYS) + ".";
return s;
}
}
}
// validate entry points
if ((mForwardEntryPoints.isEmpty()) && (mReverseEntryPoints.isEmpty())) {
String s = "Section " + getDisplayName(USERSYS) + "has no Entry Points.";
return s;
}
if (mForwardEntryPoints.size() > 0) {
for (int i = 0; i < mForwardEntryPoints.size(); i++) {
EntryPoint ep = mForwardEntryPoints.get(i);
if (!containsBlock(ep.getBlock())) {
String s = "Entry Point Block, " + ep.getBlock().getDisplayName(USERSYS)
+ ", is not a Block in Section " + getDisplayName(USERSYS) + ".";
return s;
}
if (!connectsToBlock(ep.getFromBlock())) {
String s = "Entry Point From Block, " + ep.getBlock().getDisplayName(USERSYS)
+ ", is not connected to a Block in Section " + getDisplayName(USERSYS) + ".";
return s;
}
if (!ep.isForwardType()) {
String s = "Direction of FORWARD Entry Point From Block "
+ ep.getFromBlock().getDisplayName(USERSYS) + " to Section "
+ getDisplayName(USERSYS) + " is incorrectly set.";
return s;
}
if (!connected(ep.getBlock(), ep.getFromBlock())) {
String s = "Entry Point Blocks, " + ep.getBlock().getDisplayName(USERSYS)
+ " and " + ep.getFromBlock().getDisplayName(USERSYS)
+ ", are not connected in Section " + getDisplayName(USERSYS) + ".";
return s;
}
}
}
if (mReverseEntryPoints.size() > 0) {
for (int i = 0; i < mReverseEntryPoints.size(); i++) {
EntryPoint ep = mReverseEntryPoints.get(i);
if (!containsBlock(ep.getBlock())) {
String s = "Entry Point Block, " + ep.getBlock().getDisplayName(USERSYS)
+ ", is not a Block in Section " + getDisplayName(USERSYS) + ".";
return s;
}
if (!connectsToBlock(ep.getFromBlock())) {
String s = "Entry Point From Block, " + ep.getBlock().getDisplayName(USERSYS)
+ ", is not connected to a Block in Section " + getDisplayName(USERSYS) + ".";
return s;
}
if (!ep.isReverseType()) {
String s = "Direction of REVERSE Entry Point From Block "
+ ep.getFromBlock().getDisplayName(USERSYS) + " to Section "
+ getDisplayName(USERSYS) + " is incorrectly set.";
return s;
}
if (!connected(ep.getBlock(), ep.getFromBlock())) {
String s = "Entry Point Blocks, " + ep.getBlock().getDisplayName(USERSYS)
+ " and " + ep.getFromBlock().getDisplayName(USERSYS)
+ ", are not connected in Section " + getDisplayName(USERSYS) + ".";
return s;
}
}
}
return "";
}
private boolean connected(Block b1, Block b2) {
if ((b1 != null) && (b2 != null)) {
List<Path> paths = b1.getPaths();
for (int i = 0; i < paths.size(); i++) {
if (paths.get(i).getBlock() == b2) {
return true;
}
}
}
return false;
}
/**
* Set/reset the display to use alternate color for unoccupied blocks in
* this section. If Layout Editor panel is not present, Layout Blocks will
* not be present, and nothing will be set.
*
* @param set true to use alternate unoccupied color; false otherwise
*/
@Override
public void setAlternateColor(boolean set) {
for (Block b : mBlockEntries) {
String userName = b.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
lb.setUseExtraColor(set);
}
}
}
}
/**
* Set/reset the display to use alternate color for unoccupied blocks in
* this Section. If the Section already contains an active block, then the
* alternative color will be set from the active block, if no active block
* is found or we are clearing the alternative color then all the blocks in
* the Section will be set. If Layout Editor panel is not present, Layout
* Blocks will not be present, and nothing will be set.
*
* @param set true to use alternate unoccupied color; false otherwise
*/
@Override
public void setAlternateColorFromActiveBlock(boolean set) {
LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class);
boolean beenSet = false;
if (!set || getState() == FREE || getState() == UNKNOWN) {
setAlternateColor(set);
} else if (getState() == FORWARD) {
for (Block b : mBlockEntries) {
if (b.getState() == Block.OCCUPIED) {
beenSet = true;
}
if (beenSet) {
String userName = b.getUserName();
if (userName != null) {
LayoutBlock lb = lbm.getByUserName(userName);
if (lb != null) {
lb.setUseExtraColor(set);
}
}
}
}
} else if (getState() == REVERSE) {
for (Block b : mBlockEntries) {
if (b.getState() == Block.OCCUPIED) {
beenSet = true;
}
if (beenSet) {
String userName = b.getUserName();
if (userName != null) {
LayoutBlock lb = lbm.getByUserName(userName);
if (lb != null) {
lb.setUseExtraColor(set);
}
}
}
}
}
if (!beenSet) {
setAlternateColor(set);
}
}
/**
* Set the block values for blocks in this Section.
*
* @param name the value to set all blocks to
*/
@Override
public void setNameInBlocks(String name) {
for (Block b : mBlockEntries) {
b.setValue(name);
}
}
/**
* Set the block values for blocks in this Section.
*
* @param value the name to set block values to
*/
@Override
public void setNameInBlocks(Object value) {
for (Block b : mBlockEntries) {
b.setValue(value);
}
}
@Override
public void setNameFromActiveBlock(Object value) {
boolean beenSet = false;
if (value == null || getState() == FREE || getState() == UNKNOWN) {
setNameInBlocks(value);
} else if (getState() == FORWARD) {
for (Block b : mBlockEntries) {
if (b.getState() == Block.OCCUPIED) {
beenSet = true;
}
if (beenSet) {
b.setValue(value);
}
}
} else if (getState() == REVERSE) {
for (Block b : mBlockEntries) {
if (b.getState() == Block.OCCUPIED) {
beenSet = true;
}
if (beenSet) {
b.setValue(value);
}
}
}
if (!beenSet) {
setNameInBlocks(value);
}
}
/**
* Clear the block values for blocks in this Section.
*/
@Override
public void clearNameInUnoccupiedBlocks() {
for (Block b : mBlockEntries) {
if (b.getState() == Block.UNOCCUPIED) {
b.setValue(null);
}
}
}
/**
* Suppress the update of a memory variable when a block goes to unoccupied,
* so the text set above doesn't get wiped out.
*
* @param set true to suppress the update; false otherwise
*/
@Override
public void suppressNameUpdate(boolean set) {
for (Block b : mBlockEntries) {
String userName = b.getUserName();
if (userName != null) {
LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getByUserName(userName);
if (lb != null) {
lb.setSuppressNameUpdate(set);
}
}
}
}
private SectionType sectionType = USERDEFINED;
/**
* Set Section Type.
* <ul>
* <li>USERDEFINED - Default Save all the information.
* <li>SIGNALMASTLOGIC - Save only the name, blocks will be added by the SignalMast logic.
* <li>DYNAMICADHOC - Created on an as required basis, not to be saved.
* </ul>
* @param type constant of section type.
*/
@Override
public void setSectionType(SectionType type) {
sectionType = type;
}
/**
* Get Section Type.
* Defaults to USERDEFINED.
* @return constant of section type.
*/
@Override
public SectionType getSectionType() {
return sectionType;
}
@Override
public String getBeanType() {
return Bundle.getMessage("BeanNameSection");
}
@Override
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
if ("CanDelete".equals(evt.getPropertyName())) { // NOI18N
NamedBean nb = (NamedBean) evt.getOldValue();
if (nb instanceof Sensor) {
if (nb.equals(getForwardBlockingSensor())) {
PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null);
throw new PropertyVetoException(Bundle.getMessage("VetoBlockingSensor", nb.getBeanType(), Bundle.getMessage("Forward"), Bundle.getMessage("Blocking"), getDisplayName()), e); // NOI18N
}
if (nb.equals(getForwardStoppingSensor())) {
PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null);
throw new PropertyVetoException(Bundle.getMessage("VetoBlockingSensor", nb.getBeanType(), Bundle.getMessage("Forward"), Bundle.getMessage("Stopping"), getDisplayName()), e);
}
if (nb.equals(getReverseBlockingSensor())) {
PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null);
throw new PropertyVetoException(Bundle.getMessage("VetoBlockingSensor", nb.getBeanType(), Bundle.getMessage("Reverse"), Bundle.getMessage("Blocking"), getDisplayName()), e);
}
if (nb.equals(getReverseStoppingSensor())) {
PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null);
throw new PropertyVetoException(Bundle.getMessage("VetoBlockingSensor", nb.getBeanType(), Bundle.getMessage("Reverse"), Bundle.getMessage("Stopping"), getDisplayName()), e);
}
}
if (nb instanceof Block) {
Block check = (Block)nb;
if (getBlockList().contains(check)) {
PropertyChangeEvent e = new PropertyChangeEvent(this, "DoNotDelete", null, null);
throw new PropertyVetoException(Bundle.getMessage("VetoBlockInSection", getDisplayName()), e);
}
}
}
// "DoDelete" case, if needed, should be handled here.
}
@Override
public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) {
List<NamedBeanUsageReport> report = new ArrayList<>();
if (bean != null) {
getBlockList().forEach((block) -> {
if (bean.equals(block)) {
report.add(new NamedBeanUsageReport("SectionBlock"));
}
});
if (bean.equals(getForwardBlockingSensor())) {
report.add(new NamedBeanUsageReport("SectionSensorForwardBlocking"));
}
if (bean.equals(getForwardStoppingSensor())) {
report.add(new NamedBeanUsageReport("SectionSensorForwardStopping"));
}
if (bean.equals(getReverseBlockingSensor())) {
report.add(new NamedBeanUsageReport("SectionSensorReverseBlocking"));
}
if (bean.equals(getReverseStoppingSensor())) {
report.add(new NamedBeanUsageReport("SectionSensorReverseStopping"));
}
}
return report;
}
private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultSection.class);
}