001package jmri.jmrix.ieee802154.xbee;
002
003import java.util.Locale;
004import javax.annotation.Nonnull;
005import jmri.JmriException;
006import jmri.NamedBean;
007import jmri.Turnout;
008import jmri.managers.AbstractTurnoutManager;
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012/**
013 * Implement turnout manager for XBee connections
014 *
015 * @author Paul Bender Copyright (C) 2014
016 */
017public class XBeeTurnoutManager extends AbstractTurnoutManager {
018
019    protected XBeeTrafficController tc = null;
020
021    public XBeeTurnoutManager(XBeeConnectionMemo memo) {
022        super(memo);
023        tc = (XBeeTrafficController) memo.getTrafficController();
024    }
025
026    /**
027     * {@inheritDoc}
028     */
029    @Override
030    @Nonnull
031    public XBeeConnectionMemo getMemo() {
032        return (XBeeConnectionMemo) memo;
033    }
034
035    // for now, set this to false. Multiple additions currently works
036    // partially, but not for all possible cases.
037    @Override
038    public boolean allowMultipleAdditions(@Nonnull String systemName) {
039        return false;
040    }
041
042    /**
043     * {@inheritDoc}
044     */
045    @Nonnull
046    @Override
047    protected Turnout createNewTurnout(@Nonnull String systemName, String userName) throws IllegalArgumentException {
048        XBeeNode curNode;
049        String name = addressFromSystemName(systemName);
050        if ((curNode = (XBeeNode) tc.getNodeFromName(name)) == null) {
051            if ((curNode = (XBeeNode) tc.getNodeFromAddress(name)) == null) {
052                try {
053                    curNode = (XBeeNode) tc.getNodeFromAddress(Integer.parseInt(name));
054                } catch (java.lang.NumberFormatException nfe) {
055                    // if there was a number format exception, we couldn't
056                    // find the node.
057                    log.debug("failed to create turnout {}", systemName);
058                    throw new IllegalArgumentException("Cannot find Node, check Turnout System Name " + systemName);
059                }
060            }
061        }
062        int pin = pinFromSystemName(systemName);
063        int pin2 = pin2FromSystemName(systemName);
064        if (!curNode.getPinAssigned(pin)
065                && (pin2 == -1 || !curNode.getPinAssigned(pin2))) {
066            log.debug("Adding turnout to pin {}", pin);
067            curNode.setPinBean(pin, new XBeeTurnout(systemName, userName, tc));
068            if (pin2 != -1) {
069                curNode.setPinBean(pin2, curNode.getPinBean(pin));
070            }
071            return (XBeeTurnout) curNode.getPinBean(pin);
072        } else {
073            log.debug("failed to create turnout {}", systemName);
074            throw new IllegalArgumentException("Cannot create Turnout " + systemName + ", check pins.");
075        }
076    }
077
078    @Override
079    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException {
080        return prefix + typeLetter() + curAddress;
081    }
082
083    /**
084     * {@inheritDoc}
085     */
086    @Override
087    @Nonnull
088    public String validateSystemNameFormat(@Nonnull String name, @Nonnull Locale locale) {
089        super.validateSystemNameFormat(name, locale);
090        int pin = pinFromSystemName(name);
091        int pin2 = pin2FromSystemName(name);
092        if (pin < 0 || pin > 7) {
093            throw new NamedBean.BadSystemNameException(
094                    Bundle.getMessage(Locale.ENGLISH, "SystemNameInvalidPin", name),
095                    Bundle.getMessage(locale, "SystemNameInvalidPin", name));
096        }
097        if (pin2 != -1 && (pin2 < 0 || pin2 > 7)) {
098            throw new NamedBean.BadSystemNameException(
099                    Bundle.getMessage(Locale.ENGLISH, "SystemNameInvalidPin", name),
100                    Bundle.getMessage(locale, "SystemNameInvalidPin", name));
101        }
102        return name;
103    }
104
105    /**
106     * {@inheritDoc}
107     */
108    @Override
109    public NameValidity validSystemNameFormat(@Nonnull String systemName) {
110        if (tc.getNodeFromName(addressFromSystemName(systemName)) == null
111                && tc.getNodeFromAddress(addressFromSystemName(systemName)) == null) {
112            try {
113                if (tc.getNodeFromAddress(Integer.parseInt(addressFromSystemName(systemName))) == null) {
114                    return NameValidity.INVALID;
115                } else {
116                    return (pinFromSystemName(systemName) >= 0
117                            && pinFromSystemName(systemName) <= 7
118                            && (pin2FromSystemName(systemName) == -1
119                            || (pin2FromSystemName(systemName) >= 0
120                            && pin2FromSystemName(systemName) <= 7))) ? NameValidity.VALID : NameValidity.INVALID;
121                }
122            } catch (java.lang.NumberFormatException nfe) {
123                // if there was a number format exception, we couldn't
124                // find the node.
125                log.error("Unable to convert {} into the Xbee node and pin format of nn:xx", systemName);
126                return NameValidity.INVALID;
127            }
128
129        } else {
130            return (pinFromSystemName(systemName) >= 0
131                    && pinFromSystemName(systemName) <= 7
132                    && (pin2FromSystemName(systemName) == -1
133                    || (pin2FromSystemName(systemName) >= 0
134                    && pin2FromSystemName(systemName) <= 7))) ? NameValidity.VALID : NameValidity.INVALID;
135        }
136    }
137
138    private String addressFromSystemName(@Nonnull String systemName) {
139        String encoderAddress;
140
141        if (systemName.contains(":")) {
142            //Address format passed is in the form of encoderAddress:input or S:turnout address
143            int seperator = systemName.indexOf(":");
144            encoderAddress = systemName.substring(getSystemPrefix().length() + 1, seperator);
145        } else {
146            encoderAddress = systemName.substring(getSystemPrefix().length() + 1, systemName.length() - 1);
147        }
148        log.debug("Converted {} to hardware address {}", systemName, encoderAddress);
149        return encoderAddress;
150    }
151
152    private int pinFromSystemName(@Nonnull String systemName) {
153        int input = 0;
154        int iName = 0;
155
156        if (systemName.contains(":")) {
157            //Address format passed is in the form of encoderAddress:input or T:turnout address
158            int seperator = systemName.indexOf(":");
159            int seperator2 = systemName.indexOf(":", seperator + 1);
160            int len = systemName.length();
161            try {
162                if ((seperator2 >= 0) && (seperator2 <= len)) {
163                    input = Integer.parseInt(systemName.substring(seperator + 1, seperator2));
164                } else {
165                    input = Integer.parseInt(systemName.substring(seperator + 1, len));
166                }
167            } catch (NumberFormatException ex) {
168                log.debug("Unable to convert {} into the XBee node and pin format of nn:xx", systemName);
169                return -1;
170            }
171        } else {
172            try {
173                iName = Integer.parseInt(systemName.substring(getSystemPrefix().length() + 1));
174                input = iName % 10;
175            } catch (NumberFormatException ex) {
176                log.debug("Unable to convert {} system name to a number", systemName);
177                return -1;
178            }
179        }
180        log.debug("Converted {} to pin number {}", systemName, input);
181        return input;
182    }
183
184    private int pin2FromSystemName(@Nonnull String systemName) {
185        int input = 0;
186
187        if (systemName.contains(":")) {
188            //Address format passed is in the form of encoderAddress:input or T:turnout address
189            int seperator = systemName.indexOf(":");
190            int seperator2 = systemName.indexOf(":", seperator + 1);
191            try {
192                input = Integer.parseInt(systemName.substring(seperator2 + 1));
193            } catch (NumberFormatException ex) {
194                log.debug("Unable to convert {} into the cab and input format of nn:xx", systemName);
195                return -1;
196            }
197        } else {
198            // no ":" means only one pin.
199            input = -1;
200        }
201        log.debug("Converted {} to pin number {}", systemName, input);
202        return input;
203    }
204
205    @Override
206    public void deregister(@Nonnull jmri.Turnout t) {
207        super.deregister(t);
208        // remove the specified turnout from the associated XBee pin.
209        String systemName = t.getSystemName();
210        String name = addressFromSystemName(systemName);
211        int pin = pinFromSystemName(systemName);
212        XBeeNode curNode;
213        if ((curNode = (XBeeNode) tc.getNodeFromName(name)) == null) {
214            if ((curNode = (XBeeNode) tc.getNodeFromAddress(name)) == null) {
215                try {
216                    curNode = (XBeeNode) tc.getNodeFromAddress(Integer.parseInt(name));
217                } catch (java.lang.NumberFormatException nfe) {
218                    // if there was a number format exception, we couldn't find the node.
219                    curNode = null;
220                }
221            }
222        }
223        if (curNode != null) {
224            if (curNode.removePinBean(pin, t)) {
225                log.debug("Removing turnout from pin {}", pin);
226            } else {
227                log.debug("Failed to removing turnout from pin {}", pin);
228            }
229        }
230    }
231
232    /**
233     * {@inheritDoc}
234     */
235    @Override
236    public String getEntryToolTip() {
237        return Bundle.getMessage("AddTurnoutEntryToolTip");
238    }
239
240    private final static Logger log = LoggerFactory.getLogger(XBeeTurnoutManager.class);
241
242}