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}