001package jmri.jmrit.display.layoutEditor; 002 003import java.awt.Color; 004import java.awt.Font; 005import java.text.MessageFormat; 006import java.util.*; 007 008import javax.annotation.CheckForNull; 009import javax.annotation.Nonnull; 010 011import jmri.*; 012import jmri.jmrit.signalling.SignallingGuiTools; 013import jmri.jmrit.display.ToolTip; 014 015/** 016 * LayoutTurnout is the abstract base for classes representing various types of turnout on the layout. 017 * A LayoutTurnout is an 018 * extension of the standard Turnout object with drawing and connectivity 019 * information added. 020 * <p> 021 * Specific forms are represented: right-hand, left-hand, wye, double crossover, 022 * right-handed single crossover, and left-handed single crossover. Note that 023 * double-slip turnouts can be handled as two turnouts, throat to throat, and 024 * three-way turnouts can be handled as two turnouts, left-hand and right-hand, 025 * arranged throat to continuing route. 026 * <p> 027 * A LayoutTurnout has three or four connection points, designated A, B, C, and 028 * D. For right-handed or left-handed turnouts, A corresponds to the throat. At 029 * the crossing, A-B (and C-D for crossovers) is a straight segment (continuing 030 * route). A-C (and B-D for crossovers) is the diverging route. B-C (and A-D for 031 * crossovers) is an illegal condition. 032 * <br> 033 * <pre> 034 * Turnouts 035 * Right-hand Left-hand 036 * 037 * C 038 * // 039 * A ==**== B A ==**== B 040 * \\ 041 * C 042 * 043 * Wye Three-way 044 * 045 * B D 046 * // // 047 * A ==** A ==**== B 048 * \\ \\ 049 * C C 050 * 051 * Crossovers 052 * Right-hand left-hand 053 * A ==**===== B A ====**== B 054 * \\ // 055 * \\ // 056 * D ====**== C D ==**===== C 057 * 058 * Double 059 * A ==**==**== B 060 * \\// 061 * XX 062 * //\\ 063 * D ==**==**== C 064 * </pre> 065 * (The {@link LayoutSlip} track objects follow a different pattern. They put A-D in 066 * different places and have AD and BC as the normal-continuance parallel paths) 067 * <p> 068 * A LayoutTurnout carries Block information. For right-handed, left-handed, and 069 * wye turnouts, the entire turnout is in one block, however, a block border may 070 * occur at any connection (A,B,C,D). For a double crossover turnout, up to four 071 * blocks may be assigned, one for each connection point, but if only one block 072 * is assigned, that block applies to the entire turnout. 073 * <p> 074 * For drawing purposes, each LayoutTurnout carries a center point and 075 * displacements for B and C. For right-handed or left-handed turnouts, the 076 * displacement for A = - the displacement for B, and the center point is at the 077 * junction of the diverging route and the straight through continuing route. 078 * For double crossovers, the center point is at the center of the turnout, and 079 * the displacement for A = - the displacement for C and the displacement for D 080 * = - the displacement for B. The center point and these displacements may be 081 * adjusted by the user when in edit mode. For double crossovers, AB and BC are 082 * constrained to remain perpendicular. For single crossovers, AB and CD are 083 * constrained to remain parallel, and AC and BD are constrained to remain 084 * parallel. 085 * <p> 086 * When LayoutTurnouts are first created, a rotation (degrees) is provided. For 087 * 0.0 rotation, the turnout lies on the east-west line with A facing east. 088 * Rotations are performed in a clockwise direction. 089 * <p> 090 * When LayoutTurnouts are first created, there are no connections. Block 091 * information and connections may be added when available. 092 * <p> 093 * When a LayoutTurnout is first created, it is enabled for control of an 094 * assigned actual turnout. Clicking on the turnout center point will toggle the 095 * turnout. This can be disabled via the popup menu. 096 * <p> 097 * Signal Head names are saved here to keep track of where signals are. 098 * LayoutTurnout only serves as a storage place for signal head names. The names 099 * are placed here by tools, e.g., Set Signals at Turnout, and Set Signals at 100 * Double Crossover. Each connection point can have up to three SignalHeads and one SignalMast. 101 * <p> 102 * A LayoutWye may be linked to another LayoutTurnout to form a turnout 103 * pair. 104 *<br> 105 * Throat-To-Throat Turnouts - Two turnouts connected closely at their 106 * throats, so closely that signals are not appropriate at the their throats. 107 * This is the situation when two RH, LH, or WYE turnouts are used to model a 108 * double slip. 109 *<br> 110 * 3-Way Turnout - Two turnouts modeling a 3-way turnout, where the 111 * throat of the second turnout is closely connected to the continuing track of 112 * the first turnout. The throat will have three heads, or one head. A link is 113 * required to be able to correctly interpret the use of signal heads. 114 * 115 * @author Dave Duchamp Copyright (c) 2004-2007 116 * @author George Warner Copyright (c) 2017-2019 117 * @author Bob Jacobsen Copyright (c) 2020 118 */ 119abstract public class LayoutTurnout extends LayoutTrack { 120 121 protected LayoutTurnout(@Nonnull String id, 122 @Nonnull LayoutEditor models, TurnoutType t) { 123 super(id, models); 124 125 type = t; 126 createTooltip(models); 127 } 128 129 protected LayoutTurnout(@Nonnull String id, 130 @Nonnull LayoutEditor models) { 131 this(id, models, TurnoutType.NONE); 132 } 133 134 public LayoutTurnout(@Nonnull String id, TurnoutType t, 135 @Nonnull LayoutEditor models) { 136 this(id, t, models, 1); 137 } 138 139 /** 140 * Main constructor method. 141 * @param id Layout Turnout ID. 142 * @param t type, e.g. LH_TURNOUT, WYE_TURNOUT 143 * @param models main layout editor. 144 * @param v version. 145 */ 146 public LayoutTurnout(@Nonnull String id, TurnoutType t, 147 @Nonnull LayoutEditor models, 148 int v) { 149 super(id, models); 150 151 namedTurnout = null; 152 turnoutName = ""; 153 mTurnoutListener = null; 154 disabled = false; 155 disableWhenOccupied = false; 156 type = t; 157 version = v; 158 createTooltip(models); 159 160 } 161 162 private void createTooltip(LayoutEditor models) { 163 var tt = new ToolTip(null, 0, 0, new Font("SansSerif", Font.PLAIN, 12), 164 Color.black, new Color(215, 225, 255), Color.black, null); 165 setToolTip(tt); 166 setShowToolTip(models.showToolTip()); 167 } 168 169 // Defined constants for turnout types 170 // This is being replaced by subclasses; do not add more 171 // references to it. 172 public enum TurnoutType { 173 NONE, 174 RH_TURNOUT, 175 LH_TURNOUT, 176 WYE_TURNOUT, 177 DOUBLE_XOVER, 178 RH_XOVER, 179 LH_XOVER, 180 SINGLE_SLIP, // used for LayoutSlip which extends this class 181 DOUBLE_SLIP // used for LayoutSlip which extends this class 182 } 183 184 /** 185 * Returns true if this is a turnout (not a crossover or slip) 186 * 187 * @param type the turnout type 188 * @return boolean true if this is a turnout 189 */ 190 public static boolean isTurnoutTypeTurnout(TurnoutType type) { 191 return (type == TurnoutType.RH_TURNOUT 192 || type == TurnoutType.LH_TURNOUT 193 || type == TurnoutType.WYE_TURNOUT); 194 } 195 196 /** 197 * Returns true if this is a turnout (not a crossover or slip) 198 * 199 * @return boolean true if this is a turnout 200 */ 201 public boolean isTurnoutTypeTurnout() { 202 return isTurnoutTypeTurnout(getTurnoutType()); 203 } 204 205 /** 206 * Returns true if this is a crossover 207 * 208 * @param type the turnout type 209 * @return boolean true if this is a crossover 210 */ 211 public static boolean isTurnoutTypeXover(TurnoutType type) { 212 return (type == TurnoutType.DOUBLE_XOVER 213 || type == TurnoutType.RH_XOVER 214 || type == TurnoutType.LH_XOVER); 215 } 216 217 /** 218 * Returns true if this is a crossover 219 * 220 * @return boolean true if this is a crossover 221 */ 222 public boolean isTurnoutTypeXover() { 223 return isTurnoutTypeXover(getTurnoutType()); 224 } 225 226 /** 227 * Returns true if this is a slip 228 * 229 * @param type the turnout type 230 * @return boolean true if this is a slip 231 */ 232 public static boolean isTurnoutTypeSlip(TurnoutType type) { 233 return (type == TurnoutType.SINGLE_SLIP 234 || type == TurnoutType.DOUBLE_SLIP); 235 } 236 237 /** 238 * Returns true if this is a slip 239 * 240 * @return boolean true if this is a slip 241 */ 242 public boolean isTurnoutTypeSlip() { 243 return isTurnoutTypeSlip(getTurnoutType()); 244 } 245 246 /** 247 * Returns true if this has a single-track entrance end. (turnout or wye) 248 * 249 * @param type the turnout type 250 * @return boolean true if single track entrance 251 */ 252 public static boolean hasEnteringSingleTrack(TurnoutType type) { 253 return isTurnoutTypeTurnout(type); 254 } 255 256 /** 257 * Returns true if this has a single-track entrance end. (turnout or wye) 258 * 259 * @return boolean true if single track entrance 260 */ 261 public boolean hasEnteringSingleTrack() { 262 return hasEnteringSingleTrack(getTurnoutType()); 263 } 264 265 /** 266 * Returns true if this has double track on the entrance end (crossover or 267 * slip) 268 * 269 * @param type the turnout type 270 * @return boolean true if double track entrance 271 */ 272 public static boolean hasEnteringDoubleTrack(TurnoutType type) { 273 return isTurnoutTypeXover(type) || isTurnoutTypeSlip(type); 274 } 275 276 /** 277 * Returns true if this has double track on the entrance end (crossover or 278 * slip) 279 * 280 * @return boolean true if double track entrance 281 */ 282 public boolean hasEnteringDoubleTrack() { 283 return hasEnteringDoubleTrack(getTurnoutType()); 284 } 285 286 public enum LinkType { 287 NO_LINK, 288 FIRST_3_WAY, // this turnout is the first turnout of a 3-way 289 // turnout pair (closest to the throat) 290 SECOND_3_WAY, // this turnout is the second turnout of a 3-way 291 // turnout pair (furthest from the throat) 292 THROAT_TO_THROAT // this turnout is one of two throat-to-throat 293 // turnouts - no signals at throat 294 } 295 296 // operational instance variables (not saved between sessions) 297 public static final int UNKNOWN = Turnout.UNKNOWN; 298 public static final int INCONSISTENT = Turnout.INCONSISTENT; 299 public static final int STATE_AC = 0x02; 300 public static final int STATE_BD = 0x04; 301 public static final int STATE_AD = 0x06; 302 public static final int STATE_BC = 0x08; 303 304 // program default turnout size parameters 305 public static final double turnoutBXDefault = 20.0; // RH, LH, WYE 306 public static final double turnoutCXDefault = 20.0; 307 public static final double turnoutWidDefault = 10.0; 308 public static final double xOverLongDefault = 30.0; // DOUBLE_XOVER, RH_XOVER, LH_XOVER 309 public static final double xOverHWidDefault = 10.0; 310 public static final double xOverShortDefault = 10.0; 311 312 // operational instance variables (not saved between sessions) 313 protected NamedBeanHandle<Turnout> namedTurnout = null; 314 // Second turnout is used to either throw a second turnout in a cross over or if one turnout address is used to throw two physical ones 315 protected NamedBeanHandle<Turnout> secondNamedTurnout = null; 316 317 private java.beans.PropertyChangeListener mTurnoutListener = null; 318 319 // persistent instances variables (saved between sessions) 320 // these should be the system or user name of an existing physical turnout 321 @Nonnull private String turnoutName = ""; // "" means none, never null 322 @Nonnull private String secondTurnoutName = ""; // "" means none, never null 323 private boolean secondTurnoutInverted = false; 324 325 // default is package protected 326 protected NamedBeanHandle<LayoutBlock> namedLayoutBlockA = null; 327 protected NamedBeanHandle<LayoutBlock> namedLayoutBlockB = null; // Xover - second block, if there is one 328 protected NamedBeanHandle<LayoutBlock> namedLayoutBlockC = null; // Xover - third block, if there is one 329 protected NamedBeanHandle<LayoutBlock> namedLayoutBlockD = null; // Xover - forth block, if there is one 330 331 protected NamedBeanHandle<SignalHead> signalA1HeadNamed = null; // signal 1 (continuing) (throat for RH, LH, WYE) 332 protected NamedBeanHandle<SignalHead> signalA2HeadNamed = null; // signal 2 (diverging) (throat for RH, LH, WYE) 333 protected NamedBeanHandle<SignalHead> signalA3HeadNamed = null; // signal 3 (second diverging) (3-way turnouts only) 334 protected NamedBeanHandle<SignalHead> signalB1HeadNamed = null; // continuing (RH, LH, WYE) signal 1 (double crossover) 335 protected NamedBeanHandle<SignalHead> signalB2HeadNamed = null; // LH_Xover and double crossover only 336 protected NamedBeanHandle<SignalHead> signalC1HeadNamed = null; // diverging (RH, LH, WYE) signal 1 (double crossover) 337 protected NamedBeanHandle<SignalHead> signalC2HeadNamed = null; // RH_Xover and double crossover only 338 protected NamedBeanHandle<SignalHead> signalD1HeadNamed = null; // single or double crossover only 339 protected NamedBeanHandle<SignalHead> signalD2HeadNamed = null; // LH_Xover and double crossover only 340 341 public enum Geometry { 342 NONE, 343 POINTA1, 344 POINTA2, 345 POINTA3, 346 POINTB1, 347 POINTB2, 348 POINTC1, 349 POINTC2, 350 POINTD1, 351 POINTD2 352 } 353 354 protected NamedBeanHandle<SignalMast> signalAMastNamed = null; // Throat 355 protected NamedBeanHandle<SignalMast> signalBMastNamed = null; // Continuing 356 protected NamedBeanHandle<SignalMast> signalCMastNamed = null; // diverging 357 protected NamedBeanHandle<SignalMast> signalDMastNamed = null; // single or double crossover only 358 359 protected NamedBeanHandle<Sensor> sensorANamed = null; // Throat 360 protected NamedBeanHandle<Sensor> sensorBNamed = null; // Continuing 361 protected NamedBeanHandle<Sensor> sensorCNamed = null; // diverging 362 protected NamedBeanHandle<Sensor> sensorDNamed = null; // single or double crossover only 363 364 protected final TurnoutType type; 365 366 public LayoutTrack connectA = null; // throat of LH, RH, RH Xover, LH Xover, and WYE turnouts 367 public LayoutTrack connectB = null; // straight leg of LH and RH turnouts 368 public LayoutTrack connectC = null; 369 public LayoutTrack connectD = null; // double xover, RH Xover, LH Xover only 370 371 public int continuingSense = Turnout.CLOSED; 372 373 public boolean disabled = false; 374 public boolean disableWhenOccupied = false; 375 376 private int version = 1; 377 378 @Nonnull public String linkedTurnoutName = ""; // name of the linked Turnout (as entered in tool); "" means none, never null 379 public LinkType linkType = LinkType.NO_LINK; 380 381 private final boolean useBlockSpeed = false; 382 383 /** 384 * {@inheritDoc} 385 */ 386 // this should only be used for debugging... 387 @Override 388 @Nonnull 389 public String toString() { 390 return "LayoutTurnout " + getName(); 391 } 392 393 /** 394 * Get the Version. 395 * @return turnout version. 396 */ 397 public int getVersion() { 398 return version; 399 } 400 401 public void setVersion(int v) { 402 version = v; 403 } 404 405 public boolean useBlockSpeed() { 406 return useBlockSpeed; 407 } 408 409 @Nonnull 410 public String getTurnoutName() { 411 if (namedTurnout != null) { 412 turnoutName = namedTurnout.getName(); 413 } 414 return turnoutName; 415 } 416 417 @Nonnull 418 public String getSecondTurnoutName() { 419 if (secondNamedTurnout != null) { 420 secondTurnoutName = secondNamedTurnout.getName(); 421 } 422 return secondTurnoutName; 423 } 424 425 public boolean isSecondTurnoutInverted() { 426 return secondTurnoutInverted; 427 } 428 429 @Nonnull 430 public String getBlockName() { 431 String result = null; 432 if (namedLayoutBlockA != null) { 433 result = namedLayoutBlockA.getName(); 434 } 435 return ((result == null) ? "" : result); 436 } 437 438 @Nonnull 439 public String getBlockBName() { 440 String result = getBlockName(); 441 if (namedLayoutBlockB != null) { 442 result = namedLayoutBlockB.getName(); 443 } 444 return result; 445 } 446 447 @Nonnull 448 public String getBlockCName() { 449 String result = getBlockName(); 450 if (namedLayoutBlockC != null) { 451 result = namedLayoutBlockC.getName(); 452 } 453 return result; 454 } 455 456 @Nonnull 457 public String getBlockDName() { 458 String result = getBlockName(); 459 if (namedLayoutBlockD != null) { 460 result = namedLayoutBlockD.getName(); 461 } 462 return result; 463 } 464 465 @CheckForNull 466 public SignalHead getSignalHead(Geometry loc) { 467 NamedBeanHandle<SignalHead> signalHead = null; 468 switch (loc) { 469 case POINTA1: 470 signalHead = signalA1HeadNamed; 471 break; 472 case POINTA2: 473 signalHead = signalA2HeadNamed; 474 break; 475 case POINTA3: 476 signalHead = signalA3HeadNamed; 477 break; 478 case POINTB1: 479 signalHead = signalB1HeadNamed; 480 break; 481 case POINTB2: 482 signalHead = signalB2HeadNamed; 483 break; 484 case POINTC1: 485 signalHead = signalC1HeadNamed; 486 break; 487 case POINTC2: 488 signalHead = signalC2HeadNamed; 489 break; 490 case POINTD1: 491 signalHead = signalD1HeadNamed; 492 break; 493 case POINTD2: 494 signalHead = signalD2HeadNamed; 495 break; 496 default: 497 log.warn("{}.getSignalHead({}); Unhandled point type", getName(), loc); 498 break; 499 } 500 if (signalHead != null) { 501 return signalHead.getBean(); 502 } 503 return null; 504 } 505 506 @CheckForNull 507 public SignalHead getSignalA1() { 508 return signalA1HeadNamed != null ? signalA1HeadNamed.getBean() : null; 509 } 510 511 @Nonnull 512 public String getSignalA1Name() { 513 if (signalA1HeadNamed != null) { 514 return signalA1HeadNamed.getName(); 515 } 516 return ""; 517 } 518 519 public void setSignalA1Name(@CheckForNull String signalHead) { 520 if (signalHead == null || signalHead.isEmpty()) { 521 signalA1HeadNamed = null; 522 return; 523 } 524 525 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 526 if (head != null) { 527 signalA1HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 528 } else { 529 signalA1HeadNamed = null; 530 log.error("{}.setSignalA1Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 531 } 532 } 533 534 @CheckForNull 535 public SignalHead getSignalA2() { 536 return signalA2HeadNamed != null ? signalA2HeadNamed.getBean() : null; 537 } 538 539 @Nonnull 540 public String getSignalA2Name() { 541 if (signalA2HeadNamed != null) { 542 return signalA2HeadNamed.getName(); 543 } 544 return ""; 545 } 546 547 public void setSignalA2Name(@CheckForNull String signalHead) { 548 if (signalHead == null || signalHead.isEmpty()) { 549 signalA2HeadNamed = null; 550 return; 551 } 552 553 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 554 if (head != null) { 555 signalA2HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 556 } else { 557 signalA2HeadNamed = null; 558 log.error("{}.setSignalA2Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 559 } 560 } 561 562 @CheckForNull 563 public SignalHead getSignalA3() { 564 return signalA3HeadNamed != null ? signalA3HeadNamed.getBean() : null; 565 } 566 567 @Nonnull 568 public String getSignalA3Name() { 569 if (signalA3HeadNamed != null) { 570 return signalA3HeadNamed.getName(); 571 } 572 return ""; 573 } 574 575 public void setSignalA3Name(@CheckForNull String signalHead) { 576 if (signalHead == null || signalHead.isEmpty()) { 577 signalA3HeadNamed = null; 578 return; 579 } 580 581 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 582 if (head != null) { 583 signalA3HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 584 } else { 585 signalA3HeadNamed = null; 586 log.error("{}.setSignalA3Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 587 } 588 } 589 590 @CheckForNull 591 public SignalHead getSignalB1() { 592 return signalB1HeadNamed != null ? signalB1HeadNamed.getBean() : null; 593 } 594 595 @Nonnull 596 public String getSignalB1Name() { 597 if (signalB1HeadNamed != null) { 598 return signalB1HeadNamed.getName(); 599 } 600 return ""; 601 } 602 603 public void setSignalB1Name(@CheckForNull String signalHead) { 604 if (signalHead == null || signalHead.isEmpty()) { 605 signalB1HeadNamed = null; 606 return; 607 } 608 609 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 610 if (head != null) { 611 signalB1HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 612 } else { 613 signalB1HeadNamed = null; 614 log.error("{}.setSignalB1Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 615 } 616 } 617 618 @CheckForNull 619 public SignalHead getSignalB2() { 620 return signalB2HeadNamed != null ? signalB2HeadNamed.getBean() : null; 621 } 622 623 @Nonnull 624 public String getSignalB2Name() { 625 if (signalB2HeadNamed != null) { 626 return signalB2HeadNamed.getName(); 627 } 628 return ""; 629 } 630 631 public void setSignalB2Name(@CheckForNull String signalHead) { 632 if (signalHead == null || signalHead.isEmpty()) { 633 signalB2HeadNamed = null; 634 return; 635 } 636 637 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 638 if (head != null) { 639 signalB2HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 640 } else { 641 signalB2HeadNamed = null; 642 log.error("{}.setSignalB2Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 643 } 644 } 645 646 @CheckForNull 647 public SignalHead getSignalC1() { 648 return signalC1HeadNamed != null ? signalC1HeadNamed.getBean() : null; 649 } 650 651 @Nonnull 652 public String getSignalC1Name() { 653 if (signalC1HeadNamed != null) { 654 return signalC1HeadNamed.getName(); 655 } 656 return ""; 657 } 658 659 public void setSignalC1Name(@CheckForNull String signalHead) { 660 if (signalHead == null || signalHead.isEmpty()) { 661 signalC1HeadNamed = null; 662 return; 663 } 664 665 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 666 if (head != null) { 667 signalC1HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 668 } else { 669 signalC1HeadNamed = null; 670 log.error("{}.setSignalC1Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 671 } 672 } 673 674 @CheckForNull 675 public SignalHead getSignalC2() { 676 return signalC2HeadNamed != null ? signalC2HeadNamed.getBean() : null; 677 } 678 679 @Nonnull 680 public String getSignalC2Name() { 681 if (signalC2HeadNamed != null) { 682 return signalC2HeadNamed.getName(); 683 } 684 return ""; 685 } 686 687 public void setSignalC2Name(@CheckForNull String signalHead) { 688 if (signalHead == null || signalHead.isEmpty()) { 689 signalC2HeadNamed = null; 690 return; 691 } 692 693 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 694 if (head != null) { 695 signalC2HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 696 } else { 697 signalC2HeadNamed = null; 698 log.error("{}.setSignalC2Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 699 } 700 } 701 702 @CheckForNull 703 public SignalHead getSignalD1() { 704 return signalD1HeadNamed != null ? signalD1HeadNamed.getBean() : null; 705 } 706 707 @Nonnull 708 public String getSignalD1Name() { 709 if (signalD1HeadNamed != null) { 710 return signalD1HeadNamed.getName(); 711 } 712 return ""; 713 } 714 715 public void setSignalD1Name(@CheckForNull String signalHead) { 716 if (signalHead == null || signalHead.isEmpty()) { 717 signalD1HeadNamed = null; 718 return; 719 } 720 721 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 722 if (head != null) { 723 signalD1HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 724 } else { 725 signalD1HeadNamed = null; 726 log.error("{}.setSignalD1Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 727 } 728 } 729 730 @CheckForNull 731 public SignalHead getSignalD2() { 732 return signalD2HeadNamed != null ? signalD2HeadNamed.getBean() : null; 733 } 734 735 @Nonnull 736 public String getSignalD2Name() { 737 if (signalD2HeadNamed != null) { 738 return signalD2HeadNamed.getName(); 739 } 740 return ""; 741 } 742 743 public void setSignalD2Name(@CheckForNull String signalHead) { 744 if (signalHead == null || signalHead.isEmpty()) { 745 signalD2HeadNamed = null; 746 return; 747 } 748 749 SignalHead head = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signalHead); 750 if (head != null) { 751 signalD2HeadNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalHead, head); 752 } else { 753 signalD2HeadNamed = null; 754 log.error("{}.setSignalD2Name({}); not fournd for turnout {}", getName(), signalHead, getTurnoutName()); 755 } 756 } 757 758 public void removeBeanReference(@CheckForNull jmri.NamedBean nb) { 759 if (nb == null) { 760 return; 761 } 762 if (nb instanceof SignalMast) { 763 if (nb.equals(getSignalAMast())) { 764 setSignalAMast(null); 765 return; 766 } 767 if (nb.equals(getSignalBMast())) { 768 setSignalBMast(null); 769 return; 770 } 771 if (nb.equals(getSignalCMast())) { 772 setSignalCMast(null); 773 return; 774 } 775 if (nb.equals(getSignalDMast())) { 776 setSignalDMast(null); 777 } 778 } else if (nb instanceof Sensor) { 779 if (nb.equals(getSensorA())) { 780 setSensorA(null); 781 return; 782 } 783 if (nb.equals(getSensorB())) { 784 setSensorB(null); 785 return; 786 } 787 if (nb.equals(getSensorC())) { 788 setSensorC(null); 789 return; 790 } 791 if (nb.equals(getSensorB())) { 792 setSensorD(null); 793 } 794 } else if (nb instanceof SignalHead) { 795 if (nb.equals(getSignalHead(Geometry.POINTA1))) { 796 setSignalA1Name(null); 797 } 798 if (nb.equals(getSignalHead(Geometry.POINTA2))) { 799 setSignalA2Name(null); 800 } 801 if (nb.equals(getSignalHead(Geometry.POINTA3))) { 802 setSignalA3Name(null); 803 } 804 if (nb.equals(getSignalHead(Geometry.POINTB1))) { 805 setSignalB1Name(null); 806 } 807 if (nb.equals(getSignalHead(Geometry.POINTB2))) { 808 setSignalB2Name(null); 809 } 810 if (nb.equals(getSignalHead(Geometry.POINTC1))) { 811 setSignalC1Name(null); 812 } 813 if (nb.equals(getSignalHead(Geometry.POINTC2))) { 814 setSignalC2Name(null); 815 } 816 if (nb.equals(getSignalHead(Geometry.POINTD1))) { 817 setSignalD1Name(null); 818 } 819 if (nb.equals(getSignalHead(Geometry.POINTD2))) { 820 setSignalD2Name(null); 821 } 822 } 823 } 824 825 /** 826 * {@inheritDoc} 827 */ 828 @Override 829 public boolean canRemove() { 830 ArrayList<String> beanReferences = getBeanReferences("All"); // NOI18N 831 if (!beanReferences.isEmpty()) { 832 models.displayRemoveWarning(this, beanReferences, "BeanNameTurnout"); // NOI18N 833 } 834 return beanReferences.isEmpty(); 835 } 836 837 /** 838 * Build a list of sensors, signal heads, and signal masts attached to a 839 * turnout point. 840 * 841 * @param pointName Specify the point (A-D) or all (All) points. 842 * @return a list of bean reference names. 843 */ 844 @Nonnull 845 public ArrayList<String> getBeanReferences(String pointName) { 846 ArrayList<String> references = new ArrayList<>(); 847 if (pointName.equals("A") || pointName.equals("All")) { // NOI18N 848 if (!getSignalAMastName().isEmpty()) { 849 references.add(getSignalAMastName()); 850 } 851 if (!getSensorAName().isEmpty()) { 852 references.add(getSensorAName()); 853 } 854 if (!getSignalA1Name().isEmpty()) { 855 references.add(getSignalA1Name()); 856 } 857 if (!getSignalA2Name().isEmpty()) { 858 references.add(getSignalA2Name()); 859 } 860 if (!getSignalA3Name().isEmpty()) { 861 references.add(getSignalA3Name()); 862 } 863 } 864 if (pointName.equals("B") || pointName.equals("All")) { // NOI18N 865 if (!getSignalBMastName().isEmpty()) { 866 references.add(getSignalBMastName()); 867 } 868 if (!getSensorBName().isEmpty()) { 869 references.add(getSensorBName()); 870 } 871 if (!getSignalB1Name().isEmpty()) { 872 references.add(getSignalB1Name()); 873 } 874 if (!getSignalB2Name().isEmpty()) { 875 references.add(getSignalB2Name()); 876 } 877 } 878 if (pointName.equals("C") || pointName.equals("All")) { // NOI18N 879 if (!getSignalCMastName().isEmpty()) { 880 references.add(getSignalCMastName()); 881 } 882 if (!getSensorCName().isEmpty()) { 883 references.add(getSensorCName()); 884 } 885 if (!getSignalC1Name().isEmpty()) { 886 references.add(getSignalC1Name()); 887 } 888 if (!getSignalC2Name().isEmpty()) { 889 references.add(getSignalC2Name()); 890 } 891 } 892 if (pointName.equals("D") || pointName.equals("All")) { // NOI18N 893 if (!getSignalDMastName().isEmpty()) { 894 references.add(getSignalDMastName()); 895 } 896 if (!getSensorDName().isEmpty()) { 897 references.add(getSensorDName()); 898 } 899 if (!getSignalD1Name().isEmpty()) { 900 references.add(getSignalD1Name()); 901 } 902 if (!getSignalD2Name().isEmpty()) { 903 references.add(getSignalD2Name()); 904 } 905 } 906 return references; 907 } 908 909 @Nonnull 910 public String getSignalAMastName() { 911 if (signalAMastNamed != null) { 912 return signalAMastNamed.getName(); 913 } 914 return ""; 915 } 916 917 // @CheckForNull temporary until we get central error check 918 public SignalMast getSignalAMast() { 919 if (signalAMastNamed != null) { 920 return signalAMastNamed.getBean(); 921 } 922 return null; 923 } 924 925 public void setSignalAMast(@CheckForNull String signalMast) { 926 if (signalMast == null || signalMast.isEmpty()) { 927 signalAMastNamed = null; 928 return; 929 } 930 931 SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class).getSignalMast(signalMast); 932 if (mast != null) { 933 signalAMastNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalMast, mast); 934 } else { 935 signalAMastNamed = null; 936 log.error("{}.setSignalAMast({}); not fournd for turnout {}", getName(), signalMast, getTurnoutName()); 937 } 938 } 939 940 @Nonnull 941 public String getSignalBMastName() { 942 if (signalBMastNamed != null) { 943 return signalBMastNamed.getName(); 944 } 945 return ""; 946 } 947 948 // @CheckForNull temporary until we get central error check 949 public SignalMast getSignalBMast() { 950 if (signalBMastNamed != null) { 951 return signalBMastNamed.getBean(); 952 } 953 return null; 954 } 955 956 public void setSignalBMast(@CheckForNull String signalMast) { 957 if (signalMast == null || signalMast.isEmpty()) { 958 signalBMastNamed = null; 959 return; 960 } 961 962 SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class).getSignalMast(signalMast); 963 if (mast != null) { 964 signalBMastNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalMast, mast); 965 } else { 966 signalBMastNamed = null; 967 log.error("{}.setSignalBMast({}); not fournd for turnout {}", getName(), signalMast, getTurnoutName()); 968 } 969 } 970 971 @Nonnull 972 public String getSignalCMastName() { 973 if (signalCMastNamed != null) { 974 return signalCMastNamed.getName(); 975 } 976 return ""; 977 } 978 979 // @CheckForNull temporary until we get central error check 980 public SignalMast getSignalCMast() { 981 if (signalCMastNamed != null) { 982 return signalCMastNamed.getBean(); 983 } 984 return null; 985 } 986 987 public void setSignalCMast(@CheckForNull String signalMast) { 988 if (signalMast == null || signalMast.isEmpty()) { 989 signalCMastNamed = null; 990 return; 991 } 992 993 SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class).getSignalMast(signalMast); 994 if (mast != null) { 995 signalCMastNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalMast, mast); 996 } else { 997 log.error("{}.setSignalCMast({}); not fournd for turnout {}", getName(), signalMast, getTurnoutName()); 998 signalCMastNamed = null; 999 } 1000 } 1001 1002 @Nonnull 1003 public String getSignalDMastName() { 1004 if (signalDMastNamed != null) { 1005 return signalDMastNamed.getName(); 1006 } 1007 return ""; 1008 } 1009 1010 // @CheckForNull temporary until we get central error check 1011 public SignalMast getSignalDMast() { 1012 if (signalDMastNamed != null) { 1013 return signalDMastNamed.getBean(); 1014 } 1015 return null; 1016 } 1017 1018 public void setSignalDMast(@CheckForNull String signalMast) { 1019 if (signalMast == null || signalMast.isEmpty()) { 1020 signalDMastNamed = null; 1021 return; 1022 } 1023 1024 SignalMast mast = InstanceManager.getDefault(jmri.SignalMastManager.class).getSignalMast(signalMast); 1025 if (mast != null) { 1026 signalDMastNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(signalMast, mast); 1027 } else { 1028 log.error("{}.setSignalDMast({}); not fournd for turnout {}", getName(), signalMast, getTurnoutName()); 1029 signalDMastNamed = null; 1030 } 1031 } 1032 1033 @Nonnull 1034 public String getSensorAName() { 1035 if (sensorANamed != null) { 1036 return sensorANamed.getName(); 1037 } 1038 return ""; 1039 } 1040 1041 @CheckForNull 1042 public Sensor getSensorA() { 1043 if (sensorANamed != null) { 1044 return sensorANamed.getBean(); 1045 } 1046 return null; 1047 } 1048 1049 public void setSensorA(@CheckForNull String sensorName) { 1050 if (sensorName == null || sensorName.isEmpty()) { 1051 sensorANamed = null; 1052 return; 1053 } 1054 1055 try { 1056 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName); 1057 sensorANamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor); 1058 } catch (IllegalArgumentException ex) { 1059 sensorANamed = null; 1060 } 1061 } 1062 1063 @Nonnull 1064 public String getSensorBName() { 1065 if (sensorBNamed != null) { 1066 return sensorBNamed.getName(); 1067 } 1068 return ""; 1069 } 1070 1071 @CheckForNull 1072 public Sensor getSensorB() { 1073 if (sensorBNamed != null) { 1074 return sensorBNamed.getBean(); 1075 } 1076 return null; 1077 } 1078 1079 public void setSensorB(@CheckForNull String sensorName) { 1080 if (sensorName == null || sensorName.isEmpty()) { 1081 sensorBNamed = null; 1082 return; 1083 } 1084 1085 try { 1086 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName); 1087 sensorBNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor); 1088 } catch (IllegalArgumentException ex) { 1089 sensorBNamed = null; 1090 } 1091 } 1092 1093 @Nonnull 1094 public String getSensorCName() { 1095 if (sensorCNamed != null) { 1096 return sensorCNamed.getName(); 1097 } 1098 return ""; 1099 } 1100 1101 @CheckForNull 1102 public Sensor getSensorC() { 1103 if (sensorCNamed != null) { 1104 return sensorCNamed.getBean(); 1105 } 1106 return null; 1107 } 1108 1109 public void setSensorC(@CheckForNull String sensorName) { 1110 if (sensorName == null || sensorName.isEmpty()) { 1111 sensorCNamed = null; 1112 return; 1113 } 1114 1115 try { 1116 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName); 1117 sensorCNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor); 1118 } catch (IllegalArgumentException ex) { 1119 sensorCNamed = null; 1120 } 1121 } 1122 1123 @Nonnull 1124 public String getSensorDName() { 1125 if (sensorDNamed != null) { 1126 return sensorDNamed.getName(); 1127 } 1128 return ""; 1129 } 1130 1131 @CheckForNull 1132 public Sensor getSensorD() { 1133 if (sensorDNamed != null) { 1134 return sensorDNamed.getBean(); 1135 } 1136 return null; 1137 } 1138 1139 public void setSensorD(@CheckForNull String sensorName) { 1140 if (sensorName == null || sensorName.isEmpty()) { 1141 sensorDNamed = null; 1142 return; 1143 } 1144 1145 try { 1146 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName); 1147 sensorDNamed = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor); 1148 } catch (IllegalArgumentException ex) { 1149 sensorDNamed = null; 1150 } 1151 } 1152 1153 @Nonnull 1154 public String getLinkedTurnoutName() { 1155 return linkedTurnoutName; 1156 } 1157 1158 public void setLinkedTurnoutName(@Nonnull String s) { 1159 linkedTurnoutName = s; 1160 } // Could be done with changing over to a NamedBeanHandle 1161 1162 public LinkType getLinkType() { 1163 return linkType; 1164 } 1165 1166 public void setLinkType(LinkType ltype) { 1167 linkType = ltype; 1168 } 1169 1170 public TurnoutType getTurnoutType() { 1171 return type; 1172 } 1173 1174 public LayoutTrack getConnectA() { 1175 return connectA; 1176 } 1177 1178 public LayoutTrack getConnectB() { 1179 return connectB; 1180 } 1181 1182 public LayoutTrack getConnectC() { 1183 return connectC; 1184 } 1185 1186 public LayoutTrack getConnectD() { 1187 return connectD; 1188 } 1189 1190 /** 1191 * Perhaps confusingly, this returns an actual Turnout reference 1192 * or null for the turnout associated with this is LayoutTurnout. 1193 * This is different from {@link #setTurnout(String)}, which 1194 * takes a name (system or user) or an empty string. 1195 * @return Null if no Turnout set 1196 */ 1197 // @CheckForNull temporary - want to restore once better handled 1198 public Turnout getTurnout() { 1199 if (namedTurnout == null) { 1200 // set physical turnout if possible and needed 1201 setTurnout(turnoutName); 1202 if (namedTurnout == null) { 1203 return null; 1204 } 1205 } 1206 return namedTurnout.getBean(); 1207 } 1208 1209 public int getContinuingSense() { 1210 return continuingSense; 1211 } 1212 1213 /** 1214 * 1215 * @return true is the continuingSense matches the known state 1216 */ 1217 public boolean isInContinuingSenseState() { 1218 return getState() == continuingSense; 1219 } 1220 1221 /** 1222 * Perhaps confusingly, this takes a Turnout name (system or user) 1223 * to locate and set the turnout associated with this is LayoutTurnout. 1224 * This is different from {@link #getTurnout()}, which returns an 1225 * actual Turnout reference or null. 1226 * @param tName provide empty string for none; never null 1227 */ 1228 public void setTurnout(@Nonnull String tName) { 1229 assert tName != null; 1230 if (namedTurnout != null) { 1231 deactivateTurnout(); 1232 } 1233 turnoutName = tName; 1234 Turnout turnout = null; 1235 if (!turnoutName.isEmpty()) { 1236 turnout = InstanceManager.turnoutManagerInstance().getTurnout(turnoutName); 1237 } 1238 if (turnout != null) { 1239 namedTurnout = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(turnoutName, turnout); 1240 activateTurnout(); 1241 } else { 1242 turnoutName = ""; 1243 namedTurnout = null; 1244 setDisableWhenOccupied(false); 1245 } 1246 Turnout secondTurnout = getSecondTurnout(); 1247 if (secondTurnout != null && secondTurnout.getFeedbackMode() == Turnout.DIRECT) { 1248 secondTurnout.setLeadingTurnout(turnout, false); 1249 } 1250 } 1251 1252 // @CheckForNull temporary until we have central paradigm for null 1253 public Turnout getSecondTurnout() { 1254 Turnout result = null; 1255 if (secondNamedTurnout == null) { 1256 // set physical turnout if possible and needed 1257 setSecondTurnout(secondTurnoutName); 1258 } 1259 if (secondNamedTurnout != null) { 1260 result = secondNamedTurnout.getBean(); 1261 } 1262 return result; 1263 } 1264 1265 /** 1266 * @param tName provide empty string for none (not null) 1267 */ 1268 public void setSecondTurnout(@Nonnull String tName) { 1269 assert tName != null; 1270 if (tName.equals(secondTurnoutName)) { // haven't changed anything 1271 return; 1272 } 1273 1274 if (secondNamedTurnout != null) { 1275 deactivateTurnout(); 1276 Turnout turnout = secondNamedTurnout.getBean(); 1277 if (turnout.getLeadingTurnout() == namedTurnout.getBean()) { 1278 turnout.setLeadingTurnout(null); 1279 } 1280 } 1281 String oldSecondTurnoutName = secondTurnoutName; 1282 secondTurnoutName = tName; 1283 Turnout turnout = null; 1284 if (! tName.isEmpty()) { 1285 turnout = InstanceManager.turnoutManagerInstance().getTurnout(secondTurnoutName); 1286 } 1287 if (turnout != null) { 1288 secondNamedTurnout = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(secondTurnoutName, turnout); 1289 if (turnout.getFeedbackMode() == Turnout.DIRECT) { 1290 turnout.setLeadingTurnout(getTurnout(), false); 1291 } 1292 } else { 1293 secondTurnoutName = ""; 1294 secondNamedTurnout = null; 1295 } 1296 activateTurnout(); // Even if secondary is null, the primary Turnout may still need to be re-activated 1297 if (isTurnoutTypeTurnout()) { 1298 LayoutEditorFindItems lf = new LayoutEditorFindItems(models); 1299 if (oldSecondTurnoutName != null && !oldSecondTurnoutName.isEmpty()) { 1300 Turnout oldTurnout = InstanceManager.turnoutManagerInstance().getTurnout(oldSecondTurnoutName); 1301 String oldSystemName = (oldTurnout == null) ? null : oldTurnout.getSystemName(); 1302 LayoutTurnout oldLinked = (oldSystemName == null) ? null 1303 : lf.findLayoutTurnoutByTurnoutName(oldSystemName); 1304 if (oldLinked == null) { 1305 String oldUserName = (oldTurnout == null) ? null : oldTurnout.getUserName(); 1306 oldLinked = (oldUserName == null) ? null 1307 : lf.findLayoutTurnoutByTurnoutName(oldUserName); 1308 } 1309 if ((oldLinked != null) && oldLinked.getSecondTurnout() == getTurnout()) { 1310 oldLinked.setSecondTurnout(""); 1311 } 1312 } 1313 if (turnout != null) { 1314 LayoutTurnout newLinked = lf.findLayoutTurnoutByTurnoutName(turnout.getSystemName()); 1315 if (newLinked == null) { 1316 newLinked = lf.findLayoutTurnoutByTurnoutName(turnout.getUserName()); 1317 } 1318 if (newLinked != null) { 1319 newLinked.setSecondTurnout(turnoutName); 1320 } 1321 } 1322 } 1323 } 1324 1325 public void setSecondTurnoutInverted(boolean inverted) { 1326 secondTurnoutInverted = inverted; 1327 } 1328 1329 public void setContinuingSense(int sense) { 1330 continuingSense = sense; 1331 } 1332 1333 public void setDisabled(boolean state) { 1334 if (disabled != state) { 1335 disabled = state; 1336 if (models != null) { 1337 models.redrawPanel(); 1338 } 1339 } 1340 } 1341 1342 public boolean isDisabled() { 1343 return disabled; 1344 } 1345 1346 public void setDisableWhenOccupied(boolean state) { 1347 if (disableWhenOccupied != state) { 1348 disableWhenOccupied = state; 1349 if (models != null) { 1350 models.redrawPanel(); 1351 } 1352 } 1353 } 1354 1355 public boolean isDisabledWhenOccupied() { 1356 return disableWhenOccupied; 1357 } 1358 1359 /** 1360 * {@inheritDoc} 1361 */ 1362 @Override 1363 @CheckForNull 1364 public LayoutTrack getConnection(HitPointType connectionType) { 1365 LayoutTrack result = null; 1366 switch (connectionType) { 1367 case TURNOUT_A: { 1368 result = connectA; 1369 break; 1370 } 1371 case TURNOUT_B: { 1372 result = connectB; 1373 break; 1374 } 1375 case TURNOUT_C: { 1376 result = connectC; 1377 break; 1378 } 1379 case TURNOUT_D: { 1380 result = connectD; 1381 break; 1382 } 1383 default: { 1384 String errString = MessageFormat.format("{0}.getConnection({1}); Invalid Connection Type", 1385 getName(), connectionType); // I18IN 1386 log.error("will throw {}", errString); 1387 throw new IllegalArgumentException(errString); 1388 } 1389 } 1390 return result; 1391 } 1392 1393 /** 1394 * {@inheritDoc} 1395 */ 1396 @Override 1397 public void setConnection(HitPointType connectionType, @CheckForNull LayoutTrack o, HitPointType type) throws jmri.JmriException { 1398 if ((type != HitPointType.TRACK) && (type != HitPointType.NONE)) { 1399 String errString = MessageFormat.format("{0}.setConnection({1}, {2}, {3}); unexpected type", 1400 getName(), connectionType, (o == null) ? "null" : o.getName(), type, new Exception("traceback")); // I18IN 1401 log.error("will throw {}", errString); 1402 throw new jmri.JmriException(errString); 1403 } 1404 switch (connectionType) { 1405 case TURNOUT_A: 1406 connectA = o; 1407 break; 1408 case TURNOUT_B: 1409 connectB = o; 1410 break; 1411 case TURNOUT_C: 1412 connectC = o; 1413 break; 1414 case TURNOUT_D: 1415 connectD = o; 1416 break; 1417 default: 1418 String errString = MessageFormat.format("{0}.setConnection({1}, {2}, {3}); Invalid Connection Type", 1419 getName(), connectionType, (o == null) ? "null" : o.getName(), type); // I18IN 1420 log.error("will throw {}", errString); 1421 throw new jmri.JmriException(errString); 1422 } 1423 } 1424 1425 public void setConnectA(@CheckForNull LayoutTrack o, HitPointType type) { 1426 connectA = o; 1427 if ((type != HitPointType.TRACK) && (type != HitPointType.NONE)) { 1428 log.error("{}.setConnectA({}, {}); unexpected type", 1429 getName(), (o == null) ? "null" : o.getName(), type); 1430 } 1431 } 1432 1433 public void setConnectB(@CheckForNull LayoutTrack o, HitPointType type) { 1434 connectB = o; 1435 if ((type != HitPointType.TRACK) && (type != HitPointType.NONE)) { 1436 log.error("{}.setConnectB({}, {}); unexpected type", 1437 getName(), (o == null) ? "null" : o.getName(), type); 1438 } 1439 } 1440 1441 public void setConnectC(@CheckForNull LayoutTrack o, HitPointType type) { 1442 connectC = o; 1443 if ((type != HitPointType.TRACK) && (type != HitPointType.NONE)) { 1444 log.error("{}.setConnectC({}, {}); unexpected type", 1445 getName(), (o == null) ? "null" : o.getName(), type); 1446 } 1447 } 1448 1449 public void setConnectD(@CheckForNull LayoutTrack o, HitPointType type) { 1450 connectD = o; 1451 if ((type != HitPointType.TRACK) && (type != HitPointType.NONE)) { 1452 log.error("{}.setConnectD({}, {}); unexpected type", 1453 getName(), (o == null) ? "null" : o.getName(), type); 1454 } 1455 } 1456 1457 // @CheckForNull - temporary, until we can centralize protection for this 1458 public LayoutBlock getLayoutBlock() { 1459 return (namedLayoutBlockA != null) ? namedLayoutBlockA.getBean() : null; 1460 } 1461 1462 // @CheckForNull - temporary, until we can centralize protection for this 1463 public LayoutBlock getLayoutBlockB() { 1464 return (namedLayoutBlockB != null) ? namedLayoutBlockB.getBean() : getLayoutBlock(); 1465 } 1466 1467 // @CheckForNull - temporary, until we can centralize protection for this 1468 public LayoutBlock getLayoutBlockC() { 1469 return (namedLayoutBlockC != null) ? namedLayoutBlockC.getBean() : getLayoutBlock(); 1470 } 1471 1472 // @CheckForNull - temporary, until we can centralize protection for this 1473 public LayoutBlock getLayoutBlockD() { 1474 return (namedLayoutBlockD != null) ? namedLayoutBlockD.getBean() : getLayoutBlock(); 1475 } 1476 1477 // updates connectivity for blocks assigned to this turnout and connected track segments 1478 public void updateBlockInfo() { 1479 LayoutBlock bA = null; 1480 LayoutBlock bB = null; 1481 LayoutBlock bC = null; 1482 LayoutBlock bD = null; 1483 models.getLEAuxTools().setBlockConnectivityChanged(); 1484 if (getLayoutBlock() != null) { 1485 getLayoutBlock().updatePaths(); 1486 } 1487 if (connectA != null) { 1488 bA = ((TrackSegment) connectA).getLayoutBlock(); 1489 if ((bA != null) && (bA != getLayoutBlock())) { 1490 bA.updatePaths(); 1491 } 1492 } 1493 if ((getLayoutBlockB() != null) 1494 && (getLayoutBlockB() != getLayoutBlock()) 1495 && (getLayoutBlockB() != bA)) { 1496 getLayoutBlockB().updatePaths(); 1497 } 1498 if (connectB != null) { 1499 bB = ((TrackSegment) connectB).getLayoutBlock(); 1500 if ((bB != null) && (bB != getLayoutBlock()) && (bB != bA) 1501 && (bB != getLayoutBlockB())) { 1502 bB.updatePaths(); 1503 } 1504 } 1505 if ((getLayoutBlockC() != null) 1506 && (getLayoutBlockC() != getLayoutBlock()) 1507 && (getLayoutBlockC() != bA) 1508 && (getLayoutBlockC() != bB) 1509 && (getLayoutBlockC() != getLayoutBlockB())) { 1510 getLayoutBlockC().updatePaths(); 1511 } 1512 if (connectC != null) { 1513 bC = ((TrackSegment) connectC).getLayoutBlock(); 1514 if ((bC != null) && (bC != getLayoutBlock()) 1515 && (bC != bA) && (bC != getLayoutBlockB()) 1516 && (bC != bB) 1517 && (bC != getLayoutBlockC())) { 1518 bC.updatePaths(); 1519 } 1520 } 1521 if ((getLayoutBlockD() != null) 1522 && (getLayoutBlockD() != getLayoutBlock()) 1523 && (getLayoutBlockD() != bA) 1524 && (getLayoutBlockD() != bB) 1525 && (getLayoutBlockD() != getLayoutBlockB()) 1526 && (getLayoutBlockD() != bC) 1527 && (getLayoutBlockD() != getLayoutBlockC())) { 1528 getLayoutBlockD().updatePaths(); 1529 } 1530 if (connectD != null) { 1531 bD = ((TrackSegment) connectD).getLayoutBlock(); 1532 if ((bD != null) && (bD != getLayoutBlock()) 1533 && (bD != bA) && (bD != getLayoutBlockB()) 1534 && (bD != bB) && (bD != getLayoutBlockC()) 1535 && (bD != bC) && (bD != getLayoutBlockD())) { 1536 bD.updatePaths(); 1537 } 1538 } 1539 } 1540 1541 1542 /** 1543 * Set up Layout Block(s) for this Turnout. 1544 * @param newLayoutBlock the new layout block. 1545 */ 1546 protected void setLayoutBlock(LayoutBlock newLayoutBlock) { 1547 LayoutBlock blockA = getLayoutBlock(); 1548 LayoutBlock blockB = getLayoutBlockB(); 1549 LayoutBlock blockC = getLayoutBlockC(); 1550 LayoutBlock blockD = getLayoutBlockD(); 1551 if (blockA != newLayoutBlock) { 1552 // block has changed, if old block exists, decrement use 1553 if ((blockA != null) 1554 && (blockA != blockB) 1555 && (blockA != blockC) 1556 && (blockA != blockD)) { 1557 blockA.decrementUse(); 1558 } 1559 1560 blockA = newLayoutBlock; 1561 if (newLayoutBlock != null) { 1562 String userName = newLayoutBlock.getUserName(); 1563 if (userName != null) { 1564 namedLayoutBlockA = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, newLayoutBlock); 1565 } 1566 } else { 1567 namedLayoutBlockA = null; 1568 setDisableWhenOccupied(false); 1569 } 1570 // decrement use if block was already counted 1571 if ((blockA != null) 1572 && ((blockA == blockB) || (blockA == blockC) || (blockA == blockD))) { 1573 blockA.decrementUse(); 1574 } 1575 } 1576 } 1577 1578 protected void setLayoutBlockB(LayoutBlock newLayoutBlock) { 1579 if (getLayoutBlock() == null) { 1580 setLayoutBlock(newLayoutBlock); 1581 } 1582 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 1583 LayoutBlock blockA = getLayoutBlock(); 1584 LayoutBlock blockB = getLayoutBlockB(); 1585 LayoutBlock blockC = getLayoutBlockC(); 1586 LayoutBlock blockD = getLayoutBlockD(); 1587 if (blockB != newLayoutBlock) { 1588 // block has changed, if old block exists, decrement use 1589 if ((blockB != null) 1590 && (blockB != blockA) 1591 && (blockB != blockC) 1592 && (blockB != blockD)) { 1593 blockB.decrementUse(); 1594 } 1595 blockB = newLayoutBlock; 1596 if (newLayoutBlock != null) { 1597 String userName = newLayoutBlock.getUserName(); 1598 if (userName != null) { 1599 namedLayoutBlockB = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, newLayoutBlock); 1600 } 1601 } else { 1602 namedLayoutBlockB = null; 1603 } 1604 // decrement use if block was already counted 1605 if ((blockB != null) 1606 && ((blockB == blockA) || (blockB == blockC) || (blockB == blockD))) { 1607 blockB.decrementUse(); 1608 } 1609 } 1610 } else { 1611 log.error("{}.setLayoutBlockB({}); not a crossover/slip", getName(), newLayoutBlock.getUserName()); 1612 } 1613 } 1614 1615 protected void setLayoutBlockC(@CheckForNull LayoutBlock newLayoutBlock) { 1616 if (getLayoutBlock() == null) { 1617 setLayoutBlock(newLayoutBlock); 1618 } 1619 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 1620 LayoutBlock blockA = getLayoutBlock(); 1621 LayoutBlock blockB = getLayoutBlockB(); 1622 LayoutBlock blockC = getLayoutBlockC(); 1623 LayoutBlock blockD = getLayoutBlockD(); 1624 if (blockC != newLayoutBlock) { 1625 // block has changed, if old block exists, decrement use 1626 if ((blockC != null) 1627 && (blockC != blockA) 1628 && (blockC != blockB) 1629 && (blockC != blockD)) { 1630 blockC.decrementUse(); 1631 } 1632 blockC = newLayoutBlock; 1633 if (newLayoutBlock != null) { 1634 String userName = newLayoutBlock.getUserName(); 1635 if (userName != null) { 1636 namedLayoutBlockC = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, newLayoutBlock); 1637 } 1638 } else { 1639 namedLayoutBlockC = null; 1640 } 1641 // decrement use if block was already counted 1642 if ((blockC != null) 1643 && ((blockC == blockA) || (blockC == blockB) || (blockC == blockD))) { 1644 blockC.decrementUse(); 1645 } 1646 } 1647 } else { 1648 log.error("{}.setLayoutBlockC({}); not a crossover/slip", getName(), newLayoutBlock.getUserName()); 1649 } 1650 } 1651 1652 protected void setLayoutBlockD(LayoutBlock newLayoutBlock) { 1653 if (getLayoutBlock() == null) { 1654 setLayoutBlock(newLayoutBlock); 1655 } 1656 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 1657 LayoutBlock blockA = getLayoutBlock(); 1658 LayoutBlock blockB = getLayoutBlockB(); 1659 LayoutBlock blockC = getLayoutBlockC(); 1660 LayoutBlock blockD = getLayoutBlockD(); 1661 if (blockD != newLayoutBlock) { 1662 // block has changed, if old block exists, decrement use 1663 if ((blockD != null) 1664 && (blockD != blockA) 1665 && (blockD != blockB) 1666 && (blockD != blockC)) { 1667 blockD.decrementUse(); 1668 } 1669 blockD = newLayoutBlock; 1670 if (newLayoutBlock != null) { 1671 String userName = newLayoutBlock.getUserName(); 1672 if (userName != null) { 1673 namedLayoutBlockD = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, newLayoutBlock); 1674 } 1675 } else { 1676 namedLayoutBlockD = null; 1677 } 1678 // decrement use if block was already counted 1679 if ((blockD != null) 1680 && ((blockD == blockA) || (blockD == blockB) || (blockD == blockC))) { 1681 blockD.decrementUse(); 1682 } 1683 } 1684 } else { 1685 log.error("{}.setLayoutBlockD({}); not a crossover/slip", getName(), newLayoutBlock.getUserName()); 1686 } 1687 } 1688 1689 public void setLayoutBlockByName(@Nonnull String name) { 1690 setLayoutBlock(models.provideLayoutBlock(name)); 1691 } 1692 1693 public void setLayoutBlockBByName(@Nonnull String name) { 1694 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 1695 setLayoutBlockB(models.provideLayoutBlock(name)); 1696 } else { 1697 log.error("{}.setLayoutBlockBByName({}); not a crossover/slip", getName(), name); 1698 } 1699 } 1700 1701 public void setLayoutBlockCByName(@Nonnull String name) { 1702 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 1703 setLayoutBlockC(models.provideLayoutBlock(name)); 1704 } else { 1705 log.error("{}.setLayoutBlockCByName({}); not a crossover/slip", getName(), name); 1706 } 1707 } 1708 1709 public void setLayoutBlockDByName(@Nonnull String name) { 1710 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 1711 setLayoutBlockD(models.provideLayoutBlock(name)); 1712 } else { 1713 log.error("{}.setLayoutBlockDByName({}); not a crossover/slip", getName(), name); 1714 } 1715 } 1716 1717 /** 1718 * Test if turnout legs are mainline track or not. 1719 * 1720 * @return true if connecting track segment is mainline; Defaults to not 1721 * mainline if connecting track segment is missing 1722 */ 1723 public boolean isMainlineA() { 1724 if (connectA != null) { 1725 return ((TrackSegment) connectA).isMainline(); 1726 } else { 1727 // if no connection, depends on type of turnout 1728 if (isTurnoutTypeXover()) { 1729 // All crossovers - straight continuing is B 1730 if (connectB != null) { 1731 return ((TrackSegment) connectB).isMainline(); 1732 } 1733 } else if (isTurnoutTypeSlip()) { 1734 if (connectD != null) { 1735 return ((TrackSegment) connectD).isMainline(); 1736 } 1737 } // must be RH, LH, or WYE turnout - A is the switch throat 1738 else if (((connectB != null) 1739 && (((TrackSegment) connectB).isMainline())) 1740 || ((connectC != null) 1741 && (((TrackSegment) connectC).isMainline()))) { 1742 return true; 1743 } 1744 } 1745 return false; 1746 } 1747 1748 public boolean isMainlineB() { 1749 if (connectB != null) { 1750 return ((TrackSegment) connectB).isMainline(); 1751 } else { 1752 // if no connection, depends on type of turnout 1753 if (isTurnoutTypeXover()) { 1754 // All crossovers - straight continuing is A 1755 if (connectA != null) { 1756 return ((TrackSegment) connectA).isMainline(); 1757 } 1758 } else if (getTurnoutType() == TurnoutType.DOUBLE_SLIP) { 1759 if (connectD != null) { 1760 return ((TrackSegment) connectD).isMainline(); 1761 } 1762 } // must be RH, LH, or WYE turnout - A is the switch throat, 1763 // B is normally the continuing straight 1764 else if (continuingSense == Turnout.CLOSED) { 1765 // user hasn't changed the continuing turnout state 1766 if (connectA != null) { // if throat is mainline, this leg must be also 1767 return ((TrackSegment) connectA).isMainline(); 1768 } 1769 } 1770 } 1771 return false; 1772 } 1773 1774 public boolean isMainlineC() { 1775 if (connectC != null) { 1776 return ((TrackSegment) connectC).isMainline(); 1777 } else { 1778 // if no connection, depends on type of turnout 1779 if (isTurnoutTypeXover()) { 1780 // All crossovers - straight continuing is D 1781 if (connectD != null) { 1782 return ((TrackSegment) connectD).isMainline(); 1783 } 1784 } else if (getTurnoutType() == TurnoutType.DOUBLE_SLIP) { 1785 if (connectB != null) { 1786 return ((TrackSegment) connectB).isMainline(); 1787 } 1788 } // must be RH, LH, or WYE turnout - A is the switch throat, 1789 // B is normally the continuing straight 1790 else if (continuingSense == Turnout.THROWN) { 1791 // user has changed the continuing turnout state 1792 if (connectA != null) { // if throat is mainline, this leg must be also 1793 return ((TrackSegment) connectA).isMainline(); 1794 } 1795 } 1796 } 1797 return false; 1798 } 1799 1800 public boolean isMainlineD() { 1801 // this is a crossover turnout 1802 if (connectD != null) { 1803 return ((TrackSegment) connectD).isMainline(); 1804 } else if (isTurnoutTypeSlip()) { 1805 if (connectB != null) { 1806 return ((TrackSegment) connectB).isMainline(); 1807 } 1808 } else if (connectC != null) { 1809 return ((TrackSegment) connectC).isMainline(); 1810 } 1811 return false; 1812 } 1813 1814 /** 1815 * {@inheritDoc} 1816 */ 1817 @Override 1818 public boolean isMainline() { 1819 return (isMainlineA() || isMainlineB() || isMainlineC() || isMainlineD()); 1820 } 1821 1822 /** 1823 * Activate/Deactivate turnout to redraw when turnout state changes 1824 */ 1825 private void activateTurnout() { 1826 deactivateTurnout(); 1827 if (namedTurnout != null) { 1828 namedTurnout.getBean().addPropertyChangeListener( 1829 mTurnoutListener = (java.beans.PropertyChangeEvent e) -> { 1830 if (e.getNewValue() == null) { 1831 return; 1832 } 1833 if (disableWhenOccupied && isOccupied()) { 1834 return; 1835 } 1836 if (secondNamedTurnout != null) { 1837 int t1state = namedTurnout.getBean().getCommandedState(); 1838 int t2state = secondNamedTurnout.getBean().getCommandedState(); 1839 if (e.getSource().equals(namedTurnout.getBean()) 1840 && e.getNewValue().equals(t1state)) { 1841 if (secondTurnoutInverted) { 1842 t1state = Turnout.invertTurnoutState(t1state); 1843 } 1844 if (secondNamedTurnout.getBean().getCommandedState() != t1state) { 1845 secondNamedTurnout.getBean().setCommandedState(t1state); 1846 } 1847 } else if (e.getSource().equals(secondNamedTurnout.getBean()) 1848 && e.getNewValue().equals(t2state)) { 1849 if (secondTurnoutInverted) { 1850 t2state = Turnout.invertTurnoutState(t2state); 1851 } 1852 if (namedTurnout.getBean().getCommandedState() != t2state) { 1853 namedTurnout.getBean().setCommandedState(t2state); 1854 } 1855 } 1856 } 1857 models.redrawPanel(); 1858 }, 1859 namedTurnout.getName(), 1860 "Layout Editor Turnout" 1861 ); 1862 } 1863 if (secondNamedTurnout != null) { 1864 secondNamedTurnout.getBean().addPropertyChangeListener(mTurnoutListener, secondNamedTurnout.getName(), "Layout Editor Turnout"); 1865 } 1866 } 1867 1868 private void deactivateTurnout() { 1869 if (mTurnoutListener != null) { 1870 if (namedTurnout != null) { 1871 namedTurnout.getBean().removePropertyChangeListener(mTurnoutListener); 1872 } 1873 if (secondNamedTurnout != null) { 1874 secondNamedTurnout.getBean().removePropertyChangeListener(mTurnoutListener); 1875 } 1876 mTurnoutListener = null; 1877 } 1878 } 1879 1880 /** 1881 * Toggle turnout if clicked on, physical turnout exists, and not disabled. 1882 */ 1883 public void toggleTurnout() { 1884 if (getTurnout() != null) { 1885 // toggle turnout 1886 if (getTurnout().getCommandedState() == Turnout.CLOSED) { 1887 setState(Turnout.THROWN); 1888 } else { 1889 setState(Turnout.CLOSED); 1890 } 1891 } else { 1892 log.debug("Turnout Icon not associated with a Turnout"); 1893 } 1894 } 1895 1896 /** 1897 * Set the LayoutTurnout state. Used for sending the toggle command Checks 1898 * not disabled, disable when occupied Also sets secondary Turnout commanded 1899 * state 1900 * 1901 * @param state New state to set, eg Turnout.CLOSED 1902 */ 1903 public void setState(int state) { 1904 if ((getTurnout() != null) && !disabled) { 1905 if (disableWhenOccupied && isOccupied()) { 1906 log.debug("Turnout not changed as Block is Occupied"); 1907 } else { 1908 getTurnout().setCommandedState(state); 1909 Turnout secondTurnout = getSecondTurnout(); 1910 if (secondTurnout != null) { 1911 if (secondTurnoutInverted) { 1912 secondTurnout.setCommandedState(Turnout.invertTurnoutState(state)); 1913 } else { 1914 secondTurnout.setCommandedState(state); 1915 } 1916 } 1917 } 1918 } 1919 } 1920 1921 /** 1922 * Get the LayoutTurnout state 1923 * <p> 1924 * Ensures the secondary Turnout state matches the primary 1925 * 1926 * @return the state, eg Turnout.CLOSED or Turnout.INCONSISTENT 1927 */ 1928 public int getState() { 1929 int result = UNKNOWN; 1930 if (getTurnout() != null) { 1931 result = getTurnout().getKnownState(); 1932 } 1933 if (getSecondTurnout() != null) { 1934 int t2state = getSecondTurnout().getKnownState(); 1935 if (secondTurnoutInverted) { 1936 t2state = Turnout.invertTurnoutState(t2state); 1937 } 1938 if (result != t2state) { 1939 return INCONSISTENT; 1940 } 1941 } 1942 return result; 1943 } 1944 1945 /** 1946 * Is this turnout occupied? 1947 * 1948 * @return true if occupied 1949 */ 1950 boolean isOccupied() { 1951 if (isTurnoutTypeTurnout()) { 1952 if (getLayoutBlock().getOccupancy() == LayoutBlock.OCCUPIED) { 1953 log.debug("Block {} is Occupied", getBlockName()); 1954 return true; 1955 } 1956 } else if (isTurnoutTypeXover()) { 1957 // If the turnout is set for straight over, we need to deal with the straight over connecting blocks 1958 if (getTurnout().getKnownState() == Turnout.CLOSED) { 1959 if ((getLayoutBlock().getOccupancy() == LayoutBlock.OCCUPIED) 1960 && (getLayoutBlockB().getOccupancy() == LayoutBlock.OCCUPIED)) { 1961 log.debug("Blocks {} & {} are Occupied", getBlockName(), getBlockBName()); 1962 return true; 1963 } 1964 if ((getLayoutBlockC().getOccupancy() == LayoutBlock.OCCUPIED) 1965 && (getLayoutBlockD().getOccupancy() == LayoutBlock.OCCUPIED)) { 1966 log.debug("Blocks {} & {} are Occupied", getBlockCName(), getBlockDName()); 1967 return true; 1968 } 1969 } 1970 1971 } 1972 if ((getTurnoutType() == TurnoutType.DOUBLE_XOVER) 1973 || (getTurnoutType() == TurnoutType.LH_XOVER)) { 1974 if (getTurnout().getKnownState() == Turnout.THROWN) { 1975 if ((getLayoutBlockB().getOccupancy() == LayoutBlock.OCCUPIED) 1976 && (getLayoutBlockD().getOccupancy() == LayoutBlock.OCCUPIED)) { 1977 log.debug("Blocks {} & {} are Occupied", getBlockBName(), getBlockDName()); 1978 return true; 1979 } 1980 } 1981 } 1982 1983 if ((getTurnoutType() == TurnoutType.DOUBLE_XOVER) 1984 || (getTurnoutType() == TurnoutType.RH_XOVER)) { 1985 if (getTurnout().getKnownState() == Turnout.THROWN) { 1986 if ((getLayoutBlock().getOccupancy() == LayoutBlock.OCCUPIED) 1987 && (getLayoutBlockC().getOccupancy() == LayoutBlock.OCCUPIED)) { 1988 log.debug("Blocks {} & {} are Occupied", getLayoutBlock(), getBlockCName()); 1989 return true; 1990 } 1991 } 1992 } 1993 return false; 1994 } 1995 1996 // initialization instance variables (used when loading a LayoutEditor) 1997 public String connectAName = ""; 1998 public String connectBName = ""; 1999 public String connectCName = ""; 2000 public String connectDName = ""; 2001 2002 public String tBlockAName = ""; 2003 public String tBlockBName = ""; 2004 public String tBlockCName = ""; 2005 public String tBlockDName = ""; 2006 2007 /** 2008 * Initialization method. The above variables are initialized by 2009 * LayoutTurnoutXml, then the following method is called after the entire 2010 * LayoutEditor is loaded to set the specific TrackSegment objects. 2011 */ 2012 @Override 2013 public void setObjects(@Nonnull LayoutEditor p) { 2014 connectA = p.getFinder().findTrackSegmentByName(connectAName); 2015 connectB = p.getFinder().findTrackSegmentByName(connectBName); 2016 connectC = p.getFinder().findTrackSegmentByName(connectCName); 2017 connectD = p.getFinder().findTrackSegmentByName(connectDName); 2018 2019 LayoutBlock lb; 2020 if (!tBlockAName.isEmpty()) { 2021 lb = p.provideLayoutBlock(tBlockAName); 2022 if (lb != null) { 2023 String userName = lb.getUserName(); 2024 if (userName != null) { 2025 namedLayoutBlockA = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, lb); 2026 lb.incrementUse(); 2027 } 2028 } else { 2029 log.error("{}.setObjects(...); bad blockname A '{}'", getName(), tBlockAName); 2030 namedLayoutBlockA = null; 2031 } 2032 tBlockAName = null; // release this memory 2033 } 2034 2035 if (!tBlockBName.isEmpty()) { 2036 lb = p.provideLayoutBlock(tBlockBName); 2037 if (lb != null) { 2038 String userName = lb.getUserName(); 2039 if (userName != null) { 2040 namedLayoutBlockB = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, lb); 2041 } 2042 if (namedLayoutBlockB != namedLayoutBlockA) { 2043 lb.incrementUse(); 2044 } 2045 } else { 2046 log.error("{}.setObjects(...); bad blockname B '{}'", getName(), tBlockBName); 2047 namedLayoutBlockB = null; 2048 } 2049 tBlockBName = null; // release this memory 2050 } 2051 2052 if (!tBlockCName.isEmpty()) { 2053 lb = p.provideLayoutBlock(tBlockCName); 2054 if (lb != null) { 2055 String userName = lb.getUserName(); 2056 if (userName != null) { 2057 namedLayoutBlockC = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, lb); 2058 } 2059 if ((namedLayoutBlockC != namedLayoutBlockA) 2060 && (namedLayoutBlockC != namedLayoutBlockB)) { 2061 lb.incrementUse(); 2062 } 2063 } else { 2064 log.error("{}.setObjects(...); bad blockname C '{}'", getName(), tBlockCName); 2065 namedLayoutBlockC = null; 2066 } 2067 tBlockCName = null; // release this memory 2068 } 2069 2070 if (!tBlockDName.isEmpty()) { 2071 lb = p.provideLayoutBlock(tBlockDName); 2072 if (lb != null) { 2073 String userName = lb.getUserName(); 2074 if (userName != null) { 2075 namedLayoutBlockD = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(userName, lb); 2076 } 2077 if ((namedLayoutBlockD != namedLayoutBlockA) 2078 && (namedLayoutBlockD != namedLayoutBlockB) 2079 && (namedLayoutBlockD != namedLayoutBlockC)) { 2080 lb.incrementUse(); 2081 } 2082 } else { 2083 log.error("{}.setObjects(...); bad blockname D '{}'", getName(), tBlockDName); 2084 namedLayoutBlockD = null; 2085 } 2086 tBlockDName = null; // release this memory 2087 } 2088 activateTurnout(); 2089 } // setObjects 2090 2091 public String[] getBlockBoundaries() { 2092 final String[] boundaryBetween = new String[4]; 2093 if (isTurnoutTypeTurnout()) { 2094 // This should only be needed where we are looking at a single turnout. 2095 if (getLayoutBlock() != null) { 2096 LayoutBlock aLBlock = null; 2097 if (connectA instanceof TrackSegment) { 2098 aLBlock = ((TrackSegment) connectA).getLayoutBlock(); 2099 if (aLBlock != getLayoutBlock()) { 2100 try { 2101 boundaryBetween[0] = (aLBlock.getDisplayName() + " - " + getLayoutBlock().getDisplayName()); 2102 } catch (java.lang.NullPointerException e) { 2103 // Can be considered normal if tracksegement hasn't yet been allocated a block 2104 log.debug("TrackSegement at connection A doesn't contain a layout block"); 2105 } 2106 } 2107 } 2108 2109 LayoutBlock bLBlock = null; 2110 if (connectB instanceof TrackSegment) { 2111 bLBlock = ((TrackSegment) connectB).getLayoutBlock(); 2112 if (bLBlock != getLayoutBlock()) { 2113 try { 2114 boundaryBetween[1] = (bLBlock.getDisplayName() + " - " + getLayoutBlock().getDisplayName()); 2115 } catch (java.lang.NullPointerException e) { 2116 // Can be considered normal if tracksegement hasn't yet been allocated a block 2117 log.debug("TrackSegement at connection B doesn't contain a layout block"); 2118 } 2119 } 2120 } 2121 2122 LayoutBlock cLBlock = null; 2123 if ((connectC instanceof TrackSegment) 2124 && (((TrackSegment) connectC).getLayoutBlock() != getLayoutBlock())) { 2125 cLBlock = ((TrackSegment) connectC).getLayoutBlock(); 2126 if (cLBlock != getLayoutBlock()) { 2127 try { 2128 boundaryBetween[2] = (cLBlock.getDisplayName() + " - " + getLayoutBlock().getDisplayName()); 2129 } catch (java.lang.NullPointerException e) { 2130 // Can be considered normal if tracksegement hasn't yet been allocated a block 2131 log.debug("TrackSegement at connection C doesn't contain a layout block"); 2132 } 2133 } 2134 } 2135 } 2136 } else { 2137 LayoutBlock aLBlock = null; 2138 LayoutBlock bLBlock = null; 2139 LayoutBlock cLBlock = null; 2140 LayoutBlock dLBlock = null; 2141 if (getLayoutBlock() != null) { 2142 if (connectA instanceof TrackSegment) { 2143 aLBlock = ((TrackSegment) connectA).getLayoutBlock(); 2144 if (aLBlock != getLayoutBlock()) { 2145 try { 2146 boundaryBetween[0] = (aLBlock.getDisplayName() + " - " + getLayoutBlock().getDisplayName()); 2147 } catch (java.lang.NullPointerException e) { 2148 // Can be considered normal if tracksegement hasn't yet been allocated a block 2149 log.debug("TrackSegement at connection A doesn't contain a layout block"); 2150 } 2151 } else if (getLayoutBlock() != getLayoutBlockB()) { 2152 try { 2153 boundaryBetween[0] = (getLayoutBlock().getDisplayName() + " - " + getLayoutBlockB().getDisplayName()); 2154 } catch (java.lang.NullPointerException e) { 2155 // Can be considered normal if tracksegement hasn't yet been allocated a block 2156 log.debug("TrackSegement at connection A doesn't contain a layout block"); 2157 } 2158 } 2159 } 2160 2161 if (connectB instanceof TrackSegment) { 2162 bLBlock = ((TrackSegment) connectB).getLayoutBlock(); 2163 2164 if (bLBlock != getLayoutBlock() && bLBlock != getLayoutBlockB()) { 2165 try { 2166 boundaryBetween[1] = (bLBlock.getDisplayName() + " - " + getLayoutBlockB().getDisplayName()); 2167 } catch (java.lang.NullPointerException e) { 2168 // Can be considered normal if tracksegement hasn't yet been allocated a block 2169 log.debug("TrackSegement at connection B doesn't contain a layout block"); 2170 } 2171 } else if (getLayoutBlock() != getLayoutBlockB()) { 2172 // This is an interal block on the turnout 2173 try { 2174 boundaryBetween[1] = (getLayoutBlockB().getDisplayName() + " - " + getLayoutBlock().getDisplayName()); 2175 } catch (java.lang.NullPointerException e) { 2176 // Can be considered normal if tracksegement hasn't yet been allocated a block 2177 log.debug("TrackSegement at connection A doesn't contain a layout block"); 2178 } 2179 } 2180 } 2181 2182 if (connectC instanceof TrackSegment) { 2183 cLBlock = ((TrackSegment) connectC).getLayoutBlock(); 2184 if (cLBlock != getLayoutBlock() && cLBlock != getLayoutBlockB() && cLBlock != getLayoutBlockC()) { 2185 try { 2186 boundaryBetween[2] = (cLBlock.getDisplayName() + " - " + getLayoutBlockC().getDisplayName()); 2187 } catch (java.lang.NullPointerException e) { 2188 // Can be considered normal if tracksegement hasn't yet been allocated a block 2189 log.debug("TrackSegement at connection C doesn't contain a layout block"); 2190 } 2191 } else if (getLayoutBlockC() != getLayoutBlockD()) { 2192 // This is an interal block on the turnout 2193 try { 2194 boundaryBetween[2] = (getLayoutBlockC().getDisplayName() + " - " + getLayoutBlockD().getDisplayName()); 2195 } catch (java.lang.NullPointerException e) { 2196 // Can be considered normal if tracksegement hasn't yet been allocated a block 2197 log.debug("TrackSegement at connection A doesn't contain a layout block"); 2198 } 2199 } 2200 } 2201 2202 if (connectD instanceof TrackSegment) { 2203 dLBlock = ((TrackSegment) connectD).getLayoutBlock(); 2204 if (dLBlock != getLayoutBlock() && dLBlock != getLayoutBlockB() && dLBlock != getLayoutBlockC() && dLBlock != getLayoutBlockD()) { 2205 try { 2206 boundaryBetween[3] = (dLBlock.getDisplayName() + " - " + getLayoutBlockD().getDisplayName()); 2207 } catch (java.lang.NullPointerException e) { 2208 // Can be considered normal if tracksegement hasn't yet been allocated a block 2209 log.debug("TrackSegement at connection C doesn't contain a layout block"); 2210 } 2211 } else if (getLayoutBlockC() != getLayoutBlockD()) { 2212 // This is an interal block on the turnout 2213 try { 2214 boundaryBetween[3] = (getLayoutBlockD().getDisplayName() + " - " + getLayoutBlockC().getDisplayName()); 2215 } catch (java.lang.NullPointerException e) { 2216 // Can be considered normal if tracksegement hasn't yet been allocated a block 2217 log.debug("TrackSegement at connection A doesn't contain a layout block"); 2218 } 2219 } 2220 } 2221 } 2222 2223 } 2224 return boundaryBetween; 2225 } // getBlockBoundaries 2226 2227 @Nonnull 2228 public ArrayList<LayoutBlock> getProtectedBlocks(jmri.NamedBean bean) { 2229 ArrayList<LayoutBlock> ret = new ArrayList<>(2); 2230 if (getLayoutBlock() == null) { 2231 return ret; 2232 } 2233 if (isTurnoutTypeXover()) { 2234 if ((getTurnoutType() == TurnoutType.DOUBLE_XOVER || getTurnoutType() == TurnoutType.RH_XOVER) 2235 && (getSignalAMast() == bean || getSignalCMast() == bean || getSensorA() == bean || getSensorC() == bean)) { 2236 if (getSignalAMast() == bean || getSensorA() == bean) { 2237 if (connectA != null) { 2238 if (((TrackSegment) connectA).getLayoutBlock() == getLayoutBlock()) { 2239 if (getLayoutBlockB() != null && getLayoutBlock() != getLayoutBlockB() && getLayoutBlockC() != null && getLayoutBlock() != getLayoutBlockC()) { 2240 ret.add(getLayoutBlockB()); 2241 ret.add(getLayoutBlockC()); 2242 } 2243 } else { 2244 ret.add(getLayoutBlock()); 2245 } 2246 } 2247 } else { 2248 if (connectC != null && getLayoutBlockC() != null) { 2249 if (((TrackSegment) connectC).getLayoutBlock() == getLayoutBlockC()) { 2250 if (getLayoutBlockC() != getLayoutBlock() && getLayoutBlockD() != null && getLayoutBlockC() != getLayoutBlockD()) { 2251 ret.add(getLayoutBlock()); 2252 ret.add(getLayoutBlockD()); 2253 } 2254 } else { 2255 ret.add(getLayoutBlockC()); 2256 } 2257 } 2258 } 2259 } 2260 if ((getTurnoutType() == TurnoutType.DOUBLE_XOVER || getTurnoutType() == TurnoutType.LH_XOVER) 2261 && (getSignalBMast() == bean || getSignalDMast() == bean || getSensorB() == bean || getSensorD() == bean)) { 2262 if (getSignalBMast() == bean || getSensorB() == bean) { 2263 if (connectB != null && getLayoutBlockB() != null) { 2264 if (((TrackSegment) connectB).getLayoutBlock() == getLayoutBlockB()) { 2265 if (getLayoutBlock() != getLayoutBlockB() && getLayoutBlockD() != null && getLayoutBlockB() != getLayoutBlockD()) { 2266 ret.add(getLayoutBlock()); 2267 ret.add(getLayoutBlockD()); 2268 } 2269 } else { 2270 ret.add(getLayoutBlockB()); 2271 } 2272 } 2273 } else { 2274 if (connectD != null && getLayoutBlockD() != null) { 2275 if (((TrackSegment) connectD).getLayoutBlock() == getLayoutBlockD()) { 2276 if (getLayoutBlockB() != null && getLayoutBlockB() != getLayoutBlockD() && getLayoutBlockC() != null && getLayoutBlockC() != getLayoutBlockD()) { 2277 ret.add(getLayoutBlockB()); 2278 ret.add(getLayoutBlockC()); 2279 } 2280 } else { 2281 ret.add(getLayoutBlockD()); 2282 } 2283 } 2284 } 2285 } 2286 if (getTurnoutType() == TurnoutType.RH_XOVER && (getSignalBMast() == bean 2287 || getSignalDMast() == bean || getSensorB() == bean || getSensorD() == bean)) { 2288 if (getSignalBMast() == bean || getSensorB() == bean) { 2289 if (connectB != null && ((TrackSegment) connectB).getLayoutBlock() == getLayoutBlockB()) { 2290 if (getLayoutBlockB() != getLayoutBlock()) { 2291 ret.add(getLayoutBlock()); 2292 } 2293 } else { 2294 ret.add(getLayoutBlockB()); 2295 } 2296 } else { 2297 if (connectD != null && ((TrackSegment) connectD).getLayoutBlock() == getLayoutBlockD()) { 2298 if (getLayoutBlockC() != getLayoutBlockD()) { 2299 ret.add(getLayoutBlockC()); 2300 } 2301 } else { 2302 ret.add(getLayoutBlockD()); 2303 } 2304 } 2305 } 2306 if (getTurnoutType() == TurnoutType.LH_XOVER && (getSensorA() == bean 2307 || getSensorC() == bean || getSignalAMast() == bean || getSignalCMast() == bean)) { 2308 if (getSignalAMast() == bean || getSensorA() == bean) { 2309 if (connectA != null && ((TrackSegment) connectA).getLayoutBlock() == getLayoutBlock()) { 2310 if (getLayoutBlockB() != getLayoutBlock()) { 2311 ret.add(getLayoutBlockB()); 2312 } 2313 } else { 2314 ret.add(getLayoutBlock()); 2315 } 2316 } else { 2317 if (connectC != null && ((TrackSegment) connectC).getLayoutBlock() == getLayoutBlockC()) { 2318 if (getLayoutBlockC() != getLayoutBlockD()) { 2319 ret.add(getLayoutBlockD()); 2320 } 2321 } else { 2322 ret.add(getLayoutBlockC()); 2323 } 2324 } 2325 } 2326 } else { 2327 if (connectA != null) { 2328 if (getSignalAMast() == bean || getSensorA() == bean) { 2329 // Mast at throat 2330 // if the turnout is in the same block as the segment connected at the throat, then we can be protecting two blocks 2331 if (((TrackSegment) connectA).getLayoutBlock() == getLayoutBlock()) { 2332 if (connectB != null && connectC != null) { 2333 if (((TrackSegment) connectB).getLayoutBlock() != getLayoutBlock() 2334 && ((TrackSegment) connectC).getLayoutBlock() != getLayoutBlock()) { 2335 ret.add(((TrackSegment) connectB).getLayoutBlock()); 2336 ret.add(((TrackSegment) connectC).getLayoutBlock()); 2337 } 2338 } 2339 } else { 2340 ret.add(getLayoutBlock()); 2341 } 2342 } else if (getSignalBMast() == bean || getSensorB() == bean) { 2343 // Mast at Continuing 2344 if (connectB != null && ((TrackSegment) connectB).getLayoutBlock() == getLayoutBlock()) { 2345 if (((TrackSegment) connectA).getLayoutBlock() != getLayoutBlock()) { 2346 ret.add(((TrackSegment) connectA).getLayoutBlock()); 2347 } 2348 } else { 2349 ret.add(getLayoutBlock()); 2350 } 2351 } else if (getSignalCMast() == bean || getSensorC() == bean) { 2352 // Mast at Diverging 2353 if (connectC != null && ((TrackSegment) connectC).getLayoutBlock() == getLayoutBlock()) { 2354 if (((TrackSegment) connectA).getLayoutBlock() != getLayoutBlock()) { 2355 ret.add(((TrackSegment) connectA).getLayoutBlock()); 2356 } 2357 } else { 2358 ret.add(getLayoutBlock()); 2359 } 2360 } 2361 } 2362 } 2363 return ret; 2364 } // getProtectedBlocks 2365 2366 protected void removeSML(@CheckForNull SignalMast signalMast) { 2367 if (signalMast == null) { 2368 return; 2369 2370 } 2371 if (jmri.InstanceManager.getDefault(LayoutBlockManager.class).isAdvancedRoutingEnabled() 2372 && InstanceManager.getDefault(jmri.SignalMastLogicManager.class).isSignalMastUsed(signalMast)) { 2373 2374 SignallingGuiTools.removeSignalMastLogic(null, signalMast); 2375 } 2376 } 2377 2378 /** 2379 * Remove this object from display and persistance. 2380 */ 2381 public void remove() { 2382 // if a turnout has been activated, deactivate it 2383 deactivateTurnout(); 2384 // remove from persistance by flagging inactive 2385 active = false; 2386 } 2387 2388 boolean active = true; 2389 2390 /** 2391 * "active" means that the object is still displayed, and should be stored. 2392 * @return true if active, else false. 2393 */ 2394 public boolean isActive() { 2395 return active; 2396 } 2397 2398 2399 /* 2400 * Used by ConnectivityUtil to determine the turnout state necessary to get 2401 * from prevLayoutBlock ==> currLayoutBlock ==> nextLayoutBlock 2402 */ 2403 protected int getConnectivityStateForLayoutBlocks( 2404 LayoutBlock currLayoutBlock, 2405 LayoutBlock prevLayoutBlock, 2406 LayoutBlock nextLayoutBlock, 2407 boolean suppress) { 2408 int result = UNKNOWN; 2409 2410 LayoutBlock layoutBlockA = ((TrackSegment) getConnectA()).getLayoutBlock(); 2411 LayoutBlock layoutBlockB = ((TrackSegment) getConnectB()).getLayoutBlock(); 2412 LayoutBlock layoutBlockC = ((TrackSegment) getConnectC()).getLayoutBlock(); 2413 // TODO: Determine if this should be being used 2414 // LayoutBlock layoutBlockD = ((TrackSegment) getConnectD()).getLayoutBlock(); 2415 2416 TurnoutType tTyp = getTurnoutType(); 2417 switch (tTyp) { 2418 case RH_TURNOUT: 2419 case LH_TURNOUT: 2420 case WYE_TURNOUT: { 2421 if (layoutBlockA == currLayoutBlock) { 2422 if ((layoutBlockC == nextLayoutBlock) || (layoutBlockC == prevLayoutBlock)) { 2423 result = Turnout.THROWN; 2424 } else if ((layoutBlockB == nextLayoutBlock) || (layoutBlockB == prevLayoutBlock)) { 2425 result = Turnout.CLOSED; 2426 } else if (layoutBlockB == currLayoutBlock) { 2427 result = Turnout.CLOSED; 2428 } else if (layoutBlockC == currLayoutBlock) { 2429 result = Turnout.THROWN; 2430 } else { 2431 if (!suppress) { 2432 log.error("{}.getConnectivityStateForLayoutBlocks(...); Cannot determine turnout setting for {}", 2433 getName(), getTurnoutName()); 2434 } 2435 result = Turnout.CLOSED; 2436 } 2437 } else if (layoutBlockB == currLayoutBlock) { 2438 result = Turnout.CLOSED; 2439 } else if (layoutBlockC == currLayoutBlock) { 2440 result = Turnout.THROWN; 2441 } else { 2442 if (!suppress) { 2443 log.debug("lb {} nlb {} connect B {} connect C {}", currLayoutBlock, nextLayoutBlock, layoutBlockB, layoutBlockC); 2444 log.error("{}.getConnectivityStateForLayoutBlocks(...); Cannot determine turnout setting for {}", 2445 getName(), getTurnoutName()); 2446 } 2447 result = Turnout.CLOSED; 2448 } 2449 break; 2450 } 2451 case RH_XOVER: 2452 case LH_XOVER: 2453 case DOUBLE_XOVER: { 2454 if (getLayoutBlock() == currLayoutBlock) { 2455 if ((tTyp != TurnoutType.LH_XOVER) 2456 && ((getLayoutBlockC() == nextLayoutBlock) 2457 || (getLayoutBlockC() == prevLayoutBlock))) { 2458 result = Turnout.THROWN; 2459 } else if ((getLayoutBlockB() == nextLayoutBlock) || (getLayoutBlockB() == prevLayoutBlock)) { 2460 result = Turnout.CLOSED; 2461 } else if (getLayoutBlockB() == currLayoutBlock) { 2462 result = Turnout.CLOSED; 2463 } else if ((tTyp != LayoutTurnout.TurnoutType.LH_XOVER) 2464 && (getLayoutBlockC() == currLayoutBlock)) { 2465 result = Turnout.THROWN; 2466 } else { 2467 if (!suppress) { 2468 log.error("{}.getConnectivityStateForLayoutBlocks(...); Cannot determine turnout setting for {}", 2469 getName(), getTurnoutName()); 2470 } 2471 result = Turnout.CLOSED; 2472 } 2473 } else if (getLayoutBlockB() == currLayoutBlock) { 2474 if ((getLayoutBlock() == nextLayoutBlock) || (getLayoutBlock() == prevLayoutBlock)) { 2475 result = Turnout.CLOSED; 2476 } else if ((tTyp != TurnoutType.RH_XOVER) 2477 && ((getLayoutBlockD() == nextLayoutBlock) 2478 || (getLayoutBlockD() == prevLayoutBlock) || (getLayoutBlockD() == currLayoutBlock))) { 2479 result = Turnout.THROWN; 2480 } else { 2481 if (!suppress) { 2482 log.error("{}.getConnectivityStateForLayoutBlocks(...); Cannot determine turnout setting for {}", 2483 getName(), getTurnoutName()); 2484 } 2485 result = Turnout.CLOSED; 2486 } 2487 } else if (getLayoutBlockC() == currLayoutBlock) { 2488 if ((tTyp != TurnoutType.LH_XOVER) 2489 && ((getLayoutBlock() == nextLayoutBlock) || (getLayoutBlock() == prevLayoutBlock))) { 2490 result = Turnout.THROWN; 2491 } else if ((getLayoutBlockD() == nextLayoutBlock) || (getLayoutBlockD() == prevLayoutBlock) || (getLayoutBlockD() == currLayoutBlock)) { 2492 result = Turnout.CLOSED; 2493 } else if ((tTyp != TurnoutType.LH_XOVER) 2494 && (getLayoutBlockD() == currLayoutBlock)) { 2495 result = Turnout.THROWN; 2496 } else { 2497 if (!suppress) { 2498 log.error("{}.getConnectivityStateForLayoutBlocks(...); Cannot determine turnout setting for {}", 2499 getName(), getTurnoutName()); 2500 } 2501 result = Turnout.CLOSED; 2502 } 2503 } else if (getLayoutBlockD() == currLayoutBlock) { 2504 if ((getLayoutBlockC() == nextLayoutBlock) || (getLayoutBlockC() == prevLayoutBlock)) { 2505 result = Turnout.CLOSED; 2506 } else if ((tTyp != TurnoutType.RH_XOVER) 2507 && ((getLayoutBlockB() == nextLayoutBlock) || (getLayoutBlockB() == prevLayoutBlock))) { 2508 result = Turnout.THROWN; 2509 } else { 2510 if (!suppress) { 2511 log.error("{}.getConnectivityStateForLayoutBlocks(...); Cannot determine turnout setting for {}", 2512 getName(), getTurnoutName()); 2513 } 2514 result = Turnout.CLOSED; 2515 } 2516 } 2517 break; 2518 } 2519 default: { 2520 log.warn("{}.getConnectivityStateForLayoutBlocks(...) unknown getTurnoutType: {}", getName(), tTyp); 2521 break; 2522 } 2523 } // switch (tTyp) 2524 2525 return result; 2526 } // getConnectivityStateForLayoutBlocks 2527 2528 /** 2529 * {@inheritDoc} 2530 */ 2531 // TODO: on the cross-overs, check the internal boundary details. 2532 @Override 2533 public void reCheckBlockBoundary() { 2534 if (connectA == null && connectB == null && connectC == null) { 2535 if (isTurnoutTypeTurnout()) { 2536 if (signalAMastNamed != null) { 2537 removeSML(getSignalAMast()); 2538 } 2539 if (signalBMastNamed != null) { 2540 removeSML(getSignalBMast()); 2541 } 2542 if (signalCMastNamed != null) { 2543 removeSML(getSignalCMast()); 2544 } 2545 signalAMastNamed = null; 2546 signalBMastNamed = null; 2547 signalCMastNamed = null; 2548 sensorANamed = null; 2549 sensorBNamed = null; 2550 sensorCNamed = null; 2551 return; 2552 } else if (isTurnoutTypeXover() && connectD == null) { 2553 if (signalAMastNamed != null) { 2554 removeSML(getSignalAMast()); 2555 } 2556 if (signalBMastNamed != null) { 2557 removeSML(getSignalBMast()); 2558 } 2559 if (signalCMastNamed != null) { 2560 removeSML(getSignalCMast()); 2561 } 2562 if (signalDMastNamed != null) { 2563 removeSML(getSignalDMast()); 2564 } 2565 signalAMastNamed = null; 2566 signalBMastNamed = null; 2567 signalCMastNamed = null; 2568 signalDMastNamed = null; 2569 sensorANamed = null; 2570 sensorBNamed = null; 2571 sensorCNamed = null; 2572 sensorDNamed = null; 2573 return; 2574 } 2575 } 2576 2577 if (connectA == null || connectB == null || connectC == null) { 2578 // could still be in the process of rebuilding. 2579 return; 2580 } else if ((connectD == null) && isTurnoutTypeXover()) { 2581 // could still be in the process of rebuilding. 2582 return; 2583 } 2584 2585 TrackSegment trkA; 2586 TrackSegment trkB; 2587 TrackSegment trkC; 2588 TrackSegment trkD; 2589 2590 if (connectA instanceof TrackSegment) { 2591 trkA = (TrackSegment) connectA; 2592 if (trkA.getLayoutBlock() == getLayoutBlock()) { 2593 if (signalAMastNamed != null) { 2594 removeSML(getSignalAMast()); 2595 } 2596 signalAMastNamed = null; 2597 sensorANamed = null; 2598 } 2599 } 2600 if (connectB instanceof TrackSegment) { 2601 trkB = (TrackSegment) connectB; 2602 if (trkB.getLayoutBlock() == getLayoutBlock() || trkB.getLayoutBlock() == getLayoutBlockB()) { 2603 if (signalBMastNamed != null) { 2604 removeSML(getSignalBMast()); 2605 } 2606 signalBMastNamed = null; 2607 sensorBNamed = null; 2608 2609 } 2610 } 2611 if (connectC instanceof TrackSegment) { 2612 trkC = (TrackSegment) connectC; 2613 if (trkC.getLayoutBlock() == getLayoutBlock() 2614 || trkC.getLayoutBlock() == getLayoutBlockB() 2615 || trkC.getLayoutBlock() == getLayoutBlockC()) { 2616 if (signalCMastNamed != null) { 2617 removeSML(getSignalCMast()); 2618 } 2619 signalCMastNamed = null; 2620 sensorCNamed = null; 2621 2622 } 2623 } 2624 if (connectD != null && connectD instanceof TrackSegment 2625 && isTurnoutTypeXover()) { 2626 trkD = (TrackSegment) connectD; 2627 if (trkD.getLayoutBlock() == getLayoutBlock() 2628 || trkD.getLayoutBlock() == getLayoutBlockB() 2629 || trkD.getLayoutBlock() == getLayoutBlockC() 2630 || trkD.getLayoutBlock() == getLayoutBlockD()) { 2631 if (signalDMastNamed != null) { 2632 removeSML(getSignalDMast()); 2633 } 2634 signalDMastNamed = null; 2635 sensorDNamed = null; 2636 } 2637 } 2638 } // reCheckBlockBoundary 2639 2640 /** 2641 * {@inheritDoc} 2642 */ 2643 @Override 2644 @Nonnull 2645 protected List<LayoutConnectivity> getLayoutConnectivity() { 2646 List<LayoutConnectivity> results = new ArrayList<>(); 2647 2648 log.trace("Start in layoutTurnout.getLayoutConnectivity for {}", getName()); 2649 2650 LayoutConnectivity lc = null; 2651 2652 LayoutBlock lbA = getLayoutBlock(), lbB = getLayoutBlockB(), lbC = getLayoutBlockC(), lbD = getLayoutBlockD(); 2653 2654 log.trace(" type: {}", type); 2655 log.trace(" lbA: {}", lbA); 2656 log.trace(" lbB: {}", lbB); 2657 log.trace(" lbC: {}", lbC); 2658 log.trace(" lbD: {}", lbD); 2659 2660 if (hasEnteringDoubleTrack() && (lbA != null)) { 2661 // have a crossover turnout with at least one block, check for multiple blocks 2662 if ((lbA != lbB) || (lbA != lbC) || (lbA != lbD)) { 2663 // have multiple blocks and therefore internal block boundaries 2664 if (lbA != lbB) { 2665 // have a AB block boundary, create a LayoutConnectivity 2666 log.debug("Block boundary ('{}'<->'{}') found at {}", lbA, lbB, this); 2667 lc = new LayoutConnectivity(lbA, lbB); 2668 lc.setXoverBoundary(this, LayoutConnectivity.XOVER_BOUNDARY_AB); 2669 2670 // The following line needed to change, because it uses location of 2671 // the points on the TurnoutView itself. Change to 2672 // direction from connections. 2673 //lc.setDirection(Path.computeDirection(getCoordsA(), getCoordsB())); 2674 lc.setDirection(models.computeDirectionAB(this)); 2675 2676 log.trace("getLayoutConnectivity lbA != lbB"); 2677 log.trace(" Block boundary ('{}'<->'{}') found at {}", lbA, lbB, this); 2678 2679 results.add(lc); 2680 } 2681 if ((getTurnoutType() != TurnoutType.LH_XOVER) && (lbA != lbC)) { 2682 // have a AC block boundary, create a LayoutConnectivity 2683 log.debug("Block boundary ('{}'<->'{}') found at {}", lbA, lbC, this); 2684 lc = new LayoutConnectivity(lbA, lbC); 2685 lc.setXoverBoundary(this, LayoutConnectivity.XOVER_BOUNDARY_AC); 2686 2687 // The following line needed to change, because it uses location of 2688 // the points on the TurnoutView itself. Change to 2689 // direction from connections. 2690 //lc.setDirection(Path.computeDirection(getCoordsA(), getCoordsC())); 2691 lc.setDirection(models.computeDirectionAC(this)); 2692 2693 log.trace("getLayoutConnectivity lbA != lbC"); 2694 log.trace(" Block boundary ('{}'<->'{}') found at {}", lbA, lbC, this); 2695 2696 results.add(lc); 2697 } 2698 if (lbC != lbD) { 2699 // have a CD block boundary, create a LayoutConnectivity 2700 log.debug("Block boundary ('{}'<->'{}') found at {}", lbC, lbD, this); 2701 lc = new LayoutConnectivity(lbC, lbD); 2702 lc.setXoverBoundary(this, LayoutConnectivity.XOVER_BOUNDARY_CD); 2703 2704 // The following line needed to change, because it uses location of 2705 // the points on the TurnoutView itself. Change to 2706 // direction from connections. 2707 //lc.setDirection(Path.computeDirection(getCoordsC(), getCoordsD())); 2708 lc.setDirection(models.computeDirectionCD(this)); 2709 2710 log.trace("getLayoutConnectivity lbC != lbD"); 2711 log.trace(" Block boundary ('{}'<->'{}') found at {}", lbC, lbD, this); 2712 2713 results.add(lc); 2714 } 2715 if ((getTurnoutType() != TurnoutType.RH_XOVER) && (lbB != lbD)) { 2716 // have a BD block boundary, create a LayoutConnectivity 2717 log.debug("Block boundary ('{}'<->'{}') found at {}", lbB, lbD, this); 2718 lc = new LayoutConnectivity(lbB, lbD); 2719 lc.setXoverBoundary(this, LayoutConnectivity.XOVER_BOUNDARY_BD); 2720 2721 // The following line needed to change, because it uses location of 2722 // the points on the TurnoutView itself. Change to 2723 // direction from connections. 2724 //lc.setDirection(Path.computeDirection(getCoordsB(), getCoordsD())); 2725 lc.setDirection(models.computeDirectionBD(this)); 2726 2727 log.trace("getLayoutConnectivity lbB != lbD"); 2728 log.trace(" Block boundary ('{}'<->'{}') found at {}", lbB, lbD, this); 2729 2730 results.add(lc); 2731 } 2732 } 2733 } 2734 return results; 2735 } 2736 2737 /** 2738 * {@inheritDoc} 2739 */ 2740 @Override 2741 @Nonnull 2742 public List<HitPointType> checkForFreeConnections() { 2743 List<HitPointType> result = new ArrayList<>(); 2744 2745 // check the A connection point 2746 if (getConnectA() == null) { 2747 result.add(HitPointType.TURNOUT_A); 2748 } 2749 2750 // check the B connection point 2751 if (getConnectB() == null) { 2752 result.add(HitPointType.TURNOUT_B); 2753 } 2754 2755 // check the C connection point 2756 if (getConnectC() == null) { 2757 result.add(HitPointType.TURNOUT_C); 2758 } 2759 2760 // check the D connection point 2761 if (isTurnoutTypeXover()) { 2762 if (getConnectD() == null) { 2763 result.add(HitPointType.TURNOUT_D); 2764 } 2765 } 2766 return result; 2767 } 2768 2769 /** 2770 * {@inheritDoc} 2771 */ 2772 @Override 2773 public boolean checkForUnAssignedBlocks() { 2774 // because getLayoutBlock[BCD] will return block [A] if they're null 2775 // we only need to test block [A] 2776 return (getLayoutBlock() != null); 2777 } 2778 2779 /** 2780 * {@inheritDoc} 2781 */ 2782 @Override 2783 public void checkForNonContiguousBlocks( 2784 @Nonnull HashMap<String, List<Set<String>>> blockNamesToTrackNameSetsMap) { 2785 /* 2786 * For each (non-null) blocks of this track do: 2787 * #1) If it's got an entry in the blockNamesToTrackNameSetMap then 2788 * #2) If this track is already in the TrackNameSet for this block 2789 * then return (done!) 2790 * #3) else add a new set (with this block/track) to 2791 * blockNamesToTrackNameSetMap and check all the connections in this 2792 * block (by calling the 2nd method below) 2793 * <p> 2794 * Basically, we're maintaining contiguous track sets for each block found 2795 * (in blockNamesToTrackNameSetMap) 2796 */ 2797 2798 // We're only using a map here because it's convient to 2799 // use it to pair up blocks and connections 2800 Map<LayoutTrack, String> blocksAndTracksMap = new HashMap<>(); 2801 if (connectA != null) { 2802 blocksAndTracksMap.put(connectA, getBlockName()); 2803 } 2804 if (connectB != null) { 2805 blocksAndTracksMap.put(connectB, getBlockBName()); 2806 } 2807 if (connectC != null) { 2808 blocksAndTracksMap.put(connectC, getBlockCName()); 2809 } 2810 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 2811 if (connectD != null) { 2812 blocksAndTracksMap.put(connectD, getBlockDName()); 2813 } 2814 } 2815 List<Set<String>> TrackNameSets = null; 2816 Set<String> TrackNameSet = null; 2817 for (Map.Entry<LayoutTrack, String> entry : blocksAndTracksMap.entrySet()) { 2818 LayoutTrack theConnect = entry.getKey(); 2819 String theBlockName = entry.getValue(); 2820 2821 TrackNameSet = null; // assume not found (pessimist!) 2822 TrackNameSets = blockNamesToTrackNameSetsMap.get(theBlockName); 2823 if (TrackNameSets != null) { // (#1) 2824 for (Set<String> checkTrackNameSet : TrackNameSets) { 2825 if (checkTrackNameSet.contains(getName())) { // (#2) 2826 TrackNameSet = checkTrackNameSet; 2827 break; 2828 } 2829 } 2830 } else { // (#3) 2831 log.debug("*New block ('{}') trackNameSets", theBlockName); 2832 TrackNameSets = new ArrayList<>(); 2833 blockNamesToTrackNameSetsMap.put(theBlockName, TrackNameSets); 2834 } 2835 if (TrackNameSet == null) { 2836 TrackNameSet = new LinkedHashSet<>(); 2837 TrackNameSets.add(TrackNameSet); 2838 } 2839 if (TrackNameSet.add(getName())) { 2840 log.debug("* Add track '{}' to trackNameSet for block '{}'", getName(), theBlockName); 2841 } 2842 theConnect.collectContiguousTracksNamesInBlockNamed(theBlockName, TrackNameSet); 2843 } 2844 } // collectContiguousTracksNamesInBlockNamed 2845 2846 /** 2847 * {@inheritDoc} 2848 */ 2849 @Override 2850 public void collectContiguousTracksNamesInBlockNamed( 2851 @Nonnull String blockName, 2852 @Nonnull Set<String> TrackNameSet) { 2853 if (!TrackNameSet.contains(getName())) { 2854 2855 // create list of our connects 2856 List<LayoutTrack> connects = new ArrayList<>(); 2857 if (getBlockName().equals(blockName) 2858 && (connectA != null)) { 2859 connects.add(connectA); 2860 } 2861 if (getBlockBName().equals(blockName) 2862 && (connectB != null)) { 2863 connects.add(connectB); 2864 } 2865 if (getBlockCName().equals(blockName) 2866 && (connectC != null)) { 2867 connects.add(connectC); 2868 } 2869 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 2870 if (getBlockDName().equals(blockName) 2871 && (connectD != null)) { 2872 connects.add(connectD); 2873 } 2874 } 2875 2876 for (LayoutTrack connect : connects) { 2877 // if we are added to the TrackNameSet 2878 if (TrackNameSet.add(getName())) { 2879 log.debug("* Add track '{}' for block '{}'", getName(), blockName); 2880 } 2881 // it's time to play... flood your neighbour! 2882 connect.collectContiguousTracksNamesInBlockNamed(blockName, TrackNameSet); 2883 } 2884 } 2885 } 2886 2887 /** 2888 * {@inheritDoc} 2889 */ 2890 @Override 2891 public void setAllLayoutBlocks(LayoutBlock layoutBlock) { 2892 setLayoutBlock(layoutBlock); 2893 if (isTurnoutTypeXover() || isTurnoutTypeSlip()) { 2894 setLayoutBlockB(layoutBlock); 2895 setLayoutBlockC(layoutBlock); 2896 setLayoutBlockD(layoutBlock); 2897 } 2898 } 2899 2900 /** 2901 * {@inheritDoc} 2902 */ 2903 @Override 2904 public String getTypeName() { 2905 return Bundle.getMessage("TypeName_Turnout"); 2906 } 2907 2908 // Tooltip support 2909 private ToolTip _tooltip; 2910 private boolean _showTooltip; 2911 2912 public void setShowToolTip(boolean set) { 2913 _showTooltip = set; 2914 } 2915 2916 public boolean showToolTip() { 2917 return _showTooltip; 2918 } 2919 2920 public void setToolTip(ToolTip tip) { 2921 _tooltip = tip; 2922 } 2923 2924 public ToolTip getToolTip() { 2925 return _tooltip; 2926 } 2927 2928 @Nonnull 2929 public String getNameString() { 2930 var turnout = getTurnout(); 2931 if (turnout != null) { 2932 return turnout.getDisplayName(jmri.NamedBean.DisplayOptions.USERNAME_SYSTEMNAME); 2933 } 2934 return getId(); 2935 } 2936 2937 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LayoutTurnout.class); 2938}