push retries to _read method in te923 driver

This commit is contained in:
Matthew Wall
2015-06-09 14:55:21 -04:00
parent b4e43e1656
commit fe2fd0bb7e
2 changed files with 69 additions and 84 deletions

View File

@@ -95,24 +95,6 @@ Memory Map
- 1
0x0000fb - records
Random Notes
Here are some tidbits for usb putzing. The documentation for reading/writing
USB in python is scarce. Apparently there are (at least) two ways of reading
from USB - one using interruptRead, the other doing bulk reads. Which you use
may depend on the device itself.
There are at least two Python interfaces, e.g. claim_interface vs
claimInterface.
usb_control_msg(0x21, # request type
0x09, # request
0x0200, # value
0x0000, # index
buf, # buffer
0x08, # size
timeout) # timeout
"""
# TODO: figure out how to set station time, if possible
@@ -131,7 +113,7 @@ claimInterface.
# do the other 4 bytes represent?
# FIXME: speed up transfers:
# date;PYTHONPATH=bin python bin/weewx/drivers/te923.py --records 0 > b; date
# date; PYTHONPATH=bin python bin/weewx/drivers/te923.py --records 0 > b; date
# Tue Nov 26 10:37:36 EST 2013
# Tue Nov 26 10:46:27 EST 2013
# date; /home/mwall/src/te923tool-0.6.1/te923con -d > a; date
@@ -148,7 +130,7 @@ import weewx.drivers
import weewx.wxformulas
DRIVER_NAME = 'TE923'
DRIVER_VERSION = '0.12'
DRIVER_VERSION = '0.13'
def loader(config_dict, engine):
return TE923Driver(**config_dict[DRIVER_NAME])
@@ -162,7 +144,6 @@ def confeditor_loader():
DEBUG_READ = 0
DEBUG_DECODE = 0
DEBUG_PRESSURE = 0
# map the 5 remote sensors to columns in the database schema
DEFAULT_SENSOR_MAP = {
@@ -350,26 +331,24 @@ class TE923Driver(weewx.drivers.AbstractDevice):
model: Which station model is this?
[Optional. Default is 'TE923']
"""
self._last_rain = None
self._last_rain = None
global DEBUG_READ
DEBUG_READ = int(stn_dict.get('debug_read', 0))
DEBUG_READ = int(stn_dict.get('debug_read', 0))
global DEBUG_DECODE
DEBUG_DECODE = int(stn_dict.get('debug_decode', 0))
global DEBUG_PRESSURE
DEBUG_PRESSURE = int(stn_dict.get('debug_pressure', 0))
DEBUG_DECODE = int(stn_dict.get('debug_decode', 0))
self.model = stn_dict.get('model', 'TE923')
self.max_tries = int(stn_dict.get('max_tries', 5))
self.retry_wait = int(stn_dict.get('retry_wait', 30))
self.polling_interval = int(stn_dict.get('polling_interval', 10))
self.sensor_map = stn_dict.get('sensor_map', DEFAULT_SENSOR_MAP)
self.battery_map = stn_dict.get('battery_map', DEFAULT_BATTERY_MAP)
self.memory_size = stn_dict.get('memory_size', 'small')
self.model = stn_dict.get('model', 'TE923')
self.max_tries = int(stn_dict.get('max_tries', 5))
self.retry_wait = int(stn_dict.get('retry_wait', 3))
self.polling_interval = int(stn_dict.get('polling_interval', 10))
self.sensor_map = stn_dict.get('sensor_map', DEFAULT_SENSOR_MAP)
self.battery_map = stn_dict.get('battery_map', DEFAULT_BATTERY_MAP)
self.memory_size = stn_dict.get('memory_size', 'small')
vendor_id = int(stn_dict.get('vendor_id', '0x1130'), 0)
product_id = int(stn_dict.get('product_id', '0x6801'), 0)
device_id = stn_dict.get('device_id', None)
vendor_id = int(stn_dict.get('vendor_id', '0x1130'), 0)
product_id = int(stn_dict.get('product_id', '0x6801'), 0)
device_id = stn_dict.get('device_id', None)
loginf('driver version is %s' % DRIVER_VERSION)
loginf('polling interval is %s' % str(self.polling_interval))
@@ -385,42 +364,27 @@ class TE923Driver(weewx.drivers.AbstractDevice):
return self.model
def closePort(self):
self.station.close()
self.station = None
if self.station is not None:
self.station.close()
self.station = None
def genLoopPackets(self):
ntries = 0
while ntries < self.max_tries:
ntries += 1
try:
data = self.station.get_readings()
status = self.station.get_status()
packet = data_to_packet(data, status=status,
last_rain=self._last_rain,
sensor_map=self.sensor_map,
battery_map=self.battery_map)
self._last_rain = packet['rainTotal']
ntries = 0
yield packet
time.sleep(self.polling_interval)
except usb.USBError, e:
# FIXME: if 'No such device' we should bail out immediately.
# unfortunately there is no reliable way (?) to get the type
# of USBError. so retry on every type.
logerr("Failed attempt %d of %d to get LOOP data: %s" %
(ntries, self.max_tries, e))
logdbg("Waiting %d seconds before retry" % self.retry_wait)
time.sleep(self.retry_wait)
else:
msg = "Max retries (%d) exceeded for LOOP data" % self.max_tries
logerr(msg)
raise weewx.RetriesExceeded(msg)
while True:
data = self.station.get_readings(self.max_tries, self.retry_wait)
status = self.station.get_status(self.max_tries, self.retry_wait)
packet = data_to_packet(data, status=status,
last_rain=self._last_rain,
sensor_map=self.sensor_map,
battery_map=self.battery_map)
self._last_rain = packet['rainTotal']
yield packet
time.sleep(self.polling_interval)
# def genArchiveRecords(self, since_ts):
# pass
def getConfig(self):
data = self.station.get_status()
data = self.station.get_status(self.max_tries, self.retry_wait)
return data
def data_to_packet(data, status=None, last_rain=None,
@@ -753,6 +717,7 @@ class BadRead(weewx.WeeWxIOError):
class TE923(object):
ENDPOINT_IN = 0x81
READ_LENGTH = 0x8
TIMEOUT = 1000
def __init__(self, vendor_id=0x1130, product_id=0x6801,
dev_id=None, memory_size='small'):
@@ -782,11 +747,12 @@ class TE923(object):
self.devh = dev.open()
if not self.devh:
raise weewx.WeeWxIOError('Open USB device failed')
self.devh.reset()
# be sure kernel does not claim the interface
try:
self.devh.detachKernelDriver(interface)
except Exception:
except (AttributeError, usb.USBError):
pass
# attempt to claim the interface
@@ -811,7 +777,8 @@ class TE923(object):
# properly received.
#
# FIXME: must be a better way to know when there are no more data
def _raw_read(self, addr, timeout=50):
# NB: the timeout matters. values less than 50 result in timeouts.
def _raw_read(self, addr):
reqbuf = [0x05, 0xAF, 0x00, 0x00, 0x00, 0x00, 0xAF, 0xFE]
reqbuf[4] = addr / 0x10000
reqbuf[3] = (addr - (reqbuf[4] * 0x10000)) / 0x100
@@ -822,30 +789,41 @@ class TE923(object):
value=0x0200,
index=0x0000,
buffer=reqbuf,
timeout=timeout)
timeout=self.TIMEOUT)
if ret != 8:
raise BadRead('Unexpected response to data request: %s != 8' % ret)
time.sleep(0.1) # te923tool is 0.3
start_ts = time.time()
rbuf = []
try:
while time.time() - start_ts < 5:
while time.time() - start_ts < 5:
try:
buf = self.devh.interruptRead(self.ENDPOINT_IN,
self.READ_LENGTH, timeout)
self.READ_LENGTH, self.TIMEOUT)
if buf:
nbytes = buf[0]
if nbytes > 7 or nbytes > len(buf)-1:
raise BadRead("Bogus length during read: %d" % nbytes)
rbuf.extend(buf[1:1+nbytes])
else:
if len(rbuf) >= 34:
break
time.sleep(0.009) # te923tool is 0.15
except usb.USBError, e:
logdbg('usb error while reading: %s' % e)
except usb.USBError, e:
# FIXME: if 'No such device' we should bail out immediately.
# unfortunately there is no reliable way (?) to get the type
# of USBError. we often get an exception of "could not detach
# kernel driver from interface 0: No data available" or
# "No error", but this seems to indicate no more data, or a
# usb timing/comm failure, not an actual error condition.
# logdbg('usb error while reading: %s' % e)
pass
time.sleep(0.009) # te923tool is 0.15
else:
raise BadRead("Timeout after %d bytes" % len(rbuf))
if len(rbuf) < 34:
raise BadRead("Not enough bytes: %d < 34" % len(rbuf))
elif len(rbuf) != 34:
loginf("Wrong number of bytes: %d != 34" % len(rbuf))
if rbuf[0] != 0x5a:
raise BadRead("Bad header byte: %02x != %02x" % (rbuf[0], 0x5a))
@@ -856,7 +834,7 @@ class TE923(object):
raise BadRead("Bad crc: %02x != %02x" % (crc, rbuf[33]))
return rbuf
def _read(self, addr, max_tries=100):
def _read(self, addr, max_tries=10, retry_wait=5):
if DEBUG_READ:
logdbg("reading station at address 0x%06x" % addr)
for cnt in range(max_tries):
@@ -865,10 +843,13 @@ class TE923(object):
if DEBUG_READ:
logdbg("BUF " + ' '.join(["%02x" % x for x in buf]))
return buf
except BadRead, e:
logdbg("Bad read (attempt %d of %d): %s" % (cnt+1,max_tries,e))
except (BadRead, usb.USBError), e:
logerr("Failed attempt %d of %d to read data: %s" %
(cnt+1, max_tries, e))
logdbg("Waiting %d seconds before retry" % retry_wait)
time.sleep(retry_wait)
else:
raise BadRead("No data after %d read attempts" % max_tries)
raise weewx.RetriesExceeded("No data after %d tries" % max_tries)
def gen_blocks(self, count=None):
"""generator that returns consecutive blocks of station memory"""
@@ -878,18 +859,18 @@ class TE923(object):
buf = self._read(x)
yield x, buf
def get_status(self):
def get_status(self, max_tries=10, retry_wait=5):
"""get station status"""
status = {}
buf = self._read(0x98)
buf = self._read(0x98, max_tries, retry_wait)
status['barVer'] = buf[1]
status['uvVer'] = buf[2]
status['rccVer'] = buf[3]
status['windVer'] = buf[4]
status['sysVer'] = buf[5]
buf = self._read(0x4c)
buf = self._read(0x4c, max_tries, retry_wait)
status['batteryRain'] = buf[1] & 0x80 == 0x80
status['batteryWind'] = buf[1] & 0x40 == 0x40
status['batteryUV'] = buf[1] & 0x20 == 0x20
@@ -901,9 +882,9 @@ class TE923(object):
return status
def get_readings(self):
def get_readings(self, max_tries=10, retry_wait=5):
"""get sensor readings from the station, return as dictionary"""
buf = self._read(0x020001)
buf = self._read(0x020001, max_tries, retry_wait)
data = decode(buf[1:])
return data

View File

@@ -78,6 +78,10 @@ Changes to the AcuRite driver include:
- Use only 12 bits for inside temperature in acurite driver when decoding
for 02032 stations
Changes to the TE923 driver include:
- consolidated retries
- improved error handling and reporting
3.1.0 02/05/15