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 turnout lockout cab commands 153 */ 154 static final int CABLOCKOUT = 1; 155 156 /** 157 * Constant representing turnout lockout pushbuttons 158 */ 159 static final int PUSHBUTTONLOCKOUT = 2; 160 161 /** 162 * Constant representing a unlocked turnout 163 */ 164 static final int UNLOCKED = 0; 165 166 /** 167 * Constant representing a locked turnout 168 */ 169 static final int LOCKED = 1; 170 171 /** 172 * Get a list of valid feedback types. The valid types depend on the 173 * implemented system. 174 * 175 * @return array of feedback types 176 */ 177 Set<Integer> getValidFeedbackModes(); 178 179 /** 180 * Get a representation of the feedback type. This is the OR of possible 181 * values: DIRECT, EXACT, etc. The valid combinations depend on the 182 * implemented system. 183 * 184 * @return the ORed combination of feedback types 185 */ 186 int getValidFeedbackTypes(); 187 188 /** 189 * Get a human readable representation of the feedback type. The values 190 * depend on the implemented system. 191 * 192 * @return the names of the feedback types or an empty list if no feedback 193 * is available 194 */ 195 @Nonnull 196 String[] getValidFeedbackNames(); 197 198 /** 199 * Set the feedback mode from a human readable name. This must be one of the 200 * names defined in a previous {@link #getValidFeedbackNames} call. 201 * 202 * @param mode the feedback type name 203 * @throws IllegalArgumentException if mode is not valid 204 */ 205 @InvokeOnLayoutThread 206 void setFeedbackMode(@Nonnull String mode) throws IllegalArgumentException; 207 208 /** 209 * Set the feedback mode from a integer. This must be one of the bit values 210 * defined in a previous {@link #getValidFeedbackTypes} call. Having more 211 * than one bit set is an error. 212 * 213 * @param mode the feedback type to set 214 * @throws IllegalArgumentException if mode is not valid 215 */ 216 @InvokeOnLayoutThread 217 void setFeedbackMode(int mode) throws IllegalArgumentException; 218 219 /** 220 * Get the feedback mode in human readable form. This will be one of the 221 * names defined in a {@link #getValidFeedbackNames} call. 222 * 223 * @return the feedback type 224 */ 225 @Nonnull 226 String getFeedbackModeName(); 227 228 /** 229 * Get the feedback mode in machine readable form. This will be one of the 230 * bits defined in a {@link #getValidFeedbackTypes} call. 231 * 232 * @return the feedback type 233 */ 234 int getFeedbackMode(); 235 236 /** 237 * Get if automatically retrying an operation is blocked for this turnout. 238 * 239 * @return true if retrying is disabled; false otherwise 240 */ 241 boolean getInhibitOperation(); 242 243 /** 244 * Set if automatically retrying an operation is blocked for this turnout. 245 * 246 * @param io true if retrying is to be disabled; false otherwise 247 */ 248 void setInhibitOperation(boolean io); 249 250 /** 251 * @return current operation automation class 252 */ 253 @CheckForNull 254 TurnoutOperation getTurnoutOperation(); 255 256 /** 257 * set current automation class 258 * 259 * @param toper TurnoutOperation subclass instance 260 */ 261 @InvokeOnLayoutThread 262 void setTurnoutOperation(@CheckForNull TurnoutOperation toper); 263 264 /** 265 * Return the inverted state of the specified state 266 * Does NOT invert INCONSISTENT 267 * @param inState the specified state 268 * @return the inverted state 269 */ 270 static int invertTurnoutState(int inState) { 271 int result = UNKNOWN; 272 if (inState == CLOSED) { 273 result = THROWN; 274 } else if (inState == THROWN){ 275 result = CLOSED; 276 } else if (inState == INCONSISTENT){ 277 result = INCONSISTENT; 278 } 279 return result; 280 } 281 282 /** 283 * Provide Sensor objects needed for some feedback types. 284 * 285 * Since we defined two feedback methods that require monitoring, we provide 286 * these methods to define those sensors to the Turnout. 287 * <p> 288 * The second sensor can be null if needed. 289 * <p> 290 * Sensor-based feedback will not function until these sensors have been 291 * provided. 292 * 293 * @param name the user or system name of the sensor 294 * @param number the feedback number of the sensor, indexed from 0 295 * @throws jmri.JmriException if unable to assign the feedback sensor 296 */ 297 default void provideFeedbackSensor(@CheckForNull String name, int number) throws JmriException { 298 switch (number) { 299 case 0: 300 provideFirstFeedbackSensor(name); 301 break; 302 case 1: 303 provideSecondFeedbackSensor(name); 304 break; 305 default: 306 throw new IllegalArgumentException("Turnouts have no more than two sensors"); 307 } 308 } 309 310 void provideFirstFeedbackSensor(@CheckForNull String pName) throws JmriException; 311 312 void provideSecondFeedbackSensor(@CheckForNull String pName) throws JmriException; 313 314 /** 315 * Get the first feedback sensor. 316 * 317 * @return the sensor or null if no Sensor set 318 */ 319 @CheckForNull 320 Sensor getFirstSensor(); 321 322 /** 323 * Get the handle for the first feedback sensor. 324 * 325 * @return the sensor handle or null if no Sensor set 326 */ 327 @CheckForNull 328 NamedBeanHandle<Sensor> getFirstNamedSensor(); 329 330 /** 331 * Get the second feedback sensor. 332 * 333 * @return the sensor or null if no Sensor set 334 */ 335 @CheckForNull 336 Sensor getSecondSensor(); 337 338 /** 339 * Get the second feedback sensor handle. 340 * 341 * @return the sensor handle or null if no Sensor set 342 */ 343 @CheckForNull 344 NamedBeanHandle<Sensor> getSecondNamedSensor(); 345 346 /** 347 * Sets the initial known state (CLOSED,THROWN,UNKNOWN) from feedback 348 * information, if appropriate. 349 * <p> 350 * This method is designed to be called only when Turnouts are loaded and 351 * when a new Turnout is defined in the Turnout table. 352 * <p> 353 * No change to known state is made if feedback information is not 354 * available. If feedback information is inconsistent, or if sensor 355 * definition is missing in ONESENSOR and TWOSENSOR feedback, turnout state 356 * is set to UNKNOWN. 357 */ 358 @InvokeOnLayoutThread 359 void setInitialKnownStateFromFeedback(); 360 361 /** 362 * Get control type. 363 * 364 * @return 0 for steady state or the number of time units the control pulses 365 */ 366 int getControlType(); 367 368 /** 369 * Set control type. 370 * 371 * @param num 0 for steady state or the number of time units the control 372 * pulses 373 */ 374 @InvokeOnLayoutThread 375 void setControlType(int num); 376 377 /** 378 * Get turnout inverted. When a turnout is inverted the {@link #CLOSED} and 379 * {@link #THROWN} states are reversed on the layout. 380 * 381 * @return true if inverted; false otherwise 382 */ 383 boolean getInverted(); 384 385 /** 386 * Get turnout inverted. When a turnout is inverted the {@link #CLOSED} and 387 * {@link #THROWN} states are reversed on the layout. 388 * 389 * @param inverted true if inverted; false otherwise 390 */ 391 void setInverted(boolean inverted); 392 393 /** 394 * Determine if turnout can be inverted. When a turnout is inverted the 395 * {@link #CLOSED} and {@link #THROWN} states are inverted on the layout. 396 * 397 * @return true if can be inverted; false otherwise 398 */ 399 boolean canInvert(); 400 401 /** 402 * Get the locked state of the turnout. A turnout can be locked to prevent 403 * it being thrown from a cab or push button on the layout if supported by 404 * the protocol. 405 * 406 * @param turnoutLockout the type of lock 407 * @return true if turnout is locked using specified lock method 408 */ 409 boolean getLocked(int turnoutLockout); 410 411 /** 412 * Enable turnout lock operators. A turnout can be locked to prevent it 413 * being thrown from a cab or push button on the layout if supported by the 414 * protocol. 415 * 416 * @param turnoutLockout the type of lock 417 * @param locked true if locking is enabled for the given type; 418 * false otherwise 419 */ 420 @InvokeOnLayoutThread 421 void enableLockOperation(int turnoutLockout, boolean locked); 422 423 /** 424 * Determine if turnout can be locked as currently configured. A turnout can be locked to prevent it 425 * being thrown from a cab or push button on the layout if supported by the 426 * protocol. 427 * 428 * @param turnoutLockout the type of lock, one of CABLOCKOUT, PUSHBUTTONLOCKOUT 429 * or BOTH = CABLOCKOUT | PUSHBUTTONLOCKOUT 430 * @return true if turnout is locked using specified lock method; false 431 * otherwise 432 */ 433 boolean canLock(int turnoutLockout); 434 435 /** 436 * Provide the possible locking modes for a turnout. 437 * These may require additional configuration, e.g. 438 * setting of a decoder definition for PUSHBUTTONLOCKOUT, 439 * before {@link #canLock(int)} will return true. 440 * 441 * @return One of 0 for none, CABLOCKOUT, PUSHBUTTONLOCKOUT 442 * or CABLOCKOUT | PUSHBUTTONLOCKOUT for both 443 */ 444 int getPossibleLockModes(); 445 446 /** 447 * Lock a turnout. A turnout can be locked to prevent it being thrown from a 448 * cab or push button on the layout if supported by the protocol. 449 * 450 * @param turnoutLockout the type of lock 451 * @param locked true if turnout is locked using specified lock 452 * method; false otherwise 453 */ 454 @InvokeOnLayoutThread 455 void setLocked(int turnoutLockout, boolean locked); 456 457 /** 458 * Get reporting of use of locked turnout by a cab or throttle. 459 * 460 * @return true to report; false otherwise 461 */ 462 boolean getReportLocked(); 463 464 /** 465 * Set reporting of use of locked turnout by a cab or throttle. 466 * 467 * @param reportLocked true to report; false otherwise 468 */ 469 @InvokeOnLayoutThread 470 void setReportLocked(boolean reportLocked); 471 472 /** 473 * Get a human readable representation of the decoder types. 474 * 475 * @return a list of known stationary decoders that can be specified for locking 476 */ 477 @Nonnull 478 String[] getValidDecoderNames(); 479 480 /** 481 * Get a human readable representation of the locking decoder type for this turnout. 482 * 483 * In AbstractTurnout this String defaults to PushbuttonPacket.unknown , ie "None" 484 * @return the name of the decoder type; null indicates none defined 485 */ 486 @CheckForNull 487 String getDecoderName(); 488 489 /** 490 * Set a human readable representation of the locking decoder type for this turnout. 491 * 492 * @param decoderName the name of the decoder type 493 */ 494 void setDecoderName(@CheckForNull String decoderName); 495 496 /** 497 * Use a binary output for sending commands. This appears to expose a 498 * LocoNet-specific feature. 499 * 500 * @param state true if the outputs are binary; false otherwise 501 */ 502 @InvokeOnLayoutThread 503 void setBinaryOutput(boolean state); 504 505 float getDivergingLimit(); 506 507 String getDivergingSpeed(); 508 509 void setDivergingSpeed(String s) throws JmriException; 510 511 float getStraightLimit(); 512 513 String getStraightSpeed(); 514 515 void setStraightSpeed(String s) throws JmriException; 516 517 /** 518 * Check if this Turnout can follow the state of another Turnout. 519 * 520 * @return true if this Turnout is capable of following; false otherwise 521 */ 522 // Note: not `canFollow()` to allow JavaBeans introspection to find 523 // the property "canFollow" 524 boolean isCanFollow(); 525 526 /** 527 * Get the Turnout this Turnout is following. 528 * 529 * @return the leading Turnout or null if none; null if 530 * {@link #isCanFollow()} is false 531 */ 532 @CheckForNull 533 Turnout getLeadingTurnout(); 534 535 /** 536 * Set the Turnout this Turnout will follow. 537 * <p> 538 * It is valid for two or more turnouts to follow each other in a circular 539 * pattern. 540 * <p> 541 * It is recommended that a following turnout's feedback mode be 542 * {@link #DIRECT}. 543 * <p> 544 * It is recommended to explicitly call 545 * {@link #setFollowingCommandedState(boolean)} after calling this method or 546 * to use {@link #setLeadingTurnout(jmri.Turnout, boolean)} to ensure this 547 * Turnout follows the leading Turnout in the expected manner. 548 * 549 * @param turnout the leading Turnout or null if this Turnout should not 550 * follow another Turnout; silently ignored if 551 * {@link #isCanFollow()} is false 552 */ 553 void setLeadingTurnout(@CheckForNull Turnout turnout); 554 555 /** 556 * Set both the leading Turnout and if the commanded state of the leading 557 * Turnout is followed. This is a convenience method for calling both 558 * {@link #setLeadingTurnout(jmri.Turnout)} and 559 * {@link #setFollowingCommandedState(boolean)}. 560 * 561 * @param turnout the leading Turnout or null if this 562 * Turnout should not follow another Turnout; 563 * silently ignored if {@link #isCanFollow()} 564 * is false 565 * @param followingCommandedState true to have all states match leading 566 * turnout; false to only have non-commanded 567 * states match 568 */ 569 void setLeadingTurnout(@CheckForNull Turnout turnout, boolean followingCommandedState); 570 571 /** 572 * Check if this Turnout is following all states or only the non-commanded 573 * states of the leading Turnout. 574 * 575 * @return true if following all states; false otherwise 576 */ 577 boolean isFollowingCommandedState(); 578 579 /** 580 * Set if this Turnout follows all states or only the non-commanded states 581 * of the leading Turnout. 582 * <p> 583 * A Turnout can be commanded to be {@link #THROWN} or {@link #CLOSED}, but 584 * can also have additional states {@link #INCONSISTENT} and 585 * {@link #UNKNOWN}. There are some use cases where a following Turnout 586 * should match all states of the leading Turnout, in which case this should 587 * be true, but there are also use cases where the following Turnout should 588 * only match the INCONSISTENT and UNKNOWN states of the leading Turnout, 589 * but should otherwise be independently commanded, in which case this 590 * should be false. 591 * 592 * @param following true to have all states match leading turnout; false to 593 * only have non-commanded states match 594 */ 595 void setFollowingCommandedState(boolean following); 596 597 /** 598 * Before setting commanded state, if required by manager, apply wait interval until 599 * outputIntervalEnds() to put less pressure on the connection. 600 * <p> 601 * Used to insert a delay before calling {@link #setCommandedState(int)} to spread out a series of 602 * output commands, as in {@link jmri.implementation.MatrixSignalMast#updateOutputs(char[])} and 603 * {@link jmri.implementation.DefaultRoute} class SetRouteThread#run(). 604 * Interval value is kept in the Memo per hardware connection, default = 0 605 * 606 * @param s turnout state to forward 607 */ 608 void setCommandedStateAtInterval(int s); 609 610}