001package jmri.managers;
002
003import java.util.Enumeration;
004import java.util.Objects;
005import javax.annotation.Nonnull;
006import jmri.JmriException;
007import jmri.Manager;
008import jmri.Sensor;
009import jmri.SensorManager;
010import jmri.SystemConnectionMemo;
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014/**
015 * Abstract base implementation of the SensorManager interface.
016 *
017 * @author Bob Jacobsen Copyright (C) 2001, 2003
018 */
019public abstract class AbstractSensorManager extends AbstractManager<Sensor> implements SensorManager {
020
021    /**
022     * Create a new SensorManager instance.
023     *
024     * @param memo the system connection
025     */
026    public AbstractSensorManager(SystemConnectionMemo memo) {
027        super(memo);
028    }
029
030    /** {@inheritDoc} */
031    @Override
032    public int getXMLOrder() {
033        return Manager.SENSORS;
034    }
035
036    /** {@inheritDoc} */
037    @Override
038    public char typeLetter() {
039        return 'S';
040    }
041
042    /** {@inheritDoc} */
043    @Override
044    @Nonnull
045    public Sensor provideSensor(@Nonnull String name) {
046        Sensor t = getSensor(name);
047        if (t == null) {
048            t = newSensor(makeSystemName(name), null);
049        }
050        return t;
051    }
052
053    /** {@inheritDoc} */
054    @Override
055    public Sensor getSensor(@Nonnull String name) {
056        Sensor t = getByUserName(name);
057        if (t != null) {
058            return t;
059        }
060        return getBySystemName(name);
061    }
062
063    static final java.util.regex.Matcher numberMatcher = java.util.regex.Pattern.compile("\\d++").matcher("");
064
065    boolean isNumber(@Nonnull String s) {
066        synchronized (numberMatcher) {
067            return numberMatcher.reset(s).matches();
068        }
069    }
070
071    /** {@inheritDoc}
072     * Special handling for numeric argument, which is treated as the suffix of a new system name
073    */
074    @Override
075
076    public Sensor getBySystemName(@Nonnull String key) {
077        if (isNumber(key)) {
078            key = makeSystemName(key);
079        }
080        return _tsys.get(key);
081    }
082
083    /**
084     * Create a New Sensor.
085     * {@inheritDoc}
086     */
087    @Override
088    @Nonnull
089    public Sensor newSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException {
090        log.debug(" newSensor(\"{}\", \"{}\")", systemName, (userName == null ? "null" : userName));
091        Objects.requireNonNull(systemName, "SystemName cannot be null. UserName was "
092                + (userName == null ? "null" : userName));  // NOI18N
093        systemName = validateSystemNameFormat(systemName);
094        // return existing if there is one
095        Sensor s;
096        if (userName != null) {
097            s = getByUserName(userName);
098            if (s != null) {
099                if (getBySystemName(systemName) != s) {
100                    log.error("inconsistent user ({}) and system name ({}) results; userName related to ({})", userName, systemName, s.getSystemName());
101                }
102                return s;
103            }
104        }
105        s = getBySystemName(systemName);
106        if (s != null) {
107            if ((s.getUserName() == null) && (userName != null)) {
108                s.setUserName(userName);
109            } else if (userName != null) {
110                log.warn("Found sensor via system name ({}) with non-null user name ({}). Sensor \"{}({})\" cannot be used.",
111                        systemName, s.getUserName(), systemName, userName);
112            }
113            return s;
114        }
115        // doesn't exist, make a new one
116        s = createNewSensor(systemName, userName);
117        // save in the maps
118        register(s);
119
120        return s;
121    }
122
123    /** {@inheritDoc} */
124    @Override
125    @Nonnull
126    public String getBeanTypeHandled(boolean plural) {
127        return Bundle.getMessage(plural ? "BeanNameSensors" : "BeanNameSensor");
128    }
129
130    /**
131     * {@inheritDoc}
132     */
133    @Override
134    public Class<Sensor> getNamedBeanClass() {
135        return Sensor.class;
136    }
137
138    /**
139     * Internal method to invoke the factory and create a new Sensor.
140     *
141     * Called after all the logic for returning an existing Sensor
142     * has been invoked.
143     * An existing SystemName is not found, existing UserName not found.
144     *
145     * Implementing classes should base Sensor on the system name, then add user name.
146     *
147     * @param systemName the system name to use for the new Sensor
148     * @param userName   the optional user name to use for the new Sensor
149     * @return the new Sensor
150     * @throws IllegalArgumentException if unsuccessful with reason for fail.
151     */
152    @Nonnull
153    abstract protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException;
154
155    /**
156     * {@inheritDoc}
157     * Note that this null implementation only needs be implemented in
158     * system-specific SensorManagers where readout of sensor status from the
159     * layout is possible.
160     */
161    @Override
162    public void updateAll() {
163    }
164
165    /**
166     * Default Sensor ensures a numeric only system name.
167     * {@inheritDoc}
168     */
169    @Nonnull
170    @Override
171    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException {
172        return prefix + typeLetter() + checkNumeric(curAddress);
173    }
174
175    protected long sensorDebounceGoingActive = 0L;
176    protected long sensorDebounceGoingInActive = 0L;
177
178    /** {@inheritDoc} */
179    @Override
180    public long getDefaultSensorDebounceGoingActive() {
181        return sensorDebounceGoingActive;
182    }
183
184    /** {@inheritDoc} */
185    @Override
186    public long getDefaultSensorDebounceGoingInActive() {
187        return sensorDebounceGoingInActive;
188    }
189
190    /** {@inheritDoc} */
191    @Override
192    public void setDefaultSensorDebounceGoingActive(long time) {
193        if (time == sensorDebounceGoingActive) {
194            return;
195        }
196        sensorDebounceGoingActive = time;
197        Enumeration<String> en = _tsys.keys();
198        while (en.hasMoreElements()) {
199            Sensor sen = _tsys.get(en.nextElement());
200            if (sen.getUseDefaultTimerSettings()) {
201                sen.setSensorDebounceGoingActiveTimer(time);
202            }
203        }
204    }
205
206    /** {@inheritDoc} */
207    @Override
208    public void setDefaultSensorDebounceGoingInActive(long time) {
209        if (time == sensorDebounceGoingInActive) {
210            return;
211        }
212        sensorDebounceGoingInActive = time;
213        Enumeration<String> en = _tsys.keys();
214        while (en.hasMoreElements()) {
215            Sensor sen = _tsys.get(en.nextElement());
216            if (sen.getUseDefaultTimerSettings()) {
217                sen.setSensorDebounceGoingInActiveTimer(time);
218            }
219        }
220    }
221
222    /**
223     * {@inheritDoc}
224     * This default implementation always returns false.
225     *
226     * @return true if pull up/pull down configuration is supported.
227     */
228    @Override
229    public boolean isPullResistanceConfigurable(){
230       return false;
231    }
232
233    /** {@inheritDoc} */
234    @Override
235    public String getEntryToolTip() {
236        return Bundle.getMessage("EnterNumber1to9999ToolTip");
237    }
238
239    private final static Logger log = LoggerFactory.getLogger(AbstractSensorManager.class);
240
241}