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> {
045
046    /**
047     * Get the Turnout with the user name, then system name if needed; if that fails, create a
048     * new Turnout. 
049     * If the name is a valid system name, it will be used for the new Turnout.
050     * Otherwise, the {@link Manager#makeSystemName} method will attempt to turn it
051     * into a valid system name.
052     * <p>
053     * This provides the same function as {@link ProvidingManager#provide}
054     * which has a more generic form.
055     *
056     * @param name User name, system name, or address which can be promoted to
057     *             system name
058     * @return Never null
059     * @throws IllegalArgumentException if Turnout doesn't already exist and the
060     *                                  manager cannot create the Turnout due to
061     *                                  an illegal name or name that can't
062     *                                  be parsed.
063     */
064    @Nonnull
065    public Turnout provideTurnout(@Nonnull String name) throws IllegalArgumentException;
066
067    /** {@inheritDoc} */
068    @Override
069    @Nonnull
070    default public Turnout provide(@Nonnull String name) throws IllegalArgumentException { return provideTurnout(name); }
071    
072    /**
073     * Get an existing Turnout or return null if it doesn't exist. 
074     * 
075     * Locates via user name, then system name if needed.
076     *
077     * @param name User name or system name to match
078     * @return null if no match found
079     */
080    @CheckForNull
081    public Turnout getTurnout(@Nonnull String name);
082
083    /**
084     * Get the Turnout with the given system name or null if no instance
085     * already exists.
086     *
087     * @param systemName the system name
088     * @return requested Turnout object or null if none exists
089     */
090    @CheckForNull
091    @Override
092    public Turnout getBySystemName(@Nonnull String systemName);
093
094    /**
095     * Get the Turnout with the given user name or null if no instance
096     * already exists.
097     *
098     * @param userName the user name
099     * @return requested Turnout object or null if none exists
100     */
101    @CheckForNull
102    @Override
103    public Turnout getByUserName(@Nonnull String userName);
104
105    /**
106     * Return a Turnout with the specified system and user names.
107     * Lookup by UserName then provide by System Name.
108     * <p>
109     * Note that
110     * two calls with the same arguments will get the same instance; there is
111     * only one Turnout object representing a given physical turnout and
112     * therefore only one with a specific system or user name.
113     * <p>
114     * This will always return a valid object reference; a new object will be
115     * created if necessary. In that case:
116     * <ul>
117     * <li>If a null reference is given for user name, no user name will be
118     * associated with the Turnout object created; a valid system name must be
119     * provided
120     * <li>If both names are provided, the system name defines the hardware
121     * access of the desired turnout, and the user address is associated with
122     * it. The system name must be valid.
123     * </ul>
124     * Note that it is possible to make an inconsistent request if both
125     * addresses are provided, but the given values are associated with
126     * different objects. This is a problem, and we don't have a good solution
127     * except to issue warnings. This will mostly happen if you're creating
128     * Turnouts when you should be looking them up.
129     *
130     * @param systemName the system name
131     * @param userName   the user name (optional)
132     * @return requested Turnout object, newly created if needed
133     * @throws IllegalArgumentException if cannot create the Turnout; likely due
134     *                                  to an illegal name or name that cannot
135     *                                  be parsed
136     */
137    @Nonnull
138    public Turnout newTurnout(@Nonnull String systemName, @CheckForNull String userName) throws IllegalArgumentException;
139
140    /**
141     * Get text to be used for the Turnout.CLOSED state in user communication.
142     * Allows text other than "CLOSED" to be used with certain hardware system
143     * to represent the Turnout.CLOSED state.
144     *
145     * @return the textual representation of {@link jmri.Turnout#CLOSED}
146     */
147    @Nonnull
148    public String getClosedText();
149
150    /**
151     * Get text to be used for the Turnout.THROWN state in user communication.
152     * Allows text other than "THROWN" to be use with certain hardware system to
153     * represent the Turnout.THROWN state.
154     *
155     * @return the textual representation of {@link jmri.Turnout#THROWN}
156     */
157    @Nonnull
158    public String getThrownText();
159
160    /**
161     * Get a list of the valid TurnoutOperation subtypes for use with turnouts
162     * of this system.
163     *
164     * @return a list of subtypes or an empty list if turnout operations are not
165     *         supported
166     */
167    @Nonnull
168    public String[] getValidOperationTypes();
169
170    /**
171     * Get, from the user, the number of addressed bits used to control a
172     * turnout. Normally this is 1, and the default routine returns one
173     * automatically. Turnout Managers for systems that can handle multiple
174     * control bits should override this method with one which asks the user to
175     * specify the number of control bits. If the user specifies more than one
176     * control bit, this method should check if the additional bits are
177     * available (not assigned to another object). If the bits are not
178     * available, this method should return 0 for number of control bits, after
179     * informing the user of the problem.
180     *
181     * @param systemName the turnout system name
182     * @return the bit length for turnout control
183     */
184    public int askNumControlBits(@Nonnull String systemName);
185
186    /**
187     * Determine if the manager supports multiple control bits, as
188     * {@link #askNumControlBits(java.lang.String)} will always return a value
189     * even if it is not supported.
190     *
191     * @param systemName the turnout system name
192     * @return true if manager supports multiple control bits for the turnout;
193     *         false otherwise
194     */
195    public boolean isNumControlBitsSupported(@Nonnull String systemName);
196
197    /**
198     * Get, from the user, the type of output to be used bits to control a
199     * turnout. Normally this is 0 for 'steady state' control, and the default
200     * routine returns 0 automatically. Turnout Managers for systems that can
201     * handle pulsed control as well as steady state control should override
202     * this method with one which asks the user to specify the type of control
203     * to be used. The routine should return 0 for 'steady state' control, or n
204     * for 'pulsed' control, where n specifies the duration of the pulse
205     * (normally in seconds).
206     *
207     * @param systemName the turnout system name
208     * @return 0 for steady state or the number of seconds for a pulse control
209     */
210    public int askControlType(@Nonnull String systemName);
211
212    /**
213     * Determine if the manager supports the handling of pulsed and steady state
214     * control as the {@link #askControlType(java.lang.String)} will always
215     * return a value even if it is not supported.
216     *
217     * @param systemName the turnout system name
218     * @return true if manager supports the control type returned by
219     *         {@link #askControlType(java.lang.String)}; false otherwise
220     *
221     */
222    public boolean isControlTypeSupported(@Nonnull String systemName);
223
224    /**
225     * Determines if it is possible to add a range of turnouts in
226     * numerical order.
227     *
228     * @param systemName the starting turnout system name; ignored in all known
229     *                   implementations
230     * @return true if a range of turnouts can be added; false otherwise
231     */
232    public boolean allowMultipleAdditions(@Nonnull String systemName);
233
234    /**
235     * Get the next valid address.
236     * <p>
237     * Determine if the address supplied is valid and free. 
238     * If not then it shall return the next free valid address up to a maximum 
239     * of 10 addresses away from the initial address.
240     * Used when adding add a range of Turnouts.
241     *
242     * @param prefix     System prefix used in system name
243     * @param curAddress desired hardware address
244     * @return the next available address or null if next 10 addresses unavailable.
245     * @throws jmri.JmriException if unable to provide a turnout at the desired
246     *                            address due to invalid format for the current
247     *                            address or other reasons.
248     * @deprecated since 4.21.3; use #getNextValidAddress(String, String, boolean) instead.
249     */
250    @Deprecated
251    public String getNextValidAddress(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException;
252
253    /**
254     * Get the Next valid Turnout address.
255     * <p>
256     * @param curAddress the starting hardware address to get the next valid from.
257     * @param prefix system prefix, just system name, not type letter.
258     * @param ignoreInitialExisting false to return the starting address if it 
259     *                          does not exist, else true to force an increment.
260     * @return the next valid system name not already in use, excluding both system name prefix and type letter.
261     * @throws JmriException    if unable to get the current / next address, 
262     *                          or more than 10 next addresses in use.
263     */
264    @Nonnull
265    public String getNextValidAddress(@Nonnull String curAddress, @Nonnull String prefix, boolean ignoreInitialExisting) throws JmriException;
266    
267    /**
268     * Get a system name for a given hardware address and system prefix.
269     *
270     * @param curAddress desired hardware address
271     * @param prefix     system prefix used in system name
272     * @return the complete turnout system name for the prefix and current
273     *         address
274     * @throws jmri.JmriException if unable to create a system name for the
275     *                            given address, possibly due to invalid address
276     *                            format
277     */
278    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException;
279
280    public void setDefaultClosedSpeed(@Nonnull String speed) throws JmriException;
281
282    public void setDefaultThrownSpeed(@Nonnull String speed) throws JmriException;
283
284    public String getDefaultThrownSpeed();
285
286    public String getDefaultClosedSpeed();
287
288    /**
289     * Get the Interval (in ms) to wait between output commands.
290     * Configured in AdapterConfig, stored in memo.
291     *
292     * @return the (Turnout) Output Interval in milliseconds
293     */
294    public int getOutputInterval();
295
296    /**
297     * Set the Interval (in ms) to wait between output commands.
298     *
299     * @param newInterval the new Output Interval in Milliseconds
300     */
301    public void setOutputInterval(int newInterval);
302
303    /**
304     * Get end time of latest OutputInterval, calculated from the current time.
305     *
306     * @return end time in milliseconds or current time if no interval was set or timer has completed
307     */
308    @Nonnull
309    public LocalDateTime outputIntervalEnds();
310
311}