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}