001package jmri; 002 003import java.util.Set; 004import javax.annotation.Nonnull; 005import javax.annotation.CheckForNull; 006 007/** 008 * Represent a Turnout on the layout. 009 * <p> 010 * A Turnout has two states: 011 * <ul> 012 * <li>The "commandedState" records the state that's been commanded in the 013 * program. It might take some time, perhaps a long time, for that to actually 014 * take effect. 015 * <li>The "knownState" is the program's best idea of the actual state on the 016 * the layout. 017 * </ul> 018 * <p> 019 * There are a number of reasons that commandedState and knownState differ: 020 * <ul> 021 * <li>A change has been commanded, but it hasn't had time to happen yet 022 * <li>Something has gone wrong, and a commanded change isn't actually going to 023 * happen 024 * <li>Although the program hasn't commanded a change, something on the layout 025 * has made the turnout change. This could be a local electrical button, a 026 * mechanical movement of the points, or something else. 027 * <li>For a bus-like system, e.g. LocoNet or XpressNet, some other device might 028 * have sent a command to change the turnout. 029 * </ul> 030 * <p> 031 * Turnout feedback is involved in the connection between these two states; for 032 * more information see the 033 * <a href="http://jmri.org/help/en/html/doc/Technical/TurnoutFeedback.shtml">feedback 034 * page</a>. 035 * <p> 036 * The AbstractTurnout class contains a basic implementation of the state and 037 * messaging code, and forms a useful start for a system-specific 038 * implementation. Specific implementations, e.g. for 039 * LocoNet and NCE, will convert to and from the layout commands. 040 * <p> 041 * The states and names are Java Bean parameters, so that listeners can be 042 * registered to be notified of any changes. 043 * <p> 044 * A sample use of the Turnout interface can be seen in the 045 * jmri.jmrit.simpleturnoutctrl.SimpleTurnoutCtrlFrame class, which provides a 046 * simple GUI for controlling a single turnout. 047 * <p> 048 * Each Turnout object has a two names. The "user" name is entirely free form, 049 * and can be used for any purpose. The "system" name is provided by the 050 * system-specific implementations, and provides a unique mapping to the layout 051 * control system (for example LocoNet or NCE) and address within that system. 052 * <p> 053 * Turnouts exhibit some complex behaviors. At the same time, they are sometimes 054 * used as generic binary outputs where those get in the way. Eventually, we 055 * need to have a separate e.g. Output class, but for now you can defeat much of 056 * the advanced behaviors with the setBinaryOutput(true) method. This is a 057 * configuration property; changing it on the fly may give unexpected results. 058 * It's value is not persisted. 059 * <p> 060 * This file is part of JMRI. 061 * <p> 062 * JMRI is free software; you can redistribute it and/or modify it under the 063 * terms of version 2 of the GNU General Public License as published by the Free 064 * Software Foundation. See the "COPYING" file for a copy of this license. 065 * <p> 066 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 067 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 068 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 069 * 070 * @author Bob Jacobsen Copyright (C) 2001 071 * @see jmri.TurnoutManager 072 * @see jmri.InstanceManager 073 * @see jmri.jmrit.simpleturnoutctrl.SimpleTurnoutCtrlFrame 074 */ 075public interface Turnout extends DigitalIO, VariableControlSpanBean { 076 077 /** 078 * Constant representing a "closed" state, either in readback or as a 079 * commanded state. Note that it's possible to be both CLOSED and THROWN at 080 * the same time on some systems, which should be called INCONSISTENT 081 */ 082 static final int CLOSED = DigitalIO.ON; 083 084 /** 085 * Constant representing a "thrown" state, either in readback or as a 086 * commanded state. Note that it's possible to be both CLOSED and THROWN at 087 * the same time on some systems, which should be called INCONSISTENT 088 */ 089 static final int THROWN = DigitalIO.OFF; 090 091 /** 092 * Constant representing "direct feedback method". In this case, the 093 * commanded state is provided when the known state is requested. The two 094 * states never differ. This mode is always possible! 095 */ 096 static final int DIRECT = 1; 097 098 /** 099 * Constant representing "exact feedback method". In this case, the layout 100 * hardware can sense both positions of the turnout, which is used to set 101 * the known state. 102 */ 103 static final int EXACT = 2; 104 105 /** 106 * Constant representing "indirect feedback". In this case, the layout 107 * hardware can only sense one setting of the turnout. The known state is 108 * inferred from that info. 109 */ 110 static final int INDIRECT = 4; // only one side directly sensed 111 112 /** 113 * Constant representing "feedback by monitoring sent commands". In this 114 * case, the known state tracks commands seen on the rails or bus. 115 */ 116 static final int MONITORING = 8; 117 118 /** 119 * Constant representing "feedback by monitoring one sensor". The sensor 120 * sets the state CLOSED when INACTIVE and THROWN when ACTIVE 121 */ 122 static final int ONESENSOR = 16; 123 124 /** 125 * Constant representing "feedback by monitoring two sensors". The first 126 * sensor sets the state THROWN when ACTIVE; the second sensor sets the 127 * state CLOSED when ACTIVE. 128 */ 129 static final int TWOSENSOR = 32; 130 131 /** 132 * Constant representing "feedback for signals" . This is DIRECT feedback, 133 * with minimal delay (for use with systems that wait for responses returned 134 * by from the command station). 135 */ 136 static final int SIGNAL = 64; 137 138 /** 139 * Constant representing "automatic delayed feedback" . This is DIRECT feedback 140 * with a fixed delay before the feedback (known state) takes effect. 141 */ 142 static final int DELAYED = 128; 143 144 /** 145 * Constant representing "loconet alternate feedback method". In this case, the layout 146 * hardware can sense both positions of the turnout, which is used to set 147 * the known state. Hardware use OPS_SW_REP alternate message. 148 */ 149 static final int LNALTERNATE = 256; 150 151 /** 152 * Constant representing "CS VPIN output mode". The command station drives a 153 * VPIN directly via the {@code <z>} command with no pre-definition required 154 * in the CS output table. Differs from {@link #DIRECT} (which sends a DCC 155 * accessory packet); this sends a VPIN pin-control command resolved by the 156 * CS HAL layer at runtime. The known state is updated optimistically on send. 157 */ 158 static final int CS_VPIN = 512; 159 160 /** 161 * Constant representing turnout lockout cab commands 162 */ 163 static final int CABLOCKOUT = 1; 164 165 /** 166 * Constant representing turnout lockout pushbuttons 167 */ 168 static final int PUSHBUTTONLOCKOUT = 2; 169 170 /** 171 * Constant representing a unlocked turnout 172 */ 173 static final int UNLOCKED = 0; 174 175 /** 176 * Constant representing a locked turnout 177 */ 178 static final int LOCKED = 1; 179 180 /** 181 * String constant for Property Change to set Commanded State. 182 */ 183 String PROPERTY_COMMANDED_STATE = "CommandedState"; 184 185 /** 186 * String constant for Property Change to set Feedback Mode. 187 */ 188 String PROPERTY_FEEDBACK_MODE = "feedbackchange"; 189 190 /** 191 * String constant for Property Change to set the Inverted Mode. 192 */ 193 String PROPERTY_INVERTED = "inverted"; 194 195 /** 196 * String constant for Property Change to set the Locked state. 197 */ 198 String PROPERTY_LOCKED = "locked"; 199 200 /** 201 * String constant for Property Change to set the Report Locked state. 202 */ 203 String PROPERTY_REPORT_LOCKED = "reportlocked"; 204 205 /** 206 * String constant for Property Change to set Decoder Name. 207 */ 208 String PROPERTY_DECODER_NAME = "decoderNameChange"; 209 210 /** 211 * String constant for Property Change to set Turnout Operator. 212 */ 213 String PROPERTY_TURNOUT_OPERATION_STATE = "TurnoutOperationState"; 214 215 /** 216 * String constant for when changing the First Feedback Sensor in use. 217 */ 218 String PROPERTY_TURNOUT_FEEDBACK_FIRST_SENSOR = "turnoutFeedbackFirstSensorChange"; 219 220 /** 221 * String constant for when changing the Second Feedback Sensor in use. 222 */ 223 String PROPERTY_TURNOUT_FEEDBACK_SECOND_SENSOR = "turnoutFeedbackSecondSensorChange"; 224 225 /** 226 * String constant for when the Diverging Speed has changed. 227 */ 228 String PROPERTY_TURNOUT_DIVERGING_SPEED = "TurnoutDivergingSpeedChange"; 229 230 /** 231 * String constant for when the Straight Speed has changed. 232 */ 233 String PROPERTY_TURNOUT_STRAIGHT_SPEED = "TurnoutStraightSpeedChange"; 234 235 /** 236 * String constant for when the Leading Turnout is set. 237 */ 238 String PROPERTY_LEADING_TURNOUT = "LeadingTurnout"; 239 240 /** 241 * Get a list of valid feedback types. The valid types depend on the 242 * implemented system. 243 * 244 * @return array of feedback types 245 */ 246 Set<Integer> getValidFeedbackModes(); 247 248 /** 249 * Get a representation of the feedback type. This is the OR of possible 250 * values: DIRECT, EXACT, etc. The valid combinations depend on the 251 * implemented system. 252 * 253 * @return the ORed combination of feedback types 254 */ 255 int getValidFeedbackTypes(); 256 257 /** 258 * Get a human readable representation of the feedback type. The values 259 * depend on the implemented system. 260 * 261 * @return the names of the feedback types or an empty list if no feedback 262 * is available 263 */ 264 @Nonnull 265 String[] getValidFeedbackNames(); 266 267 /** 268 * Set the feedback mode from a human readable name. This must be one of the 269 * names defined in a previous {@link #getValidFeedbackNames} call. 270 * 271 * @param mode the feedback type name 272 * @throws IllegalArgumentException if mode is not valid 273 */ 274 @InvokeOnLayoutThread 275 void setFeedbackMode(@Nonnull String mode) throws IllegalArgumentException; 276 277 /** 278 * Set the feedback mode from a integer. This must be one of the bit values 279 * defined in a previous {@link #getValidFeedbackTypes} call. Having more 280 * than one bit set is an error. 281 * 282 * @param mode the feedback type to set 283 * @throws IllegalArgumentException if mode is not valid 284 */ 285 @InvokeOnLayoutThread 286 void setFeedbackMode(int mode) throws IllegalArgumentException; 287 288 /** 289 * Get the feedback mode in human readable form. This will be one of the 290 * names defined in a {@link #getValidFeedbackNames} call. 291 * 292 * @return the feedback type 293 */ 294 @Nonnull 295 String getFeedbackModeName(); 296 297 /** 298 * Get the feedback mode in machine readable form. This will be one of the 299 * bits defined in a {@link #getValidFeedbackTypes} call. 300 * 301 * @return the feedback type 302 */ 303 int getFeedbackMode(); 304 305 /** 306 * Get if automatically retrying an operation is blocked for this turnout. 307 * 308 * @return true if retrying is disabled; false otherwise 309 */ 310 boolean getInhibitOperation(); 311 312 /** 313 * Set if automatically retrying an operation is blocked for this turnout. 314 * 315 * @param io true if retrying is to be disabled; false otherwise 316 */ 317 void setInhibitOperation(boolean io); 318 319 /** 320 * @return current operation automation class 321 */ 322 @CheckForNull 323 TurnoutOperation getTurnoutOperation(); 324 325 /** 326 * set current automation class 327 * 328 * @param toper TurnoutOperation subclass instance 329 */ 330 @InvokeOnLayoutThread 331 void setTurnoutOperation(@CheckForNull TurnoutOperation toper); 332 333 /** 334 * Return the inverted state of the specified state 335 * Does NOT invert INCONSISTENT 336 * @param inState the specified state 337 * @return the inverted state 338 */ 339 static int invertTurnoutState(int inState) { 340 int result = UNKNOWN; 341 if (inState == CLOSED) { 342 result = THROWN; 343 } else if (inState == THROWN){ 344 result = CLOSED; 345 } else if (inState == INCONSISTENT){ 346 result = INCONSISTENT; 347 } 348 return result; 349 } 350 351 /** 352 * Provide Sensor objects needed for some feedback types. 353 * 354 * Since we defined two feedback methods that require monitoring, we provide 355 * these methods to define those sensors to the Turnout. 356 * <p> 357 * The second sensor can be null if needed. 358 * <p> 359 * Sensor-based feedback will not function until these sensors have been 360 * provided. 361 * 362 * @param name the user or system name of the sensor 363 * @param number the feedback number of the sensor, indexed from 0 364 * @throws jmri.JmriException if unable to assign the feedback sensor 365 */ 366 default void provideFeedbackSensor(@CheckForNull String name, int number) throws JmriException { 367 switch (number) { 368 case 0: 369 provideFirstFeedbackSensor(name); 370 break; 371 case 1: 372 provideSecondFeedbackSensor(name); 373 break; 374 default: 375 throw new IllegalArgumentException("Turnouts have no more than two sensors"); 376 } 377 } 378 379 void provideFirstFeedbackSensor(@CheckForNull String pName) throws JmriException; 380 381 void provideSecondFeedbackSensor(@CheckForNull String pName) throws JmriException; 382 383 /** 384 * Get the first feedback sensor. 385 * 386 * @return the sensor or null if no Sensor set 387 */ 388 @CheckForNull 389 Sensor getFirstSensor(); 390 391 /** 392 * Get the handle for the first feedback sensor. 393 * 394 * @return the sensor handle or null if no Sensor set 395 */ 396 @CheckForNull 397 NamedBeanHandle<Sensor> getFirstNamedSensor(); 398 399 /** 400 * Get the second feedback sensor. 401 * 402 * @return the sensor or null if no Sensor set 403 */ 404 @CheckForNull 405 Sensor getSecondSensor(); 406 407 /** 408 * Get the second feedback sensor handle. 409 * 410 * @return the sensor handle or null if no Sensor set 411 */ 412 @CheckForNull 413 NamedBeanHandle<Sensor> getSecondNamedSensor(); 414 415 /** 416 * Sets the initial known state (CLOSED,THROWN,UNKNOWN) from feedback 417 * information, if appropriate. 418 * <p> 419 * This method is designed to be called only when Turnouts are loaded and 420 * when a new Turnout is defined in the Turnout table. 421 * <p> 422 * No change to known state is made if feedback information is not 423 * available. If feedback information is inconsistent, or if sensor 424 * definition is missing in ONESENSOR and TWOSENSOR feedback, turnout state 425 * is set to UNKNOWN. 426 */ 427 @InvokeOnLayoutThread 428 void setInitialKnownStateFromFeedback(); 429 430 /** 431 * Get control type. 432 * 433 * @return 0 for steady state or the number of time units the control pulses 434 */ 435 int getControlType(); 436 437 /** 438 * Set control type. 439 * 440 * @param num 0 for steady state or the number of time units the control 441 * pulses 442 */ 443 @InvokeOnLayoutThread 444 void setControlType(int num); 445 446 /** 447 * Get turnout inverted. When a turnout is inverted the {@link #CLOSED} and 448 * {@link #THROWN} states are reversed on the layout. 449 * 450 * @return true if inverted; false otherwise 451 */ 452 boolean getInverted(); 453 454 /** 455 * Get turnout inverted. When a turnout is inverted the {@link #CLOSED} and 456 * {@link #THROWN} states are reversed on the layout. 457 * 458 * @param inverted true if inverted; false otherwise 459 */ 460 void setInverted(boolean inverted); 461 462 /** 463 * Determine if turnout can be inverted. When a turnout is inverted the 464 * {@link #CLOSED} and {@link #THROWN} states are inverted on the layout. 465 * 466 * @return true if can be inverted; false otherwise 467 */ 468 boolean canInvert(); 469 470 /** 471 * Get the locked state of the turnout. A turnout can be locked to prevent 472 * it being thrown from a cab or push button on the layout if supported by 473 * the protocol. 474 * 475 * @param turnoutLockout the type of lock 476 * @return true if turnout is locked using specified lock method 477 */ 478 boolean getLocked(int turnoutLockout); 479 480 /** 481 * Enable turnout lock operators. A turnout can be locked to prevent it 482 * being thrown from a cab or push button on the layout if supported by the 483 * protocol. 484 * 485 * @param turnoutLockout the type of lock 486 * @param locked true if locking is enabled for the given type; 487 * false otherwise 488 */ 489 @InvokeOnLayoutThread 490 void enableLockOperation(int turnoutLockout, boolean locked); 491 492 /** 493 * Determine if turnout can be locked as currently configured. A turnout can be locked to prevent it 494 * being thrown from a cab or push button on the layout if supported by the 495 * protocol. 496 * 497 * @param turnoutLockout the type of lock, one of CABLOCKOUT, PUSHBUTTONLOCKOUT 498 * or BOTH = CABLOCKOUT | PUSHBUTTONLOCKOUT 499 * @return true if turnout is locked using specified lock method; false 500 * otherwise 501 */ 502 boolean canLock(int turnoutLockout); 503 504 /** 505 * Provide the possible locking modes for a turnout. 506 * These may require additional configuration, e.g. 507 * setting of a decoder definition for PUSHBUTTONLOCKOUT, 508 * before {@link #canLock(int)} will return true. 509 * 510 * @return One of 0 for none, CABLOCKOUT, PUSHBUTTONLOCKOUT 511 * or CABLOCKOUT | PUSHBUTTONLOCKOUT for both 512 */ 513 int getPossibleLockModes(); 514 515 /** 516 * Lock a turnout. A turnout can be locked to prevent it being thrown from a 517 * cab or push button on the layout if supported by the protocol. 518 * 519 * @param turnoutLockout the type of lock 520 * @param locked true if turnout is locked using specified lock 521 * method; false otherwise 522 */ 523 @InvokeOnLayoutThread 524 void setLocked(int turnoutLockout, boolean locked); 525 526 /** 527 * Get reporting of use of locked turnout by a cab or throttle. 528 * 529 * @return true to report; false otherwise 530 */ 531 boolean getReportLocked(); 532 533 /** 534 * Set reporting of use of locked turnout by a cab or throttle. 535 * 536 * @param reportLocked true to report; false otherwise 537 */ 538 @InvokeOnLayoutThread 539 void setReportLocked(boolean reportLocked); 540 541 /** 542 * Get a human readable representation of the decoder types. 543 * 544 * @return a list of known stationary decoders that can be specified for locking 545 */ 546 @Nonnull 547 String[] getValidDecoderNames(); 548 549 /** 550 * Get a human readable representation of the locking decoder type for this turnout. 551 * 552 * In AbstractTurnout this String defaults to PushbuttonPacket.unknown , ie "None" 553 * @return the name of the decoder type; null indicates none defined 554 */ 555 @CheckForNull 556 String getDecoderName(); 557 558 /** 559 * Set a human readable representation of the locking decoder type for this turnout. 560 * 561 * @param decoderName the name of the decoder type 562 */ 563 void setDecoderName(@CheckForNull String decoderName); 564 565 /** 566 * Use a binary output for sending commands. This appears to expose a 567 * LocoNet-specific feature. 568 * 569 * @param state true if the outputs are binary; false otherwise 570 */ 571 @InvokeOnLayoutThread 572 void setBinaryOutput(boolean state); 573 574 float getDivergingLimit(); 575 576 String getDivergingSpeed(); 577 578 void setDivergingSpeed(String s) throws JmriException; 579 580 float getStraightLimit(); 581 582 String getStraightSpeed(); 583 584 void setStraightSpeed(String s) throws JmriException; 585 586 /** 587 * Check if this Turnout can follow the state of another Turnout. 588 * 589 * @return true if this Turnout is capable of following; false otherwise 590 */ 591 // Note: not `canFollow()` to allow JavaBeans introspection to find 592 // the property "canFollow" 593 boolean isCanFollow(); 594 595 /** 596 * Get the Turnout this Turnout is following. 597 * 598 * @return the leading Turnout or null if none; null if 599 * {@link #isCanFollow()} is false 600 */ 601 @CheckForNull 602 Turnout getLeadingTurnout(); 603 604 /** 605 * Set the Turnout this Turnout will follow. 606 * <p> 607 * It is valid for two or more turnouts to follow each other in a circular 608 * pattern. 609 * <p> 610 * It is recommended that a following turnout's feedback mode be 611 * {@link #DIRECT}. 612 * <p> 613 * It is recommended to explicitly call 614 * {@link #setFollowingCommandedState(boolean)} after calling this method or 615 * to use {@link #setLeadingTurnout(jmri.Turnout, boolean)} to ensure this 616 * Turnout follows the leading Turnout in the expected manner. 617 * 618 * @param turnout the leading Turnout or null if this Turnout should not 619 * follow another Turnout; silently ignored if 620 * {@link #isCanFollow()} is false 621 */ 622 void setLeadingTurnout(@CheckForNull Turnout turnout); 623 624 /** 625 * Set both the leading Turnout and if the commanded state of the leading 626 * Turnout is followed. This is a convenience method for calling both 627 * {@link #setLeadingTurnout(jmri.Turnout)} and 628 * {@link #setFollowingCommandedState(boolean)}. 629 * 630 * @param turnout the leading Turnout or null if this 631 * Turnout should not follow another Turnout; 632 * silently ignored if {@link #isCanFollow()} 633 * is false 634 * @param followingCommandedState true to have all states match leading 635 * turnout; false to only have non-commanded 636 * states match 637 */ 638 void setLeadingTurnout(@CheckForNull Turnout turnout, boolean followingCommandedState); 639 640 /** 641 * Check if this Turnout is following all states or only the non-commanded 642 * states of the leading Turnout. 643 * 644 * @return true if following all states; false otherwise 645 */ 646 boolean isFollowingCommandedState(); 647 648 /** 649 * Set if this Turnout follows all states or only the non-commanded states 650 * of the leading Turnout. 651 * <p> 652 * A Turnout can be commanded to be {@link #THROWN} or {@link #CLOSED}, but 653 * can also have additional states {@link #INCONSISTENT} and 654 * {@link #UNKNOWN}. There are some use cases where a following Turnout 655 * should match all states of the leading Turnout, in which case this should 656 * be true, but there are also use cases where the following Turnout should 657 * only match the INCONSISTENT and UNKNOWN states of the leading Turnout, 658 * but should otherwise be independently commanded, in which case this 659 * should be false. 660 * 661 * @param following true to have all states match leading turnout; false to 662 * only have non-commanded states match 663 */ 664 void setFollowingCommandedState(boolean following); 665 666 /** 667 * Before setting commanded state, if required by manager, apply wait interval until 668 * outputIntervalEnds() to put less pressure on the connection. 669 * <p> 670 * Used to insert a delay before calling {@link #setCommandedState(int)} to spread out a series of 671 * output commands, as in {@link jmri.implementation.MatrixSignalMast#updateOutputs(char[])} and 672 * {@link jmri.implementation.DefaultRoute} class SetRouteThread#run(). 673 * Interval value is kept in the Memo per hardware connection. 674 * 675 * @param s turnout state to forward 676 */ 677 void setCommandedStateAtInterval(int s); 678 679}