001package jmri.jmrit.display.layoutEditor; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.List; 006import jmri.BeanSetting; 007import jmri.Path; 008import jmri.Turnout; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * LayoutEditorAuxTools provides tools making use of layout connectivity 014 * available in Layout Editor panels. (More tools are in 015 * LayoutEditorTools.java.) 016 * <p> 017 * This module manages block connectivity for its associated LayoutEditor. 018 * <p> 019 * A single object of this type, obtained via {@link LayoutEditor#getLEAuxTools()} 020 * is shared across all instances of {@link ConnectivityUtil}. 021 * <p> 022 * The tools in this module are accessed via the Tools menu in Layout Editor, or 023 * directly from LayoutEditor or LayoutEditor specific modules. 024 * 025 * @author Dave Duchamp Copyright (c) 2008 026 * @author George Warner Copyright (c) 2017-2018 027 */ 028final public class LayoutEditorAuxTools { 029 // constants 030 031 // operational instance variables 032 final private LayoutModels models; 033 final private List<LayoutConnectivity> cList = new ArrayList<>(); // LayoutConnectivity list 034 private boolean blockConnectivityChanged = false; // true if block connectivity may have changed 035 private boolean initialized = false; 036 037 // constructor method 038 public LayoutEditorAuxTools(LayoutModels theModels) { 039 models = theModels; 040 } 041 042 // register a change in block connectivity that may require an update of connectivity list 043 public void setBlockConnectivityChanged() { 044 blockConnectivityChanged = true; 045 } 046 047 /** 048 * Get Connectivity involving a specific Layout Block. 049 * <p> 050 * This routine returns an ArrayList of BlockConnectivity objects involving 051 * the specified LayoutBlock. 052 * @param blk the layout block. 053 * @return the layout connectivity list, not null. 054 */ 055 public List<LayoutConnectivity> getConnectivityList(LayoutBlock blk) { 056 if (!initialized) { 057 initializeBlockConnectivity(); 058 } 059 if (blockConnectivityChanged) { 060 updateBlockConnectivity(); 061 } 062 List<LayoutConnectivity> retList = new ArrayList<>(); 063 for (LayoutConnectivity lc : cList) { 064 if ((lc.getBlock1() == blk) || (lc.getBlock2() == blk)) { 065 retList.add(lc); 066 } 067 } 068 return (retList); 069 } 070 071 /** 072 * Initializes the block connectivity (block boundaries) for a Layout Editor 073 * panel. 074 * <p> 075 * This routine sets up the LayoutConnectivity objects needed to show the 076 * current connectivity. It gets its information from arrays contained in 077 * LayoutEditor. 078 * <p> 079 * One LayoutConnectivity object is created for each block boundary -- 080 * connection points where two blocks join. Block boundaries can occur where 081 * ever a track segment in one block joins with: 1) a track segment in 082 * another block -OR- 2) a connection point in a layout turnout in another 083 * block -OR- 3) a connection point in a level crossing in another block. 084 * <p> 085 * The first block is always a track segment. The direction set in the 086 * LayoutConnectivity is the direction of the track segment alone for cases 087 * 2) and 3) above. For case 1), two track segments, the direction reflects 088 * an "average" over the two track segments. See LayoutConnectivity for the 089 * allowed values of direction. 090 * <p> 091 * Normally the initialization only occurs once after the panel is loaded. When edge 092 * connectors are used, an incomplete LayoutConnectivity table can occur due to the 093 * panel loading sequence and the complexity of edge connector relationships. 094 * An additional initialization is allowed to build the final LayoutConnectivity table. 095 */ 096 public void initializeBlockConnectivity() { 097 cList.clear(); 098 List<LayoutConnectivity> lcs = null; 099 100 for (LayoutTrackView ltv : models.getLayoutTrackViews()) { 101 if ((ltv instanceof PositionablePointView) // effectively, skip LevelXing and LayoutTurntable - why? 102 || (ltv instanceof TrackSegmentView) 103 || (ltv instanceof LayoutTurnoutView)) { // <== includes Wye. LayoutSlips, XOvers 104 lcs = ltv.getLayoutConnectivity(); 105 cList.addAll(lcs); // append to list 106 } 107 } 108 initialized = true; 109 } // initializeBlockConnectivity 110 111 /** 112 * Updates the block connectivity (block boundaries) for a Layout Editor 113 * panel after changes may have been made. 114 */ 115 private void updateBlockConnectivity() { 116 int sz = cList.size(); 117 boolean[] found = new boolean[sz]; 118 Arrays.fill(found, false); 119 120 List<LayoutConnectivity> lcs = null; 121 122 // Check for block boundaries at positionable points. 123 for (PositionablePoint p : models.getPositionablePoints()) { 124 lcs = p.getLayoutConnectivity(); 125 for (LayoutConnectivity lc : lcs) { 126 // add to list, if not already present 127 checkConnectivity(lc, found); 128 } 129 } 130 131 // Check for block boundaries at layout turnouts and level crossings 132 for (TrackSegment ts : models.getTrackSegments()) { 133 lcs = ts.getLayoutConnectivity(); 134 for (LayoutConnectivity lc : lcs) { 135 // add to list, if not already present 136 checkConnectivity(lc, found); 137 } 138 } 139 140 // check for block boundaries internal to crossover turnouts 141 for (LayoutTurnout lt : models.getLayoutTurnouts()) { 142 lcs = lt.getLayoutConnectivity(); 143 for (LayoutConnectivity lc : lcs) { 144 // add to list, if not already present 145 checkConnectivity(lc, found); 146 } 147 } 148 149 // check for block boundaries internal to slips 150 for (LayoutSlip ls : models.getLayoutSlips()) { 151 lcs = ls.getLayoutConnectivity(); 152 for (LayoutConnectivity lc : lcs) { 153 // add to list, if not already present 154 checkConnectivity(lc, found); 155 } 156 } 157 158 // delete any LayoutConnectivity objects no longer needed 159 for (int i = sz - 1; i >= 0; i--) { 160 if (!found[i]) { 161 // djd debugging - message to list connectivity being removed 162 // LayoutConnectivity xx = (LayoutConnectivity)cList.get(i); 163 // log.error(" Deleting Layout Connectivity - " + xx.getBlock1().getId() + ", " + xx.getBlock2().getId()); 164 // end debugging 165 cList.remove(i); 166 } 167 } 168 blockConnectivityChanged = false; 169 } // updateBlockConnectivity 170 171 // 172 private void checkConnectivity(LayoutConnectivity c, boolean[] found) { 173 // initialize input LayoutConnectivity components 174 LayoutBlock blk1 = c.getBlock1(); 175 LayoutBlock blk2 = c.getBlock2(); 176 177 int dir = c.getDirection(); 178 int rDir = c.getReverseDirection(); 179 180 TrackSegment track = c.getTrackSegment(); 181 LayoutTrack connected = c.getConnectedObject(); 182 HitPointType type = c.getConnectedType(); 183 184 LayoutTurnout xOver = c.getXover(); 185 int xOverType = c.getXoverBoundaryType(); 186 187 // loop over connectivity list, looking for this layout connectivity 188 for (int i = 0; i < cList.size(); i++) { 189 LayoutConnectivity lc = cList.get(i); 190 // compare input LayoutConnectivity with LayoutConnectivity from the list 191 if (xOver == null) { 192 // not a crossover block boundary 193 if ((blk1 == lc.getBlock1()) && (blk2 == lc.getBlock2()) && (track == lc.getTrackSegment()) 194 && (connected == lc.getConnectedObject()) && (type == lc.getConnectedType()) 195 && (dir == lc.getDirection())) { 196 found[i] = true; 197 break; 198 } 199 } else { 200 // boundary is in a crossover turnout 201 if ((xOver == lc.getXover()) && (xOverType == lc.getXoverBoundaryType())) { 202 if ((blk1 == lc.getBlock1()) && (blk2 == lc.getBlock2()) && (dir == lc.getDirection())) { 203 found[i] = true; 204 break; 205 } else if ((blk2 == lc.getBlock1()) && (blk1 == lc.getBlock2()) && (rDir == lc.getDirection())) { 206 found[i] = true; 207 break; 208 } 209 } 210 } 211 } 212 213 // Check to see if this connectivity is already in the list 214 // This occurs for the first layout editor panel when there 215 // are multiple panels connected by edge connectors. 216 if (cList.contains(c)) { 217 log.debug("checkConnectivity: Duplicate connection: '{}'", c); // NOI18N 218 } else { 219 cList.add(c); 220 } 221 } // checkConnectivity 222 223 /** 224 * Searches for and adds BeanSetting's to a Path as needed. 225 * <p> 226 * This method starts at the entry point to the LayoutBlock given in the 227 * Path at the block boundary specified in the LayoutConnectivity. It 228 * follows the track looking for turnout settings that are required for a 229 * train entering on this block boundary point to exit the block. If a 230 * required turnout setting is found, the turnout and its required state are 231 * used to create a BeanSetting, which is added to the Path. Such a setting 232 * can occur, for example, if a track enters a right-handed turnout from 233 * either the diverging track or the continuing track. 234 * <p> 235 * If the track branches into two tracks (for example, by entering a 236 * right-handed turnout via the throat track), the search is stopped. The 237 * search is also stopped when the track reaches a different block (or an 238 * undefined block), or reaches an end bumper. 239 * @param p path to follow until branch. 240 * @param lc layout connectivity. 241 * @param layoutBlock the layout block. 242 */ 243 public void addBeanSettings(Path p, LayoutConnectivity lc, LayoutBlock layoutBlock) { 244 p.clearSettings(); 245 LayoutTrack curConnection = null; 246 LayoutTrack prevConnection = null; 247 HitPointType typeCurConnection = HitPointType.NONE; 248 BeanSetting bs = null; 249 LayoutTurnout lt = null; 250 // process track at block boundary 251 if (lc.getBlock1() == layoutBlock) { // block1 is this LayoutBlock 252 curConnection = lc.getTrackSegment(); 253 if (curConnection != null) { // connected track in this block is a track segment 254 prevConnection = lc.getConnectedObject(); 255 typeCurConnection = HitPointType.TRACK; 256 // is this Track Segment connected to a RH, LH, or WYE turnout at the continuing or diverging track? 257 if ((lc.getConnectedType() == HitPointType.TURNOUT_B 258 || lc.getConnectedType() == HitPointType.TURNOUT_C) 259 && ((LayoutTurnout) prevConnection).getTurnoutType() != LayoutTurnout.TurnoutType.NONE 260 && LayoutTurnout.hasEnteringSingleTrack(((LayoutTurnout) prevConnection).getTurnoutType())) { 261 LayoutTurnout ltx = (LayoutTurnout) prevConnection; 262 // Track Segment connected to continuing track of turnout? 263 if (lc.getConnectedType() == HitPointType.TURNOUT_B) { 264 Turnout ltxto = ltx.getTurnout(); 265 if ( ltxto != null) { 266 bs = new BeanSetting(ltxto, ltx.getTurnoutName(), ltx.getContinuingSense()); 267 p.addSetting(bs); 268 } else { 269 log.error("No assigned turnout (A): LTO = {}, blk = {}", ltx.getName(), ltx.getLayoutBlock().getDisplayName()); // NOI18N 270 } 271 } else if (lc.getConnectedType() == HitPointType.TURNOUT_C) { 272 // is Track Segment connected to diverging track of turnout? 273 Turnout ltxto = ltx.getTurnout(); 274 if (ltxto != null) { 275 if (ltx.getContinuingSense() == Turnout.CLOSED) { 276 bs = new BeanSetting(ltxto, ltx.getTurnoutName(), Turnout.THROWN); 277 } else { 278 bs = new BeanSetting(ltxto, ltx.getTurnoutName(), Turnout.CLOSED); 279 } 280 p.addSetting(bs); 281 } else { 282 log.error("No assigned turnout (B): LTO = {}, blk = {}", ltx.getName(), ltx.getLayoutBlock().getDisplayName()); // NOI18N 283 } 284 } else { 285 log.warn("Did not decode lc.getConnectedType() of {}", lc.getConnectedType()); // NOI18N 286 } 287 } // is this Track Segment connected to the continuing track of a RH_XOVER or LH_XOVER? 288 else if (HitPointType.isTurnoutHitType(lc.getConnectedType()) 289 && ((((LayoutTurnout) prevConnection).getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER) 290 || (((LayoutTurnout) prevConnection).getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER))) { 291 LayoutTurnout ltz = (LayoutTurnout) prevConnection; 292 if (((ltz.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER) 293 && ((lc.getConnectedType() == HitPointType.TURNOUT_B) 294 || (lc.getConnectedType() == HitPointType.TURNOUT_D))) 295 || ((ltz.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER) 296 && ((lc.getConnectedType() == HitPointType.TURNOUT_A) 297 || (lc.getConnectedType() == HitPointType.TURNOUT_C)))) { 298 299 Turnout ltzto = ltz.getTurnout(); 300 if (ltzto != null) { 301 bs = new BeanSetting(ltzto, ltz.getTurnoutName(), Turnout.CLOSED); 302 p.addSetting(bs); 303 } else { 304 log.error("No assigned turnout (C): LTO = {}, blk = {}, TO type = {}, conn type = {}", // NOI18N 305 ltz.getName(), ltz.getLayoutBlock().getDisplayName(), ltz.getTurnoutType(), lc.getConnectedType()); 306 } 307 } 308 } // is this track section is connected to a slip? 309 else if (HitPointType.isSlipHitType(lc.getConnectedType())) { 310 LayoutSlip lsz = (LayoutSlip) prevConnection; 311 if (lsz.getSlipType() == LayoutSlip.TurnoutType.SINGLE_SLIP) { 312 if (lc.getConnectedType() == HitPointType.SLIP_C) { 313 Turnout lszto = lsz.getTurnout(); 314 if (lszto != null) { 315 bs = new BeanSetting(lszto, lsz.getTurnoutName(), lsz.getTurnoutState(LayoutTurnout.STATE_AC)); 316 p.addSetting(bs); 317 } else { 318 log.error("No assigned turnout (D): LTO = {}, blk = {}", lsz.getName(), lsz.getLayoutBlock().getDisplayName()); // NOI18N 319 } 320 Turnout lsztob = lsz.getTurnoutB(); 321 if (lsztob != null) { 322 bs = new BeanSetting(lsztob, lsz.getTurnoutBName(), lsz.getTurnoutBState(LayoutTurnout.STATE_AC)); 323 p.addSetting(bs); 324 } else { 325 log.error("No assigned turnoutB (E): LTO = {}, blk = {}", lsz.getName(), lsz.getLayoutBlock().getDisplayName()); // NOI18N 326 } 327 } else if (lc.getConnectedType() == HitPointType.SLIP_B) { 328 Turnout lszto = lsz.getTurnout(); 329 if (lszto != null) { 330 bs = new BeanSetting(lszto, lsz.getTurnoutName(), lsz.getTurnoutState(LayoutTurnout.STATE_BD)); 331 p.addSetting(bs); 332 } else { 333 log.error("No assigned turnout (F): LTO = {}, blk = {}", lsz.getName(), lsz.getLayoutBlock().getDisplayName()); // NOI18N 334 } 335 336 Turnout lsztob = lsz.getTurnoutB(); 337 if (lsztob != null) { 338 bs = new BeanSetting(lsztob, lsz.getTurnoutBName(), lsz.getTurnoutBState(LayoutTurnout.STATE_BD)); 339 p.addSetting(bs); 340 } else { 341 log.error("No assigned turnoutB (G): LTO = {}, blk = {}", lsz.getName(), lsz.getLayoutBlock().getDisplayName()); // NOI18N 342 } 343 } else if (lc.getConnectedType() == HitPointType.SLIP_A) { 344 log.debug("At connection A of a single slip which could go in two different directions"); // NOI18N 345 } else if (lc.getConnectedType() == HitPointType.SLIP_D) { 346 log.debug("At connection D of a single slip which could go in two different directions"); // NOI18N 347 } 348 } else { 349 //note: I'm adding these logs as a prequel to adding the correct code for double slips 350 if (lc.getConnectedType() == HitPointType.SLIP_A) { 351 log.debug("At connection A of a double slip which could go in two different directions"); // NOI18N 352 } else if (lc.getConnectedType() == HitPointType.SLIP_B) { 353 log.debug("At connection B of a double slip which could go in two different directions"); // NOI18N 354 } else if (lc.getConnectedType() == HitPointType.SLIP_C) { 355 log.debug("At connection C of a double slip which could go in two different directions"); // NOI18N 356 } else if (lc.getConnectedType() == HitPointType.SLIP_D) { 357 log.debug("At connection D of a double slip which could go in two different directions"); // NOI18N 358 } else { // this should NEVER happen (it should always be SLIP_A, _B, _C or _D. 359 log.info("At a double slip we could go in two different directions"); // NOI18N 360 } 361 } 362 } 363 } else { 364 // block boundary is internal to a crossover turnout 365 lt = lc.getXover(); 366 prevConnection = lt; 367 if ((lt != null) && (lt.getTurnout() != null)) { 368 int type = lc.getXoverBoundaryType(); 369 // bs is known to be null at this point 370 if (lt.getTurnout() != null) { 371 if (type == LayoutConnectivity.XOVER_BOUNDARY_AB) { 372 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 373 curConnection = lt.getConnectA(); 374 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_CD) { 375 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 376 curConnection = lt.getConnectC(); 377 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_AC) { 378 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN); 379 curConnection = lt.getConnectA(); 380 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_BD) { 381 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN); 382 curConnection = lt.getConnectB(); 383 } else { 384 log.warn("failed to decode lc.getXoverBoundaryType() of {} (A)", lc.getXoverBoundaryType()); // NOI18N 385 } 386 } 387 typeCurConnection = HitPointType.TRACK; 388 if (bs != null) { 389 p.addSetting(bs); 390 } else { 391 log.error("No assigned turnout (H): LTO = {}, blk = {}, type = {}", lt.getName(), lt.getLayoutBlock().getDisplayName(), type); // NOI18N 392 } 393 } 394 } 395 } else if (lc.getXover() != null) { 396 // first Block is not in a Track Segment, must be block boundary internal to a crossover turnout 397 lt = lc.getXover(); 398 if ((lt != null) && (lt.getTurnout() != null)) { 399 int type = lc.getXoverBoundaryType(); 400 // bs is known to be null at this point 401 if (lt.getTurnout() != null) { 402 if (type == LayoutConnectivity.XOVER_BOUNDARY_AB) { 403 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 404 curConnection = lt.getConnectB(); 405 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_CD) { 406 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 407 curConnection = lt.getConnectD(); 408 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_AC) { 409 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN); 410 curConnection = lt.getConnectC(); 411 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_BD) { 412 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN); 413 curConnection = lt.getConnectD(); 414 } else { 415 log.warn("failed to decode lc.getXoverBoundaryType() of {} (B)", lc.getXoverBoundaryType()); // NOI18N 416 } 417 } 418 typeCurConnection = HitPointType.TRACK; 419 if (bs != null) { 420 p.addSetting(bs); 421 } else { 422 log.error("No assigned turnout (I): LTO = {}, blk = {}, type = {}", lt.getName(), lt.getLayoutBlock().getDisplayName(), type); // NOI18N 423 } 424 } 425 } else { 426 // block2 is this LayoutBlock, and block1 is in a track segment 427 if (lc.getConnectedObject() != null) { 428 // connected object in this block is a turnout or levelxing 429 curConnection = lc.getConnectedObject(); 430 prevConnection = lc.getTrackSegment(); 431 typeCurConnection = lc.getConnectedType(); 432 if (HitPointType.isTurnoutHitType(typeCurConnection)) { 433 // connected object is a turnout 434 LayoutTurnout.TurnoutType turnoutType = ((LayoutTurnout) curConnection).getTurnoutType(); 435 if (LayoutTurnout.hasEnteringDoubleTrack(turnoutType)) { 436 // have crossover turnout 437 if ((turnoutType == LayoutTurnout.TurnoutType.DOUBLE_XOVER) 438 || ((turnoutType == LayoutTurnout.TurnoutType.RH_XOVER) && ((typeCurConnection == HitPointType.TURNOUT_A) || (typeCurConnection == HitPointType.TURNOUT_C))) 439 || ((turnoutType == LayoutTurnout.TurnoutType.LH_XOVER) && ((typeCurConnection == HitPointType.TURNOUT_B) || (typeCurConnection == HitPointType.TURNOUT_D)))) { 440 // entering turnout at a throat, cannot follow path any further 441 curConnection = null; 442 } else { 443 // entering turnout at continuing track 444 if (((LayoutTurnout) curConnection).getTurnout() != null) { 445 bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.CLOSED); 446 p.addSetting(bs); 447 } else { 448 log.error("No assigned turnout (J): LTO = {}, blk = {}", // NOI18N 449 curConnection.getName(), 450 ((LayoutTurnout) curConnection).getLayoutBlock().getDisplayName()); 451 } 452 prevConnection = curConnection; 453 if (typeCurConnection == HitPointType.TURNOUT_A) { 454 curConnection = ((LayoutTurnout) curConnection).getConnectB(); 455 } else if (typeCurConnection == HitPointType.TURNOUT_B) { 456 curConnection = ((LayoutTurnout) curConnection).getConnectA(); 457 } else if (typeCurConnection == HitPointType.TURNOUT_C) { 458 curConnection = ((LayoutTurnout) curConnection).getConnectD(); 459 } else { // typeCurConnection == LayoutEditor.HitPointTypes.TURNOUT_D per if statement 3 levels up 460 curConnection = ((LayoutTurnout) curConnection).getConnectC(); 461 } 462 typeCurConnection = HitPointType.TRACK; 463 } 464 } // must be RH, LH, or WYE turnout 465 else if (typeCurConnection == HitPointType.TURNOUT_A) { 466 // turnout throat, no bean setting needed and cannot follow Path any further 467 log.debug("At connection A of a turnout which could go in two different directions"); // NOI18N 468 curConnection = null; 469 } else if (typeCurConnection == HitPointType.TURNOUT_B) { 470 // continuing track of turnout 471 if (((LayoutTurnout) curConnection).getTurnout() != null) { 472 if (((LayoutTurnout) curConnection).getContinuingSense() == Turnout.CLOSED) { 473 bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.CLOSED); 474 } else { 475 bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.THROWN); 476 } 477 p.addSetting(bs); 478 } else { 479 log.error("No assigned turnout (K): LTO = {}, blk = {}", // NOI18N 480 curConnection.getName(), 481 ((LayoutTurnout) curConnection).getLayoutBlock().getDisplayName()); 482 } 483 prevConnection = curConnection; 484 curConnection = ((LayoutTurnout) curConnection).getConnectA(); 485 typeCurConnection = HitPointType.TRACK; 486 } else if (typeCurConnection == HitPointType.TURNOUT_C) { 487 // diverging track of turnout 488 if (((LayoutTurnout) curConnection).getTurnout() != null) { 489 if (((LayoutTurnout) curConnection).getContinuingSense() == Turnout.CLOSED) { 490 bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.THROWN); 491 } else { 492 bs = new BeanSetting(((LayoutTurnout) curConnection).getTurnout(), ((LayoutTurnout) curConnection).getTurnoutName(), Turnout.CLOSED); 493 } 494 p.addSetting(bs); 495 } else { 496 log.error("No assigned turnout (L): LTO = {}, blk = {}", // NOI18N 497 curConnection.getName(), 498 ((LayoutTurnout) curConnection).getLayoutBlock().getDisplayName()); 499 } 500 prevConnection = curConnection; 501 curConnection = ((LayoutTurnout) curConnection).getConnectA(); 502 typeCurConnection = HitPointType.TRACK; 503 } 504 } // if level crossing, skip to the connected track segment on opposite side 505 else if (typeCurConnection == HitPointType.LEVEL_XING_A) { 506 prevConnection = curConnection; 507 curConnection = ((LevelXing) curConnection).getConnectC(); 508 typeCurConnection = HitPointType.TRACK; 509 } else if (typeCurConnection == HitPointType.LEVEL_XING_C) { 510 prevConnection = curConnection; 511 curConnection = ((LevelXing) curConnection).getConnectA(); 512 typeCurConnection = HitPointType.TRACK; 513 } else if (typeCurConnection == HitPointType.LEVEL_XING_B) { 514 prevConnection = curConnection; 515 curConnection = ((LevelXing) curConnection).getConnectD(); 516 typeCurConnection = HitPointType.TRACK; 517 } else if (typeCurConnection == HitPointType.LEVEL_XING_D) { 518 prevConnection = curConnection; 519 curConnection = ((LevelXing) curConnection).getConnectB(); 520 typeCurConnection = HitPointType.TRACK; 521 } 522 } else { 523 // block boundary is internal to a crossover turnout 524 lt = lc.getXover(); 525 prevConnection = lt; 526 if ((lt != null) && (lt.getTurnout() != null)) { 527 int type = lc.getXoverBoundaryType(); 528 // bs is known to be null at this point 529 if (lt.getTurnout() != null) { 530 if (type == LayoutConnectivity.XOVER_BOUNDARY_AB) { 531 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 532 curConnection = lt.getConnectB(); 533 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_CD) { 534 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 535 curConnection = lt.getConnectD(); 536 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_AC) { 537 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN); 538 curConnection = lt.getConnectC(); 539 } else if (type == LayoutConnectivity.XOVER_BOUNDARY_BD) { 540 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN); 541 curConnection = lt.getConnectD(); 542 } 543 } 544 typeCurConnection = HitPointType.TRACK; 545 if (bs != null) { 546 p.addSetting(bs); 547 } else { 548 log.error("No assigned turnout (Q): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName()); // NOI18N 549 } 550 } 551 } 552 } 553 // follow path through this block - done when reaching another block, or a branching of Path 554 while (curConnection != null) { 555 if (typeCurConnection == HitPointType.TRACK) { 556 TrackSegment curTS = (TrackSegment) curConnection; 557 // track segment is current connection 558 if (curTS.getLayoutBlock() != layoutBlock) { 559 curConnection = null; 560 } else { 561 // skip over to other end of Track Segment 562 if (curTS.getConnect1() == prevConnection) { 563 prevConnection = curConnection; 564 typeCurConnection = curTS.getType2(); 565 curConnection = curTS.getConnect2(); 566 } else { 567 prevConnection = curConnection; 568 typeCurConnection = curTS.getType1(); 569 curConnection = curTS.getConnect1(); 570 } 571 // skip further if positionable point (possible anchor point) 572 if (typeCurConnection == HitPointType.POS_POINT) { 573 PositionablePoint pt = (PositionablePoint) curConnection; 574 if (pt.getType() == PositionablePoint.PointType.END_BUMPER) { 575 // reached end of track 576 curConnection = null; 577 } else { 578 // at an anchor point, find track segment on other side 579 TrackSegment track = null; 580 if (pt.getConnect1() == prevConnection) { 581 track = pt.getConnect2(); 582 } else { 583 track = pt.getConnect1(); 584 } 585 // check for block boundary 586 if ((track == null) || (track.getLayoutBlock() != layoutBlock)) { 587 // moved outside of block - anchor point was a block boundary -OR- 588 // reached the end of the defined track 589 curConnection = null; 590 } else { 591 prevConnection = curConnection; 592 curConnection = track; 593 typeCurConnection = HitPointType.TRACK; 594 } 595 } 596 } 597 } 598 } else if (HitPointType.isTurnoutHitType(typeCurConnection)) { 599 lt = (LayoutTurnout) curConnection; 600 // test for crossover turnout 601 if (lt.hasEnteringSingleTrack()) { 602 // have RH, LH, or WYE turnout 603 604 if (lt.getLayoutBlock() != layoutBlock) { 605 curConnection = null; 606 } else { 607 // turnout is in current block, test connection point 608 if (typeCurConnection == HitPointType.TURNOUT_A) { 609 // turnout throat, no bean setting needed and cannot follow possible path any further 610 curConnection = null; 611 } else if (typeCurConnection == HitPointType.TURNOUT_B) { 612 // continuing track of turnout, add a bean setting 613 if (lt.getTurnout() != null) { 614 if (lt.getContinuingSense() == Turnout.CLOSED) { 615 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 616 } else { 617 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN); 618 } 619 p.addSetting(bs); 620 } else { 621 log.error("No assigned turnout (R): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName()); // NOI18N 622 } 623 if (lt.getLayoutBlock() != layoutBlock) { 624 curConnection = null; 625 } else { 626 prevConnection = curConnection; 627 curConnection = lt.getConnectA(); 628 typeCurConnection = HitPointType.TRACK; 629 } 630 } else if (typeCurConnection == HitPointType.TURNOUT_C) { 631 // diverging track of turnout 632 if (lt.getTurnout() != null) { 633 if (lt.getContinuingSense() == Turnout.CLOSED) { 634 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.THROWN); 635 } else { 636 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 637 } 638 p.addSetting(bs); 639 } else { 640 log.error("No assigned turnout (S): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName()); // NOI18N 641 } 642 if (lt.getLayoutBlock() != layoutBlock) { 643 curConnection = null; 644 } else { 645 prevConnection = curConnection; 646 curConnection = lt.getConnectA(); 647 typeCurConnection = HitPointType.TRACK; 648 } 649 } 650 } 651 } else if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) { 652 // have a double crossover turnout, cannot follow possible path any further 653 curConnection = null; 654 } else if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.RH_XOVER) { 655 // have a right-handed crossover turnout 656 if ((typeCurConnection == HitPointType.TURNOUT_A) 657 || (typeCurConnection == HitPointType.TURNOUT_C)) { 658 // entry is at turnout throat, cannot follow possible path any further 659 curConnection = null; 660 } else if (typeCurConnection == HitPointType.TURNOUT_B) { 661 // entry is at continuing track of turnout 662 if (lt.getLayoutBlockB() != layoutBlock) { 663 // cross-over block different, end of current block 664 break; 665 } 666 if (lt.getTurnout() != null) { 667 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 668 p.addSetting(bs); 669 } else { 670 log.error("No assigned turnout (T): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName()); // NOI18N 671 } 672 if (lt.getLayoutBlock() != layoutBlock) { 673 // left current block 674 curConnection = null; 675 } else { 676 prevConnection = curConnection; 677 curConnection = lt.getConnectA(); 678 typeCurConnection = HitPointType.TRACK; 679 } 680 } else { // typeCurConnection == LayoutEditor.HitPointTypes.TURNOUT_D 681 // entry is at continuing track of turnout 682 if (lt.getLayoutBlockD() != layoutBlock) { 683 // cross-over block different, end of current block 684 break; 685 } 686 if (lt.getTurnout() != null) { 687 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 688 p.addSetting(bs); 689 } else { 690 log.error("No assigned turnout (U): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName()); // NOI18N 691 } 692 if (lt.getLayoutBlockC() != layoutBlock) { 693 // left current block 694 curConnection = null; 695 } else { 696 prevConnection = curConnection; 697 curConnection = lt.getConnectC(); 698 typeCurConnection = HitPointType.TRACK; 699 } 700 } 701 } else if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.LH_XOVER) { 702 // have a left-handed crossover turnout 703 if ((typeCurConnection == HitPointType.TURNOUT_B) 704 || (typeCurConnection == HitPointType.TURNOUT_D)) { 705 // entry is at turnout throat, cannot follow possible path any further 706 curConnection = null; 707 } else if (typeCurConnection == HitPointType.TURNOUT_A) { 708 // entry is at continuing track of turnout 709 if (lt.getLayoutBlock() != layoutBlock) { 710 // cross-over block different, end of current block 711 break; 712 } 713 if (lt.getTurnout() != null) { 714 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 715 p.addSetting(bs); 716 } else { 717 log.error("No assigned turnout (V): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName()); // NOI18N 718 } 719 if (lt.getLayoutBlockB() != layoutBlock) { 720 // left current block 721 curConnection = null; 722 } else { 723 prevConnection = curConnection; 724 curConnection = lt.getConnectB(); 725 typeCurConnection = HitPointType.TRACK; 726 } 727 } else { // typeCurConnection == LayoutEditor.HitPointTypes.TURNOUT_C per if statement 2 levels up 728 // entry is at continuing track of turnout 729 if (lt.getLayoutBlockC() != layoutBlock) { 730 // cross-over block different, end of current block 731 break; 732 } 733 if (lt.getTurnout() != null) { 734 bs = new BeanSetting(lt.getTurnout(), lt.getTurnoutName(), Turnout.CLOSED); 735 p.addSetting(bs); 736 } else { 737 log.error("No assigned turnout (W): LTO = {}, blk = {}", lt.getName(), lt.getLayoutBlock().getDisplayName()); // NOI18N 738 } 739 if (lt.getLayoutBlockD() != layoutBlock) { 740 // left current block 741 curConnection = null; 742 } else { 743 prevConnection = curConnection; 744 curConnection = lt.getConnectD(); 745 typeCurConnection = HitPointType.TRACK; 746 } 747 } 748 } 749 } else if (typeCurConnection == HitPointType.LEVEL_XING_A) { 750 // have a level crossing connected at A 751 if (((LevelXing) curConnection).getLayoutBlockAC() != layoutBlock) { 752 // moved outside of this block 753 curConnection = null; 754 } else { 755 // move to other end of this section of this level crossing track 756 prevConnection = curConnection; 757 curConnection = ((LevelXing) curConnection).getConnectC(); 758 typeCurConnection = HitPointType.TRACK; 759 } 760 } else if (typeCurConnection == HitPointType.LEVEL_XING_B) { 761 // have a level crossing connected at B 762 if (((LevelXing) curConnection).getLayoutBlockBD() != layoutBlock) { 763 // moved outside of this block 764 curConnection = null; 765 } else { 766 // move to other end of this section of this level crossing track 767 prevConnection = curConnection; 768 curConnection = ((LevelXing) curConnection).getConnectD(); 769 typeCurConnection = HitPointType.TRACK; 770 } 771 } else if (typeCurConnection == HitPointType.LEVEL_XING_C) { 772 // have a level crossing connected at C 773 if (((LevelXing) curConnection).getLayoutBlockAC() != layoutBlock) { 774 // moved outside of this block 775 curConnection = null; 776 } else { 777 // move to other end of this section of this level crossing track 778 prevConnection = curConnection; 779 curConnection = ((LevelXing) curConnection).getConnectA(); 780 typeCurConnection = HitPointType.TRACK; 781 } 782 } else if (typeCurConnection == HitPointType.LEVEL_XING_D) { 783 // have a level crossing connected at D 784 if (((LevelXing) curConnection).getLayoutBlockBD() != layoutBlock) { 785 // moved outside of this block 786 curConnection = null; 787 } else { 788 // move to other end of this section of this level crossing track 789 prevConnection = curConnection; 790 curConnection = ((LevelXing) curConnection).getConnectB(); 791 typeCurConnection = HitPointType.TRACK; 792 } 793 } else if (HitPointType.isSlipHitType(typeCurConnection)) { 794 LayoutSlip ls = (LayoutSlip) curConnection; 795 if (ls.getLayoutBlock() != layoutBlock) { 796 curConnection = null; 797 } else if (ls.getSlipType() == LayoutSlip.TurnoutType.SINGLE_SLIP) { 798 if (typeCurConnection == HitPointType.SLIP_C) { 799 if (ls.getTurnout() != null) { 800 bs = new BeanSetting(ls.getTurnout(), ls.getTurnoutName(), ls.getTurnoutState(LayoutTurnout.STATE_AC)); 801 p.addSetting(bs); 802 } else { 803 log.error("No assigned turnout (X): LTO = {}, blk = {}", ls.getName(), ls.getLayoutBlock().getDisplayName()); // NOI18N 804 } 805 if (ls.getTurnoutB() != null) { 806 bs = new BeanSetting(ls.getTurnoutB(), ls.getTurnoutBName(), ls.getTurnoutBState(LayoutTurnout.STATE_AC)); 807 p.addSetting(bs); 808 } else { 809 log.error("No assigned turnoutB (Y): LTO = {}, blk = {}", ls.getName(), ls.getLayoutBlock().getDisplayName()); // NOI18N 810 } 811 prevConnection = curConnection; 812 curConnection = ((LayoutSlip) curConnection).getConnectC(); 813 typeCurConnection = HitPointType.TRACK; 814 } else if (typeCurConnection == HitPointType.SLIP_B) { 815 if (ls.getTurnout() != null) { 816 bs = new BeanSetting(ls.getTurnout(), ls.getTurnoutName(), ls.getTurnoutState(LayoutTurnout.STATE_BD)); 817 p.addSetting(bs); 818 } else { 819 log.error("No assigned turnout (Z): LTO = {}, blk = {}", ls.getName(), ls.getLayoutBlock().getDisplayName()); // NOI18N 820 } 821 822 if (ls.getTurnoutB() != null) { 823 bs = new BeanSetting(ls.getTurnoutB(), ls.getTurnoutBName(), ls.getTurnoutBState(LayoutTurnout.STATE_BD)); 824 p.addSetting(bs); 825 } else { 826 log.error("No assigned turnoutB (1): LTO = {}, blk = {}", ls.getName(), ls.getLayoutBlock().getDisplayName()); // NOI18N 827 } 828 prevConnection = curConnection; 829 curConnection = ((LayoutSlip) curConnection).getConnectB(); 830 typeCurConnection = HitPointType.TRACK; 831 } else { 832 //Else could be going in the slip direction 833 curConnection = null; 834 } 835 836 } else { 837 //At double slip, can not follow any further 838 curConnection = null; 839 } 840 } else if (HitPointType.isTurntableRayHitType(typeCurConnection)) { 841 if (log.isDebugEnabled()) { 842 log.debug("Layout Block: {}, found track type: {}, to " // NOI18N 843 + "Block: {}, is potentially assigned to turntable ray", // NOI18N 844 layoutBlock.getDisplayName(), 845 typeCurConnection, 846 p.getBlock().getDisplayName() 847 ); 848 } 849 curConnection = null; 850 } else { 851 // catch when some new type got added 852 log.error("Layout Block: {} found unknown track type: {}" // NOI18N 853 + " to Block: {}", 854 layoutBlock.getDisplayName(), 855 typeCurConnection, 856 p.getBlock().getDisplayName() 857 ); 858 break; 859 } 860 } 861 } // addBeanSettings 862 863 // initialize logging 864 private final static Logger log 865 = LoggerFactory.getLogger(LayoutEditorAuxTools.class); 866}