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}