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