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