001package jmri.jmrix.ieee802154.xbee; 002 003import com.digi.xbee.api.RemoteXBeeDevice; 004import com.digi.xbee.api.exceptions.InterfaceNotOpenException; 005import com.digi.xbee.api.exceptions.TimeoutException; 006import com.digi.xbee.api.exceptions.XBeeException; 007import com.digi.xbee.api.io.IOLine; 008import com.digi.xbee.api.io.IOSample; 009import com.digi.xbee.api.io.IOValue; 010import com.digi.xbee.api.listeners.IIOSampleReceiveListener; 011import jmri.Sensor; 012import jmri.implementation.AbstractSensor; 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016/** 017 * Extend jmri.AbstractSensor for XBee connections. 018 * 019 * @author Paul Bender Copyright (C) 2013 020 */ 021public class XBeeSensor extends AbstractSensor implements IIOSampleReceiveListener { 022 023 private String nodeIdentifier; /* This is a string representation of 024 the XBee address in the system name. 025 It may be an address or it may be 026 the nodeIdentifier string stored in 027 the NI parameter on the node.*/ 028 029 private int pin; /* Which DIO pin does this sensor represent. */ 030 031 private XBeeNode node = null; // Which node does this belong too. 032 033 protected XBeeTrafficController tc = null; 034 035 public XBeeSensor(String systemName, String userName, XBeeTrafficController controller) { 036 super(systemName, userName); 037 tc = controller; 038 init(systemName); 039 } 040 041 public XBeeSensor(String systemName, XBeeTrafficController controller) { 042 super(systemName); 043 tc = controller; 044 init(systemName); 045 } 046 047 /** 048 * Common initialization for both constructors. 049 */ 050 private void init(String id) { 051 // store address 052 jmri.jmrix.ieee802154.IEEE802154SystemConnectionMemo m = tc.getAdapterMemo(); 053 if( !(m instanceof XBeeConnectionMemo)) 054 { 055 log.error("Memo associated with the traffic controller is not the right type"); 056 throw new IllegalArgumentException("Memo associated with the traffic controller is not the right type"); 057 } else { 058 XBeeConnectionMemo memo = (XBeeConnectionMemo) m; 059 String prefix = memo.getSensorManager().getSystemPrefix(); 060 061 if (id.contains(":")) { 062 //Address format passed is in the form of encoderAddress:input or S:sensor address 063 int seperator = id.indexOf(":"); 064 try { 065 nodeIdentifier = id.substring(prefix.length() + 1, seperator); 066 if ((node = (XBeeNode) tc.getNodeFromName(nodeIdentifier)) == null) { 067 if ((node = (XBeeNode) tc.getNodeFromAddress(nodeIdentifier)) == null) { 068 try { 069 node = (XBeeNode) tc.getNodeFromAddress(Integer.parseInt(nodeIdentifier)); 070 } catch (java.lang.NumberFormatException nfe) { 071 // if there was a number format exception, we couldn't 072 // find the node. 073 node = null; 074 } 075 } 076 } 077 pin = Integer.parseInt(id.substring(seperator + 1)); 078 } catch (NumberFormatException ex) { 079 log.debug("Unable to convert {} into the cab and input format of nn:xx", id); 080 } 081 } else { 082 try { 083 nodeIdentifier = id.substring(prefix.length() + 1, id.length() - 1); 084 int address = Integer.parseInt(id.substring(prefix.length() + 1)); 085 node = (XBeeNode) tc.getNodeFromAddress(address / 10); 086 // calculate the pin to examine 087 pin = ((address) % 10); 088 } catch (NumberFormatException ex) { 089 log.debug("Unable to convert {} Hardware Address to a number", id); 090 } 091 } 092 if (log.isDebugEnabled()) { 093 log.debug("Created Sensor {} (NodeIdentifier {} ,D{})", id, nodeIdentifier, pin); 094 } 095 096 // register to hear XBee IO Sample events. 097 tc.getXBee().addIOSampleListener(this); 098 099 // Finally, request the current state from the layout. 100 this.requestUpdateFromLayout(); 101 } 102 } 103 104 /** 105 * Request an update on status by sending an XBee message. 106 */ 107 @Override 108 public void requestUpdateFromLayout() { 109 // Request the sensor status from the XBee Node this sensor is 110 // attached to. 111 try { 112 IOValue value = node.getXBee().getDIOValue(IOLine.getDIO(pin)); 113 if ((value==IOValue.HIGH) ^ _inverted) { 114 setOwnState(Sensor.ACTIVE); 115 } else { 116 setOwnState(Sensor.INACTIVE); 117 } 118 } catch (TimeoutException toe) { 119 log.error("Timeout retrieving IO line value for {} on {}",IOLine.getDIO(pin),node.getXBee()); 120 // hidden terminal? Make sure the state appears as unknown. 121 setOwnState(Sensor.UNKNOWN); 122 } catch (InterfaceNotOpenException ino) { 123 log.error("Interface Not Open retrieving IO line value for {} on {}",IOLine.getDIO(pin),node.getXBee()); 124 } catch (XBeeException xbe) { 125 log.error("Error retrieving IO line value for {} on {}",IOLine.getDIO(pin),node.getXBee()); 126 } 127 } 128 129 130 // IIOSampleReceiveListener methods 131 132 @Override 133 public synchronized void ioSampleReceived(RemoteXBeeDevice remoteDevice,IOSample ioSample) { 134 if (log.isDebugEnabled()) { 135 log.debug("received io sample {} from {}", ioSample, remoteDevice); 136 } 137 138 XBeeNode sourcenode = (XBeeNode) tc.getNodeFromXBeeDevice(remoteDevice); 139 140 if (node.equals(sourcenode)) { 141 if ( ioSample.hasDigitalValues()){ 142 if ((ioSample.getDigitalValue(IOLine.getDIO(pin))==IOValue.HIGH) ^ _inverted) { 143 setOwnState(Sensor.ACTIVE); 144 } else { 145 setOwnState(Sensor.INACTIVE); 146 } 147 } 148 } 149 } 150 151 /** 152 * Set the pull resistance. 153 * <p> 154 * In this default implementation, the input value is ignored. 155 * 156 * @param r PullResistance value to use 157 */ 158 @Override 159 public void setPullResistance(PullResistance r){ 160 try { 161 node.setPRParameter(pin,r); 162 } catch (TimeoutException toe) { 163 log.error("Timeout retrieving PR value for {} on {}",IOLine.getDIO(pin),node.getXBee()); 164 } catch (XBeeException xbe) { 165 log.error("Error retrieving PR value for {} on {}",IOLine.getDIO(pin),node.getXBee()); 166 } 167 } 168 169 /** 170 * Get the pull resistance. 171 * 172 * @return the currently set PullResistance value 173 */ 174 @Override 175 public PullResistance getPullResistance(){ 176 try { 177 return node.getPRValueForPin(pin); 178 } catch (TimeoutException toe) { 179 log.error("Timeout retrieving PR value for {} on {}",IOLine.getDIO(pin),node.getXBee()); 180 } catch (XBeeException xbe) { 181 log.error("Error retrieving PR value for {} on {}",IOLine.getDIO(pin),node.getXBee()); 182 } 183 return PullResistance.PULL_UP; // return the default if we get this far. 184 } 185 186 @Override 187 public void dispose() { 188 tc.getXBee().removeIOSampleListener(this); 189 super.dispose(); 190 } 191 192 private final static Logger log = LoggerFactory.getLogger(XBeeSensor.class); 193 194}