001package jmri.jmrix.ieee802154.xbee;
002
003import com.digi.xbee.api.exceptions.InterfaceNotOpenException;
004import com.digi.xbee.api.exceptions.TimeoutException;
005import com.digi.xbee.api.exceptions.XBeeException;
006import com.digi.xbee.api.io.IOLine;
007import com.digi.xbee.api.io.IOValue;
008import jmri.Turnout;
009import jmri.implementation.AbstractTurnout;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013/**
014 * Turnout implementation for XBee systems.
015 *
016 * @author Paul Bender Copyright (C) 2014
017 */
018public class XBeeTurnout extends AbstractTurnout {
019
020
021
022    private XBeeNode node = null; // Which node does this belong too.
023
024    private int pin;
025    /* Which DIO pin does this turnout represent. */
026
027    private int pin2 = -1;
028    /* Which 2nd DIO pin does this turnout represent. */
029
030    protected XBeeTrafficController tc = null;
031
032    /**
033     * Create a Turnout object, with system and user names and a reference to
034     * the traffic controller.
035     *
036     * @param systemName Xbee id : pin
037     * @param userName   friendly text name
038     * @param controller tc for node connection
039     */
040    public XBeeTurnout(String systemName, String userName, XBeeTrafficController controller) {
041        super(systemName, userName);
042        tc = controller;
043        init(systemName);
044    }
045
046    public XBeeTurnout(String systemName, XBeeTrafficController controller) {
047        super(systemName);
048        tc = controller;
049        init(systemName);
050    }
051
052    /**
053     * Common initialization for both constructors
054     */
055    private void init(String id) {
056        // store address
057        jmri.jmrix.ieee802154.IEEE802154SystemConnectionMemo m = tc.getAdapterMemo();
058        if (!(m instanceof XBeeConnectionMemo)) {
059            log.error("Memo associated with the traffic controller is not the right type");
060            throw new IllegalArgumentException("Memo associated with the traffic controller is not the right type");
061        } else {
062            XBeeConnectionMemo memo = (XBeeConnectionMemo) m;
063            String prefix = memo.getTurnoutManager().getSystemPrefix();
064            String nodeIdentifier;     /* This is a string representation of
065                                          the XBee address in the system name
066                                          It may be an address or it may be
067                                          the NodeIdentifier string stored in
068                                          the NI parameter on the node.*/
069            if (id.contains(":")) {
070                //Address format passed is in the form of encoderAddress:output or T:turnout address
071                int seperator = id.indexOf(":");
072                int seperator2 = id.indexOf(":", seperator + 1);
073                try {
074                    nodeIdentifier = id.substring(prefix.length() + 1, seperator);
075                    if ((node = (XBeeNode) tc.getNodeFromName(nodeIdentifier)) == null) {
076                        if ((node = (XBeeNode) tc.getNodeFromAddress(nodeIdentifier)) == null) {
077                            try {
078                                node = (XBeeNode) tc.getNodeFromAddress(Integer.parseInt(nodeIdentifier));
079                            } catch (java.lang.NumberFormatException nfe) {
080                                // if there was a number format exception, we couldn't
081                                // find the node.
082                                node = null;
083                                throw new IllegalArgumentException("Node not defined");
084                            }
085                        }
086                    }
087                    pin = Integer.parseInt(id.substring(seperator + 1, seperator2 > 0 ? seperator2 : id.length()));
088                    if (seperator2 > 0) {
089                        pin2 = Integer.parseInt(id.substring(seperator2 + 1));
090                    }
091                } catch (NumberFormatException ex) {
092                    log.debug("Unable to convert {} into the cab and input format of nn:xx", id);
093                    throw new IllegalArgumentException("Unable to convert " + id + " into the cab and input format of nn:xx");
094                }
095            } else {
096                try {
097                    nodeIdentifier = id.substring(prefix.length() + 1, id.length() - 1);
098                    int address = Integer.parseInt(id.substring(prefix.length() + 1));
099                    node = (XBeeNode) tc.getNodeFromAddress(address / 10);
100                    // calculate the pin to use.
101                    pin = ((address) % 10);
102                } catch (NumberFormatException ex) {
103                    log.debug("Unable to convert {} Hardware Address to a number", id);
104                    throw new IllegalArgumentException("Unable to convert " + id + " Hardware Address to a number");
105                }
106            }
107            if (log.isDebugEnabled()) {
108                log.debug("Created Turnout {} (NodeIdentifier {} D{}{})", id, nodeIdentifier, pin, pin2 > 0 ? " D" + pin2 : "");
109            }
110        }
111    }
112
113    /**
114     * {@inheritDoc}
115     * Sets the XBee node DIO pin value.
116     */
117    @Override
118    protected void forwardCommandChangeToLayout(int s) {
119        try {
120            if ((s == Turnout.THROWN) ^ getInverted()) {
121                node.getXBee().setDIOValue(IOLine.getDIO(pin), IOValue.HIGH);
122            } else {
123                node.getXBee().setDIOValue(IOLine.getDIO(pin), IOValue.LOW);
124            }
125
126            if (pin2 >= 0) {
127                if ((s == Turnout.CLOSED) ^ getInverted()) {
128                    node.getXBee().setDIOValue(IOLine.getDIO(pin), IOValue.HIGH);
129                } else {
130                    node.getXBee().setDIOValue(IOLine.getDIO(pin), IOValue.LOW);
131                }
132
133            }
134        } catch (TimeoutException toe) {
135            log.error("Timeout setting IO line value for turnout {} on {}", getUserName(), node.getXBee());
136        } catch (InterfaceNotOpenException ino) {
137            log.error("Interface Not Open setting IO line value for turnout {} on {}", getUserName(), node.getXBee());
138        } catch (XBeeException xbe) {
139            log.error("Error setting IO line value for turout {} on {}", getUserName(), node.getXBee());
140        }
141    }
142
143    /**
144     * XBee turnouts do support inversion
145     */
146    @Override
147    public boolean canInvert() {
148        return true;
149    }
150
151    @Override
152    protected void turnoutPushbuttonLockout(boolean locked) {
153    }
154
155    private final static Logger log = LoggerFactory.getLogger(XBeeTurnout.class);
156}