001package jmri;
002
003import java.time.LocalDateTime;
004import javax.annotation.Nonnull;
005import javax.annotation.CheckForNull;
006
007/**
008 * Locate a Turnout object representing some specific turnout on the layout.
009 * <p>
010 * Turnout objects are obtained from a TurnoutManager, which in turn is
011 * generally located from the InstanceManager. A typical call sequence might be:
012 * <pre>
013 * Turnout turnout = InstanceManager.turnoutManagerInstance().provideTurnout("23");
014 * </pre>
015 * <p>
016 * Each turnout has a two names. The "user" name is entirely free form, and can
017 * be used for any purpose. The "system" name is provided by the system-specific
018 * implementations, and provides a unique mapping to the layout control system
019 * (for example LocoNet or NCE) and address within that system.
020 * <p>
021 * Much of the book-keeping is implemented in the AbstractTurnoutManager class,
022 * which can form the basis for a system-specific implementation.
023 * <p>
024 * A sample use of the TurnoutManager interface can be seen in the
025 * jmri.jmrit.simpleturnoutctrl.SimpleTurnoutCtrlFrame class, which provides a
026 * simple GUI for controlling a single turnout.
027 *
028 * <p>
029 * This file is part of JMRI.
030 * <p>
031 * JMRI is free software; you can redistribute it and/or modify it under the
032 * terms of version 2 of the GNU General Public License as published by the Free
033 * Software Foundation. See the "COPYING" file for a copy of this license.
034 * <p>
035 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
036 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
037 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
038 *
039 * @author Bob Jacobsen Copyright (C) 2001
040 * @see jmri.Turnout
041 * @see jmri.InstanceManager
042 * @see jmri.jmrit.simpleturnoutctrl.SimpleTurnoutCtrlFrame
043 */
044public interface TurnoutManager extends ProvidingManager<Turnout>, NameIncrementingManager {
045
046    /**
047     * String constant for Property Change to Default Turnout Thrown Speed Change.
048     */
049    String PROPERTY_DEFAULT_THROWN_SPEED = "DefaultTurnoutThrownSpeedChange";
050
051    /**
052     * String constant for Property Change to Default Turnout Closed Speed Change.
053     */
054    String PROPERTY_DEFAULT_CLOSED_SPEED = "DefaultTurnoutClosedSpeedChange";
055
056    /**
057     * Get the Turnout with the user name, then system name if needed; if that fails, create a
058     * new Turnout.
059     * If the name is a valid system name, it will be used for the new Turnout.
060     * Otherwise, the {@link Manager#makeSystemName} method will attempt to turn it
061     * into a valid system name.
062     * <p>
063     * This provides the same function as {@link ProvidingManager#provide}
064     * which has a more generic form.
065     *
066     * @param name User name, system name, or address which can be promoted to
067     *             system name
068     * @return Never null
069     * @throws IllegalArgumentException if Turnout doesn't already exist and the
070     *                                  manager cannot create the Turnout due to
071     *                                  an illegal name or name that can't
072     *                                  be parsed.
073     */
074    @Nonnull
075    Turnout provideTurnout(@Nonnull String name) throws IllegalArgumentException;
076
077    /** {@inheritDoc} */
078    @Override
079    @Nonnull
080    default Turnout provide(@Nonnull String name) throws IllegalArgumentException { return provideTurnout(name); }
081
082    /**
083     * Get an existing Turnout or return null if it doesn't exist.
084     *
085     * Locates via user name, then system name if needed.
086     *
087     * @param name User name or system name to match
088     * @return null if no match found
089     */
090    @CheckForNull
091    Turnout getTurnout(@Nonnull String name);
092
093    /**
094     * Get the Turnout with the given system name or null if no instance
095     * already exists.
096     *
097     * @param systemName the system name
098     * @return requested Turnout object or null if none exists
099     */
100    @CheckForNull
101    @Override
102    Turnout getBySystemName(@Nonnull String systemName);
103
104    /**
105     * Get the Turnout with the given user name or null if no instance
106     * already exists.
107     *
108     * @param userName the user name
109     * @return requested Turnout object or null if none exists
110     */
111    @CheckForNull
112    @Override
113    Turnout getByUserName(@Nonnull String userName);
114
115    /**
116     * Return a Turnout with the specified system and user names.
117     * Lookup by UserName then provide by System Name.
118     * <p>
119     * Note that
120     * two calls with the same arguments will get the same instance; there is
121     * only one Turnout object representing a given physical turnout and
122     * therefore only one with a specific system or user name.
123     * <p>
124     * This will always return a valid object reference; a new object will be
125     * created if necessary. In that case:
126     * <ul>
127     * <li>If a null reference is given for user name, no user name will be
128     * associated with the Turnout object created; a valid system name must be
129     * provided
130     * <li>If both names are provided, the system name defines the hardware
131     * access of the desired turnout, and the user address is associated with
132     * it. The system name must be valid.
133     * </ul>
134     * Note that it is possible to make an inconsistent request if both
135     * addresses are provided, but the given values are associated with
136     * different objects. This is a problem, and we don't have a good solution
137     * except to issue warnings. This will mostly happen if you're creating
138     * Turnouts when you should be looking them up.
139     *
140     * @param systemName the system name
141     * @param userName   the user name (optional)
142     * @return requested Turnout object, newly created if needed
143     * @throws IllegalArgumentException if cannot create the Turnout; likely due
144     *                                  to an illegal name or name that cannot
145     *                                  be parsed
146     */
147    @Nonnull
148    Turnout newTurnout(@Nonnull String systemName, @CheckForNull String userName) throws IllegalArgumentException;
149
150    /**
151     * Get text to be used for the Turnout.CLOSED state in user communication.
152     * Allows text other than "CLOSED" to be used with certain hardware system
153     * to represent the Turnout.CLOSED state.
154     *
155     * @return the textual representation of {@link jmri.Turnout#CLOSED}
156     */
157    @Nonnull
158    String getClosedText();
159
160    /**
161     * Get text to be used for the Turnout.THROWN state in user communication.
162     * Allows text other than "THROWN" to be use with certain hardware system to
163     * represent the Turnout.THROWN state.
164     *
165     * @return the textual representation of {@link jmri.Turnout#THROWN}
166     */
167    @Nonnull
168    String getThrownText();
169
170    /**
171     * Get a list of the valid TurnoutOperation subtypes for use with turnouts
172     * of this system.
173     *
174     * @return a list of subtypes or an empty list if turnout operations are not
175     *         supported
176     */
177    @Nonnull
178    String[] getValidOperationTypes();
179
180    /**
181     * Get, from the user, the number of addressed bits used to control a
182     * turnout. Normally this is 1, and the default routine returns one
183     * automatically. Turnout Managers for systems that can handle multiple
184     * control bits should override this method with one which asks the user to
185     * specify the number of control bits. If the user specifies more than one
186     * control bit, this method should check if the additional bits are
187     * available (not assigned to another object). If the bits are not
188     * available, this method should return 0 for number of control bits, after
189     * informing the user of the problem.
190     *
191     * @param systemName the turnout system name
192     * @return the bit length for turnout control
193     */
194    int askNumControlBits(@Nonnull String systemName);
195
196    /**
197     * Determine if the manager supports multiple control bits, as
198     * {@link #askNumControlBits(java.lang.String)} will always return a value
199     * even if it is not supported.
200     *
201     * @param systemName the turnout system name
202     * @return true if manager supports multiple control bits for the turnout;
203     *         false otherwise
204     */
205    boolean isNumControlBitsSupported(@Nonnull String systemName);
206
207    /**
208     * Get, from the user, the type of output to be used bits to control a
209     * turnout. Normally this is 0 for 'steady state' control, and the default
210     * routine returns 0 automatically. Turnout Managers for systems that can
211     * handle pulsed control as well as steady state control should override
212     * this method with one which asks the user to specify the type of control
213     * to be used. The routine should return 0 for 'steady state' control, or n
214     * for 'pulsed' control, where n specifies the duration of the pulse
215     * (normally in seconds).
216     *
217     * @param systemName the turnout system name
218     * @return 0 for steady state or the number of seconds for a pulse control
219     */
220    int askControlType(@Nonnull String systemName);
221
222    /**
223     * Determine if the manager supports the handling of pulsed and steady state
224     * control as the {@link #askControlType(java.lang.String)} will always
225     * return a value even if it is not supported.
226     *
227     * @param systemName the turnout system name
228     * @return true if manager supports the control type returned by
229     *         {@link #askControlType(java.lang.String)}; false otherwise
230     *
231     */
232    boolean isControlTypeSupported(@Nonnull String systemName);
233
234    /**
235     * Get a system name for a given hardware address and system prefix.
236     *
237     * @param curAddress desired hardware address
238     * @param prefix     system prefix used in system name
239     * @return the complete turnout system name for the prefix and current
240     *         address
241     * @throws jmri.JmriException if unable to create a system name for the
242     *                            given address, possibly due to invalid address
243     *                            format
244     */
245    String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException;
246
247    void setDefaultClosedSpeed(@Nonnull String speed) throws JmriException;
248
249    void setDefaultThrownSpeed(@Nonnull String speed) throws JmriException;
250
251    String getDefaultThrownSpeed();
252
253    String getDefaultClosedSpeed();
254
255    /**
256     * Get the Interval (in ms) to wait between output commands.
257     * Configured in AdapterConfig, stored in memo.
258     *
259     * @return the (Turnout) Output Interval in milliseconds
260     */
261    int getOutputInterval();
262
263    /**
264     * Set the Interval (in ms) to wait between output commands.
265     *
266     * @param newInterval the new Output Interval in Milliseconds
267     */
268    void setOutputInterval(int newInterval);
269
270    /**
271     * Get end time of latest OutputInterval, calculated from the current time.
272     *
273     * @return end time in milliseconds or current time if no interval was set or timer has completed
274     */
275    @Nonnull
276    LocalDateTime outputIntervalEnds();
277
278}