001package jmri.jmrix.oaktree;
002
003import java.util.Locale;
004import javax.annotation.Nonnull;
005import jmri.Sensor;
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008
009/**
010 * Manage the system-specific Sensor implementation.
011 * <p>
012 * System names are "OSnnn", where O is the user configurable system prefix,
013 * nnn is the sensor number without padding.
014 * <p>
015 * Sensors are numbered from 1.
016 *
017 * @author Bob Jacobsen Copyright (C) 2003, 2006
018 * @author Dave Duchamp, multi node extensions, 2004
019 */
020public class SerialSensorManager extends jmri.managers.AbstractSensorManager
021        implements SerialListener {
022
023    public SerialSensorManager() {
024        this(jmri.InstanceManager.getDefault(OakTreeSystemConnectionMemo.class));
025    }
026
027    public SerialSensorManager(OakTreeSystemConnectionMemo memo) {
028        super(memo);
029    }
030
031    /**
032     * Number of sensors per address in the naming scheme.
033     * <p>
034     * The first node address uses sensors from 1 to SENSORSPERNODE-1, the
035     * second from SENSORSPERNODE+1 to SENSORSPERNODE+(SENSORSPERNODE-1), etc.
036     * <p>
037     * Must be more than, and is generally one more than,
038     * {@link SerialNode#MAXSENSORS}
039     */
040    static final int SENSORSPERNODE = 1000;
041
042    /**
043     * {@inheritDoc}
044     */
045    @Override
046    @Nonnull
047    public OakTreeSystemConnectionMemo getMemo() {
048        return (OakTreeSystemConnectionMemo) memo;
049    }
050
051    /**
052     * {@inheritDoc}
053     * <p>
054     * System name is normalized to ensure uniqueness.
055     * @throws IllegalArgumentException when SystemName can't be converted
056     */
057    @Override
058    @Nonnull
059    protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException {
060        Sensor s;
061        // validate the system name, and normalize it
062        String sName = SerialAddress.normalizeSystemName(systemName, getSystemPrefix());
063        if (sName.isEmpty()) {
064            // system name is not valid
065            throw new IllegalArgumentException("Invalid Oaktree Sensor system name - " +  // NOI18N
066                    systemName);
067        }
068        // does this Sensor already exist
069        s = getBySystemName(sName);
070        if (s != null) {
071            throw new IllegalArgumentException("Oaktree Sensor with this name already exists - " +  // NOI18N
072                    systemName);
073        }
074        // check under alternate name
075        String altName = SerialAddress.convertSystemNameToAlternate(sName, getSystemPrefix());
076        s = getBySystemName(altName);
077        if (s != null) {
078            throw new IllegalArgumentException("Grapevine Sensor with name  " +  // NOI18N
079                    systemName + " already exists as " + altName);
080        }
081        // check bit number
082        int bit = SerialAddress.getBitFromSystemName(sName, getSystemPrefix());
083        if ((bit <= 0) || (bit >= SENSORSPERNODE)) {
084            throw new IllegalArgumentException("Sensor bit number " +  // NOI18N
085                    Integer.toString(bit) + " is outside the supported range 1-" +
086                    Integer.toString(SENSORSPERNODE - 1));
087        }
088        // Sensor system name is valid and Sensor doesn't exist, make a new one
089        if (userName == null) {
090            s = new SerialSensor(sName);
091        } else {
092            s = new SerialSensor(sName, userName);
093        }
094
095        // ensure that a corresponding Serial Node exists
096        SerialNode node = SerialAddress.getNodeFromSystemName(sName, getMemo().getTrafficController());
097        if (node == null) {
098            log.warn("Sensor {} refers to an undefined Serial Node.", sName);
099            return s;
100        }
101        // register this sensor with the Serial Node
102        node.registerSensor(s, bit - 1);
103        return s;
104    }
105
106    /**
107     * {@inheritDoc}
108     */
109    @Override
110    @Nonnull
111    public String validateSystemNameFormat(@Nonnull String systemName, @Nonnull Locale locale) {
112        return SerialAddress.validateSystemNameFormat(systemName, getSystemNamePrefix(), locale);
113    }
114
115    /**
116     * {@inheritDoc}
117     */
118    @Override
119    public NameValidity validSystemNameFormat(@Nonnull String systemName) {
120        return (SerialAddress.validSystemNameFormat(systemName, typeLetter(), getSystemPrefix()));
121    }
122
123    /**
124     * Dummy routine
125     */
126    @Override
127    public void message(SerialMessage r) {
128        log.warn("unexpected message");
129    }
130
131    /**
132     * Process a reply to a poll of Sensors of one node
133     */
134    @Override
135    public void reply(SerialReply r) {
136        // determine which node
137        SerialNode node = (SerialNode) getMemo().getTrafficController().getNodeFromAddress(r.getAddr());
138        if (node != null) {
139            node.markChanges(r);
140        }
141    }
142
143    /**
144     * Method to register any orphan Sensors when a new Serial Node is created.
145     * @param node node to register.
146     */
147    public void registerSensorsForNode(SerialNode node) {
148        // Iterate through the sensors
149        SerialNode tNode;
150        for (Sensor s : getNamedBeanSet()) {
151            String sName = s.getSystemName();
152            log.debug("system name is {}", sName);
153            if (sName.startsWith(getSystemNamePrefix())) { // multichar prefix
154                // This is a Sensor
155                tNode = SerialAddress.getNodeFromSystemName(sName, getMemo().getTrafficController());
156                if (tNode == node) {
157                    // This sensor is for this new Serial Node - register it
158                    node.registerSensor(s,
159                            (SerialAddress.getBitFromSystemName(sName, getSystemPrefix()) - 1));
160                }
161            }
162        }
163    }
164
165    /**
166     * {@inheritDoc}
167     */
168    @Override
169    public String getEntryToolTip() {
170        return Bundle.getMessage("AddInputEntryToolTip");
171    }
172
173    @Override
174    public boolean allowMultipleAdditions(@Nonnull String systemName) {
175        return true;
176    }
177
178    private final static Logger log = LoggerFactory.getLogger(SerialSensorManager.class);
179
180}