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