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