001package jmri.managers;
002
003import java.time.LocalDateTime;
004import java.util.Arrays;
005import java.util.LinkedList;
006import java.util.List;
007import javax.annotation.Nonnull;
008
009import jmri.*;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013/**
014 * Implementation of a TurnoutManager that can serve as a proxy for multiple
015 * system-specific implementations.
016 *
017 * @author Bob Jacobsen Copyright (C) 2003, 2010
018 */
019public class ProxyTurnoutManager extends AbstractProvidingProxyManager<Turnout> implements TurnoutManager {
020
021    public ProxyTurnoutManager() {
022        super();
023    }
024
025    @Override
026    protected AbstractManager<Turnout> makeInternalManager() {
027        return jmri.InstanceManager.getDefault(jmri.jmrix.internal.InternalSystemConnectionMemo.class).getTurnoutManager();
028    }
029
030    /**
031     * {@inheritDoc}
032     */
033    @Override
034    public void addManager(@Nonnull Manager<Turnout> m) {
035        super.addManager(m);
036        InstanceManager.getDefault(TurnoutOperationManager.class).loadOperationTypes();
037    }
038
039    /**
040     * Locate via user name, then system name if needed.
041     *
042     * @return Null if nothing by that name exists
043     */
044    @Override
045    public Turnout getTurnout(@Nonnull String name) {
046        return super.getNamedBean(name);
047    }
048
049    /**
050     * {@inheritDoc}
051     */
052    @Override
053    @Nonnull
054    protected Turnout makeBean(Manager<Turnout> manager, String systemName, String userName) throws IllegalArgumentException {
055        return ((TurnoutManager) manager).newTurnout(systemName, userName);
056    }
057
058    /**
059     * {@inheritDoc}
060     */
061    @Override
062    @Nonnull
063    public Turnout provideTurnout(@Nonnull String name) throws IllegalArgumentException {
064        return super.provideNamedBean(name);
065    }
066
067
068    /** {@inheritDoc} */
069    @Override
070    @Nonnull
071    public Turnout provide(@Nonnull String name) throws IllegalArgumentException { return provideTurnout(name); }
072
073    /**
074     * Get an instance with the specified system and user names. Note that
075     * two calls with the same arguments will get the same instance; there is
076     * only one Sensor object representing a given physical turnout and
077     * therefore only one with a specific system or user name.
078     * <p>
079     * This will always return a valid object reference for a valid request; a
080     * new object will be created if necessary. In that case:
081     * <ul>
082     * <li>If a null reference is given for user name, no user name will be
083     * associated with the Turnout object created; a valid system name must be
084     * provided
085     * <li>If a null reference is given for the system name, a system name will
086     * _somehow_ be inferred from the user name. How this is done is system
087     * specific. Note: a future extension of this interface will add an
088     * exception to signal that this was not possible.
089     * <li>If both names are provided, the system name defines the hardware
090     * access of the desired turnout, and the user address is associated with
091     * it.
092     * </ul>
093     * Note that it is possible to make an inconsistent request if both
094     * addresses are provided, but the given values are associated with
095     * different objects. This is a problem, and we don't have a good solution
096     * except to issue warnings. This will mostly happen if you're creating
097     * Sensors when you should be looking them up.
098     *
099     * @return requested Turnout object (never null)
100     */
101    @Override
102    @Nonnull
103    public Turnout newTurnout(@Nonnull String systemName, String userName) throws IllegalArgumentException {
104        return newNamedBean(systemName, userName);
105    }
106
107    /**
108     * Get text to be used for the Turnout.CLOSED state in user communication.
109     * Allows text other than "CLOSED" to be use with certain hardware system to
110     * represent the Turnout.CLOSED state. Defaults to the primary manager. This
111     * means that the primary manager sets the terminology used. Note: the
112     * primary manager need not override the method in AbstractTurnoutManager if
113     * "CLOSED" is the desired terminology.
114     */
115    @Override
116    @Nonnull
117    public String getClosedText() {
118        return ((TurnoutManager) getDefaultManager()).getClosedText();
119    }
120
121    /**
122     * Get text to be used for the Turnout.THROWN state in user communication.
123     * Allows text other than "THROWN" to be use with certain hardware system to
124     * represent the Turnout.THROWN state. Defaults to the primary manager. This
125     * means that the primary manager sets the terminology used. Note: the
126     * primary manager need not override the method in AbstractTurnoutManager if
127     * "THROWN" is the desired terminology.
128     */
129    @Override
130    @Nonnull
131    public String getThrownText() {
132        return ((TurnoutManager) getDefaultManager()).getThrownText();
133    }
134
135    /**
136     * Get from the user, the number of addressed bits used to control a
137     * turnout. Normally this is 1, and the default routine returns 1
138     * automatically. Turnout Managers for systems that can handle multiple
139     * control bits should override this method with one which asks the user to
140     * specify the number of control bits. If the user specifies more than one
141     * control bit, this method should check if the additional bits are
142     * available (not assigned to another object). If the bits are not
143     * available, this method should return 0 for number of control bits, after
144     * informing the user of the problem.
145     */
146    @Override
147    public int askNumControlBits(@Nonnull String systemName) {
148        return ((TurnoutManager) getManagerOrDefault(systemName)).askNumControlBits(systemName);
149    }
150
151    /**
152     * Get from the user, the type of output to be used bits to control a
153     * turnout. Normally this is 0 for 'steady state' control, and the default
154     * routine returns 0 automatically. Turnout Managers for systems that can
155     * handle pulsed control as well as steady state control should override
156     * this method with one which asks the user to specify the type of control
157     * to be used. The routine should return 0 for 'steady state' control, or n
158     * for 'pulsed' control, where n specifies the duration of the pulse
159     * (normally in seconds).
160     */
161    @Override
162    public int askControlType(@Nonnull String systemName) {
163        return ((TurnoutManager) getManagerOrDefault(systemName)).askControlType(systemName);
164    }
165
166    /**
167     * {@inheritDoc}
168     */
169    @Override
170    public boolean isControlTypeSupported(@Nonnull String systemName) {
171        return ((TurnoutManager) getManagerOrDefault(systemName)).isControlTypeSupported(systemName);
172    }
173
174    /**
175     * {@inheritDoc}
176     */
177    @Override
178    public boolean isNumControlBitsSupported(@Nonnull String systemName) {
179        return ((TurnoutManager) getManagerOrDefault(systemName)).isNumControlBitsSupported(systemName);
180    }
181
182    /** {@inheritDoc} */
183    @Override
184    @Nonnull
185    public String[] getValidOperationTypes() {
186        List<String> typeList = new LinkedList<>();
187        getManagerList().forEach(m -> typeList.addAll(Arrays.asList(((TurnoutManager) m).getValidOperationTypes())));
188        return TurnoutOperationManager.concatenateTypeLists(typeList.toArray(String[]::new));
189    }
190
191    /**
192     * {@inheritDoc}
193     */
194    @Override
195    public boolean allowMultipleAdditions(@Nonnull String systemName) {
196        return ((TurnoutManager) getManagerOrDefault(systemName)).allowMultipleAdditions(systemName);
197    }
198
199    /**
200     * {@inheritDoc}
201     */
202    @Override
203    public void setDefaultClosedSpeed(@Nonnull String speed) throws jmri.JmriException {
204        for (Manager<Turnout> m : getManagerList()) {
205            try {
206                ((TurnoutManager) m).setDefaultClosedSpeed(speed);
207            } catch (jmri.JmriException ex) {
208                log.error("JmriException {}", ex.getMessage() );
209                throw ex;
210            }
211        }
212    }
213
214    /**
215     * {@inheritDoc}
216     */
217    @Override
218    public void setDefaultThrownSpeed(@Nonnull String speed) throws jmri.JmriException {
219        for (Manager<Turnout> m : getManagerList()) {
220            try {
221                ((TurnoutManager) m).setDefaultThrownSpeed(speed);
222            } catch (jmri.JmriException ex) {
223                log.error("JmriException {}", ex.getMessage() );
224                throw ex;
225            }
226        }
227    }
228
229    /**
230     * {@inheritDoc}
231     */
232    @Override
233    public String getDefaultThrownSpeed() {
234        return ((TurnoutManager) getDefaultManager()).getDefaultThrownSpeed();
235    }
236
237    /**
238     * {@inheritDoc}
239     */
240    @Override
241    public String getDefaultClosedSpeed() {
242        return ((TurnoutManager) getDefaultManager()).getDefaultClosedSpeed();
243    }
244
245    /** {@inheritDoc}
246     * @return outputInterval from default TurnoutManager
247     */
248    @Override
249    public int getOutputInterval() {
250        return ((TurnoutManager) getDefaultManager()).getOutputInterval();
251    }
252
253    /**
254     * {@inheritDoc}
255     * This method is only used in jmri.jmrix.internal.InternalTurnoutManagerTest and should not be
256     * used in actual code, as it can overwrite individual per connection values set by the user.
257     */
258    @Override
259    public void setOutputInterval(int newInterval) {
260        log.debug("setOutputInterval called in ProxyTurnoutManager");
261        // only intended for testing; do not set interval via ProxyTurnoutManager in actual code
262        for (Manager<Turnout> manager : getManagerList()) {
263            ((TurnoutManager) manager).setOutputInterval(newInterval);
264        }
265    }
266
267    /**
268     * {@inheritDoc}
269     * @return end time of latest OutputInterval as LocalDateTime from default TurnoutManager
270     */
271    @Nonnull
272    @Override
273    public LocalDateTime outputIntervalEnds() {
274        log.debug("outputIntervalEnds called in ProxyTurnoutManager");
275        return ((TurnoutManager) getDefaultManager()).outputIntervalEnds();
276    }
277
278    /**
279     * {@inheritDoc}
280     */
281    @Override
282    public int getXMLOrder() {
283        return jmri.Manager.TURNOUTS;
284    }
285
286    /**
287     * {@inheritDoc}
288     */
289    @Override
290    @Nonnull
291    public String getBeanTypeHandled(boolean plural) {
292        return Bundle.getMessage(plural ? "BeanNameTurnouts" : "BeanNameTurnout");
293    }
294
295    /**
296     * {@inheritDoc}
297     */
298    @Override
299    public Class<Turnout> getNamedBeanClass() {
300        return Turnout.class;
301    }
302
303    // initialize logging
304    private final static Logger log = LoggerFactory.getLogger(ProxyTurnoutManager.class);
305
306}