001package jmri.jmrix.cmri.serial;
002
003import java.util.Locale;
004import javax.annotation.Nonnull;
005import jmri.JmriException;
006import jmri.Sensor;
007import jmri.jmrix.AbstractNode;
008import jmri.jmrix.cmri.CMRISystemConnectionMemo;
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012/**
013 * Manage the C/MRI serial-specific Sensor implementation.
014 * <p>
015 * System names are "CSnnnn", where C is the user-configurable system prefix,
016 * nnnn is the sensor number without padding.
017 * <p>
018 * Sensors are numbered from 1.
019 * <p>
020 * This is a SerialListener to handle the replies to poll messages. Those are
021 * forwarded to the specific SerialNode object corresponding to their origin for
022 * processing of the data.
023 *
024 * @author Bob Jacobsen Copyright (C) 2003, 2007
025 * @author Dave Duchamp, multi node extensions, 2004
026 */
027public class SerialSensorManager extends jmri.managers.AbstractSensorManager
028        implements SerialListener {
029
030    /**
031     * Number of sensors per UA in the naming scheme.
032     * <p>
033     * The first UA (node address) uses sensors from 1 to SENSORSPERUA-1, the
034     * second from SENSORSPERUA+1 to SENSORSPERUA+(SENSORSPERUA-1), etc.
035     * <p>
036     * Must be more than, and is generally one more than,
037     * {@link SerialNode#MAXSENSORS}
038     *
039     */
040    static final int SENSORSPERUA = 1000;
041
042    public SerialSensorManager(CMRISystemConnectionMemo memo) {
043        super(memo);
044    }
045
046    /**
047     * {@inheritDoc}
048     */
049    @Override
050    @Nonnull
051    public CMRISystemConnectionMemo getMemo() {
052        return (CMRISystemConnectionMemo) memo;
053    }
054
055    /**
056     * {@inheritDoc}
057     * <p>
058     * System name is normalized to ensure uniqueness.
059     *
060     * @throws IllegalArgumentException when SystemName can't be converted
061     */
062    @Override
063    @Nonnull
064    protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException {
065        Sensor s;
066        // validate the system name, and normalize it
067        String sName = getMemo().normalizeSystemName(systemName);
068        if (sName.isEmpty()) {
069            // system name is not valid
070            throw new IllegalArgumentException("Invalid C/MRI Sensor system name - " +  // NOI18N
071                    systemName);
072        }
073        // does this Sensor already exist
074        s = getBySystemName(sName);
075        if (s != null) {
076            throw new IllegalArgumentException("C/MRI Sensor with this name already exists - " +  // NOI18N
077                    systemName);
078        }
079        // check under alternate name
080        String altName = getMemo().convertSystemNameToAlternate(sName);
081        s = getBySystemName(altName);
082        if (s != null) {
083            throw new IllegalArgumentException("C/MRI Sensor with name  " +  // NOI18N
084                    systemName + " already exists as " + altName);
085        }
086        // check bit number
087        int bit = getMemo().getBitFromSystemName(sName);
088        if ((bit <= 0) || (bit >= SENSORSPERUA)) {
089            throw new IllegalArgumentException("Sensor bit number " +  // NOI18N
090                    Integer.toString(bit) + " is outside the supported range 1-" +
091                    Integer.toString(SENSORSPERUA - 1));
092        }
093        // Sensor system name is valid and Sensor doesn't exist, make a new one
094        if (userName == null) {
095            s = new SerialSensor(sName);
096        } else {
097            s = new SerialSensor(sName, userName);
098        }
099
100        // ensure that a corresponding Serial Node exists
101        SerialNode node = (SerialNode) getMemo().getNodeFromSystemName(sName,getMemo().getTrafficController());
102        if (node == null) {
103            log.warn("Sensor {} refers to an undefined Serial Node.", sName);
104            return s;
105        }
106        // register this sensor with the Serial Node
107        node.registerSensor(s, bit - 1);
108        return s;
109    }
110
111    /**
112     * Dummy routine
113     */
114    @Override
115    public void message(SerialMessage r) {
116        log.warn("unexpected message");
117    }
118
119    /**
120     * Process a reply to a poll of Sensors of one node
121     */
122    @Override
123    public void reply(SerialReply r) {
124        // determine which node
125        SerialNode node = (SerialNode) getMemo().getTrafficController().getNodeFromAddress(r.getUA());
126        if (node != null) {
127            node.markChanges(r);
128        }
129    }
130
131    /**
132     * Method to register any orphan Sensors when a new Serial Node is created.
133     * @param node the node with potential orphan sensors.
134     */
135    public void registerSensorsForNode(SerialNode node) {
136        // get list containing all Sensors
137        AbstractNode tNode;
138        for (Sensor s : getNamedBeanSet()) {
139            String sName = s.getSystemName();
140            log.debug("system name is {}", sName);
141            if ( sName.startsWith(getSystemNamePrefix()) ){
142                // This is a C/MRI Sensor
143                tNode = getMemo().getNodeFromSystemName(sName, getMemo().getTrafficController());
144                if (tNode == node) {
145                    // This sensor is for this new Serial Node - register it
146                    node.registerSensor(s,
147                            (getMemo().getBitFromSystemName(sName) - 1));
148                }
149            }
150        }
151    }
152
153    /**
154     * {@inheritDoc}
155     */
156    @Override
157    public boolean allowMultipleAdditions(@Nonnull String systemName) {
158        return true;
159    }
160
161    /**
162     * {@inheritDoc}
163     */
164    @Override
165    @Nonnull
166    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException {
167        String tmpSName = "";
168        if (curAddress.contains(":")) {
169            //Address format passed is in the form node:address
170            int seperator = curAddress.indexOf(":");
171            try {
172                nAddress = Integer.parseInt(curAddress.substring(0, seperator));
173                bitNum = Integer.parseInt(curAddress.substring(seperator + 1));
174            } catch (NumberFormatException ex) {
175                throw new JmriException("Unable to convert " + curAddress + " to a number.");
176            }
177            tmpSName = getMemo().makeSystemName("S", nAddress, bitNum);
178        } else if (curAddress.contains("B") || (curAddress.contains("b"))) {
179            curAddress = curAddress.toUpperCase();
180            try {
181                //We do this to simply check that we have numbers in the correct places ish
182                Integer.parseInt(curAddress.substring(0, 1));
183                int b = (curAddress.toUpperCase()).indexOf("B") + 1;
184                Integer.parseInt(curAddress.substring(b));
185            } catch (NumberFormatException ex) {
186                throw new JmriException("Unable to convert " + curAddress + " to a number");
187            }
188            tmpSName = prefix + typeLetter() + curAddress;
189            bitNum = getMemo().getBitFromSystemName(tmpSName);
190            nAddress = getMemo().getNodeAddressFromSystemName(tmpSName);
191        } else {
192            try {
193                //We do this to simply check that the value passed is a number!
194                Integer.parseInt(curAddress);
195            } catch (NumberFormatException ex) {
196                throw new JmriException("Unable to convert " + curAddress + " to a valid Hardware Address");
197            }
198            tmpSName = prefix + typeLetter() + curAddress;
199            bitNum = getMemo().getBitFromSystemName(tmpSName);
200            nAddress = getMemo().getNodeAddressFromSystemName(tmpSName);
201        }
202
203        return tmpSName;
204    }
205
206    private int bitNum = 0;
207    private int nAddress = 0;
208
209   /**
210     * {@inheritDoc}
211     */
212    @Override
213    @Nonnull
214    public String validateSystemNameFormat(@Nonnull String systemName, @Nonnull Locale locale) {
215        return getMemo().validateSystemNameFormat(super.validateSystemNameFormat(systemName, locale), typeLetter(), locale);
216    }
217
218    /**
219     * {@inheritDoc}
220     */
221    @Override
222    public NameValidity validSystemNameFormat(@Nonnull String systemName) {
223        return getMemo().validSystemNameFormat(systemName, typeLetter());
224    }
225
226    /**
227     * {@inheritDoc}
228     */
229    @Override
230    public String getEntryToolTip() {
231        return Bundle.getMessage("AddInputEntryToolTip");
232    }
233
234    private final static Logger log = LoggerFactory.getLogger(SerialSensorManager.class);
235
236}