001package jmri.jmrix.maple;
002
003import java.util.Locale;
004import javax.annotation.Nonnull;
005import jmri.JmriException;
006import jmri.Sensor;
007
008/**
009 * Manage the specific Sensor implementation.
010 * <p>
011 * System names are "KSnnnn", where K is the user configurable system prefix,
012 * nnnn is the sensor number without padding.
013 * <p>
014 * Sensors are numbered from 1.
015 * <p>
016 * This is a SerialListener to handle the replies to poll messages. Those are
017 * forwarded to the specific SerialNode object corresponding to their origin for
018 * processing of the data.
019 *
020 * @author Bob Jacobsen Copyright (C) 2003, 2007, 2008
021 * @author Dave Duchamp, multi node extensions, 2004
022 */
023public class SerialSensorManager extends jmri.managers.AbstractSensorManager
024        implements SerialListener {
025
026    /**
027     * Number of sensors per UA in the naming scheme.
028     * <p>
029     * The first UA (node address) uses sensors from 1 to SENSORSPERUA-1, the
030     * second from SENSORSPERUA+1 to SENSORSPERUA+(SENSORSPERUA-1), etc.
031     * <p>
032     * Must be more than, and is generally one more than,
033     * {@link SerialNode#MAXSENSORS}
034     */
035    static final int SENSORSPERUA = 1000;
036
037    public SerialSensorManager(MapleSystemConnectionMemo memo) {
038        super(memo);
039    }
040
041    /**
042     * {@inheritDoc}
043     */
044    @Override
045    @Nonnull
046    public MapleSystemConnectionMemo getMemo() {
047        return (MapleSystemConnectionMemo) memo;
048    }
049
050    /**
051     * {@inheritDoc}
052     * <p>
053     * System name is normalized to ensure uniqueness.
054     *
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 Maple 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("Maple Sensor with this name already exists - " +  // NOI18N
072                    systemName);
073        }
074        // check bit number
075        int bit = SerialAddress.getBitFromSystemName(sName, getSystemPrefix());
076        if ((bit <= 0) || (bit > 1000)) {
077            log.warn("Sensor bit number '{}' is outside the supported range, 1-1000", Integer.toString(bit));
078            throw new IllegalArgumentException("Sensor bit number " +  // NOI18N
079                    Integer.toString(bit) + " is outside the supported range 1-1000");
080        }
081        // Sensor system name is valid and Sensor doesn't exist, make a new one
082        if (userName == null) {
083            s = new SerialSensor(sName); // prefix not passed
084        } else {
085            s = new SerialSensor(sName, userName); // prefix not passed
086        }
087        // check configured
088        if (!SerialAddress.validSystemNameConfig(sName, 'S', getMemo())) {
089            log.warn("Sensor system Name '{}' does not address configured hardware.", sName);
090            jmri.util.swing.JmriJOptionPane.showMessageDialog(null, "WARNING - The Sensor just added, "
091                    + sName + ", refers to an unconfigured input bit.", "Configuration Warning",
092                    jmri.util.swing.JmriJOptionPane.INFORMATION_MESSAGE);
093        }
094        // register this sensor
095        getMemo().getTrafficController().inputBits().registerSensor(s, bit - 1);
096        return s;
097    }
098
099    /**
100     * {@inheritDoc}
101     */
102    @Override
103    @Nonnull
104    public String validateSystemNameFormat(@Nonnull String name, @Nonnull Locale locale) {
105        return SerialAddress.validateSystemNameFormat(name, this, locale);
106    }
107
108    /**
109     * {@inheritDoc}
110     */
111    @Override
112    public NameValidity validSystemNameFormat(@Nonnull String systemName) {
113        return (SerialAddress.validSystemNameFormat(systemName, typeLetter(), getSystemPrefix()));
114    }
115
116    /**
117     * {@inheritDoc}
118     */
119    @Override
120    public String getEntryToolTip() {
121        return Bundle.getMessage("AddInputEntryToolTip");
122    }
123
124    /**
125     * Dummy routine.
126     * @param r unused.
127     */
128    @Override
129    public void message(SerialMessage r) {
130        log.warn("unexpected message");
131    }
132
133    /**
134     * Process a reply to a poll of Sensors of one panel node.
135     * {@inheritDoc}
136     */
137    @Override
138    public void reply(SerialReply r) {
139        getMemo().getTrafficController().inputBits().markChanges(r);
140    }
141
142    /**
143     * Method to register any orphan Sensors when a new Serial Node is created.
144     * @param node node to register.
145     */
146    public void registerSensorsForNode(SerialNode node) {
147        // get list containing all Sensors
148        for (Sensor s : getNamedBeanSet()) {
149            String sName = s.getSystemName();
150            log.debug("system name is {}", sName);
151            if (sName.startsWith(getSystemNamePrefix())) {
152                // This is a valid Sensor - make sure it is registered
153                getMemo().getTrafficController().inputBits().registerSensor(s,
154                        (SerialAddress.getBitFromSystemName(sName, getSystemPrefix()) - 1));
155            }
156        }
157    }
158
159    @Override
160    public boolean allowMultipleAdditions(@Nonnull String systemName) {
161        return true;
162    }
163
164    @Override
165    @Nonnull
166    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException {
167        if (curAddress.contains(":")) {
168            //Address format passed is in the form of sysNode:address or T:turnout address
169            int seperator = curAddress.indexOf(":");
170            try {
171                sysNode = Integer.parseInt(curAddress.substring(0, seperator));
172                address = Integer.parseInt(curAddress.substring(seperator + 1));
173            } catch (NumberFormatException ex) {
174                throw new JmriException("Unable to convert "+curAddress+" into the cab and address format of nn:xx");
175            }
176            iName = (sysNode * 1000) + address;
177        } else {
178            //Entered in using the old format
179            try {
180                iName = Integer.parseInt(curAddress);
181            } catch (NumberFormatException ex) {
182                throw new JmriException("Hardware Address passed "+curAddress+" should be a number or the cab and address format of nn:xx");
183            }
184        }
185        return prefix + typeLetter() + iName;
186    }
187
188    private int sysNode = 0;
189    private int address = 0;
190    private int iName = 0;
191
192    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SerialSensorManager.class);
193
194}