mirror of
https://github.com/weewx/weewx.git
synced 2026-04-20 01:26:56 -04:00
fix te923 reading from logger, especially for stations with large memory configuration
This commit is contained in:
@@ -326,12 +326,12 @@ SECTION 13: Unknownn
|
||||
|
||||
SECTION 14: Archiving
|
||||
|
||||
0c0000FB - Unknown
|
||||
0x0000FB - Unknown
|
||||
0x0000FC - Memory size (0 = 0x1fff, 2 = 0x20000)
|
||||
0x0000FD - Number of records (High)
|
||||
0x0000FD - Index of latest record (High)
|
||||
0x0000FE - Archive interval
|
||||
1-11 = 5, 10, 20, 30, 60, 90, 120, 180, 240, 360, 1440 mins
|
||||
0x0000FF - Number of records (Low)
|
||||
0x0000FF - Index of latest record (Low)
|
||||
0x000100 - Checksum of FB:FF
|
||||
|
||||
0x000101 - Start of historical records:
|
||||
@@ -440,9 +440,10 @@ import usb
|
||||
|
||||
import weewx.drivers
|
||||
import weewx.wxformulas
|
||||
from weeutil.weeutil import timestamp_to_string
|
||||
|
||||
DRIVER_NAME = 'TE923'
|
||||
DRIVER_VERSION = '0.17'
|
||||
DRIVER_VERSION = '0.18'
|
||||
|
||||
def loader(config_dict, engine): # @UnusedVariable
|
||||
return TE923Driver(**config_dict[DRIVER_NAME])
|
||||
@@ -453,9 +454,9 @@ def configurator_loader(config_dict): # @UnusedVariable
|
||||
def confeditor_loader():
|
||||
return TE923ConfEditor()
|
||||
|
||||
DEBUG_READ = 1
|
||||
DEBUG_WRITE = 1
|
||||
DEBUG_DECODE = 1
|
||||
DEBUG_READ = 0
|
||||
DEBUG_WRITE = 0
|
||||
DEBUG_DECODE = 0
|
||||
|
||||
# map the station data to the default database schema, plus extensions
|
||||
DEFAULT_OBSERVATION_MAP = {
|
||||
@@ -816,7 +817,7 @@ class TE923Configurator(weewx.drivers.AbstractConfigurator):
|
||||
TE923Configurator.print_data(data, fmt)
|
||||
|
||||
@staticmethod
|
||||
def show_history(station, ts=0, count=0, fmt='dict'):
|
||||
def show_history(station, ts=0, count=None, fmt='dict'):
|
||||
print "Querying the station for historical records..."
|
||||
for r in station.gen_records(ts, count):
|
||||
TE923Configurator.print_data(r, fmt)
|
||||
@@ -1115,6 +1116,8 @@ class TE923Driver(weewx.drivers.AbstractDevice):
|
||||
model: Which station model is this?
|
||||
[Optional. Default is 'TE923']
|
||||
"""
|
||||
loginf('driver version is %s' % DRIVER_VERSION)
|
||||
|
||||
global DEBUG_READ
|
||||
DEBUG_READ = int(stn_dict.get('debug_read', DEBUG_READ))
|
||||
global DEBUG_WRITE
|
||||
@@ -1130,10 +1133,8 @@ class TE923Driver(weewx.drivers.AbstractDevice):
|
||||
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.obs_map = stn_dict.get('map', DEFAULT_OBSERVATION_MAP)
|
||||
|
||||
loginf('driver version is %s' % DRIVER_VERSION)
|
||||
loginf('polling interval is %s' % str(self.polling_interval))
|
||||
self.obs_map = stn_dict.get('map', DEFAULT_OBSERVATION_MAP)
|
||||
loginf('observation map is %s' % self.obs_map)
|
||||
|
||||
self.station = TE923Station(max_tries=self.max_tries,
|
||||
@@ -1426,10 +1427,10 @@ def decode_ws(byte1, byte2):
|
||||
value = bcd2int(byte1) / 10.0 + bcd2int(byte2 & 0x0f) * 10.0 + offset
|
||||
return value, STATE_OK
|
||||
|
||||
# the rain counter is in the station, not the rain bucket. so if the link
|
||||
# between rain bucket and station is lost, the station will miss rainfall and
|
||||
# there is no way to know about it.
|
||||
# FIXME: figure out how to detect link status between station and rain bucket
|
||||
# FIXME: according to sebastian, the counter is in the station, not the rain
|
||||
# bucket. so if the link between rain bucket and station is lost, the
|
||||
# station will miss rainfall and there is no way to know about it.
|
||||
# NB: wview treats the raw rain count as millimeters
|
||||
def decode_rain(buf):
|
||||
"""rain counter is number of bucket tips, each tip is about 0.03 inches"""
|
||||
@@ -1496,6 +1497,8 @@ class TE923Station(object):
|
||||
ENDPOINT_IN = 0x81
|
||||
READ_LENGTH = 0x8
|
||||
TIMEOUT = 1000
|
||||
START_ADDRESS = 0x101
|
||||
RECORD_SIZE = 0x26
|
||||
|
||||
idx_to_interval_sec = {
|
||||
1: 300, 2: 600, 3: 1200, 4: 1800, 5: 3600, 6: 5400, 7: 7200,
|
||||
@@ -1584,7 +1587,7 @@ class TE923Station(object):
|
||||
time.sleep(0.1) # te923tool is 0.3
|
||||
start_ts = time.time()
|
||||
rbuf = []
|
||||
while time.time() - start_ts < 1:
|
||||
while time.time() - start_ts < 3:
|
||||
try:
|
||||
buf = self.devh.interruptRead(
|
||||
self.ENDPOINT_IN, self.READ_LENGTH, self.TIMEOUT)
|
||||
@@ -1729,6 +1732,8 @@ class TE923Station(object):
|
||||
|
||||
def read_memory_size(self):
|
||||
buf = self._read(0xfc)
|
||||
if DEBUG_DECODE:
|
||||
logdbg("MEM BUF[1]=%s" % buf[1])
|
||||
if buf[1] == 0:
|
||||
self._num_rec = 208
|
||||
self._num_blk = 256
|
||||
@@ -1773,24 +1778,42 @@ class TE923Station(object):
|
||||
def get_versions(self):
|
||||
data = dict()
|
||||
buf = self._read(0x98)
|
||||
if DEBUG_DECODE:
|
||||
logdbg("VER BUF[1]=%s BUF[2]=%s BUF[3]=%s BUF[4]=%s BUF[5]=%s" %
|
||||
(buf[1], buf[2], buf[3], buf[4], buf[5]))
|
||||
data['version_bar'] = buf[1]
|
||||
data['version_uv'] = buf[2]
|
||||
data['version_rcc'] = buf[3]
|
||||
data['version_wind'] = buf[4]
|
||||
data['version_sys'] = buf[5]
|
||||
if DEBUG_DECODE:
|
||||
logdbg("VER bar=%s uv=%s rcc=%s wind=%s sys=%s" %
|
||||
(data['version_bar'], data['version_uv'],
|
||||
data['version_rcc'], data['version_wind'],
|
||||
data['version_sys']))
|
||||
return data
|
||||
|
||||
def get_status(self):
|
||||
# map the battery status flags. 0 indicates ok, 1 indicates failure.
|
||||
# FIXME: i get 1 for uv even when no uv link
|
||||
# FIXME: i get 0 for th3, th4, th5 even when no link
|
||||
status = dict()
|
||||
buf = self._read(0x4c)
|
||||
status['bat_rain'] = buf[1] & 0x80 == 0x80
|
||||
status['bat_wind'] = buf[1] & 0x40 == 0x40
|
||||
status['bat_uv'] = buf[1] & 0x20 == 0x20
|
||||
status['bat_5'] = buf[1] & 0x10 == 0x10
|
||||
status['bat_4'] = buf[1] & 0x08 == 0x08
|
||||
status['bat_3'] = buf[1] & 0x04 == 0x04
|
||||
status['bat_2'] = buf[1] & 0x02 == 0x02
|
||||
status['bat_1'] = buf[1] & 0x01 == 0x01
|
||||
if DEBUG_DECODE:
|
||||
logdbg("BAT BUF[01]=%02x" % buf[1])
|
||||
status['bat_rain'] = 0 if buf[1] & 0x80 == 0x80 else 1
|
||||
status['bat_wind'] = 0 if buf[1] & 0x40 == 0x40 else 1
|
||||
status['bat_uv'] = 0 if buf[1] & 0x20 == 0x20 else 1
|
||||
status['bat_5'] = 0 if buf[1] & 0x10 == 0x10 else 1
|
||||
status['bat_4'] = 0 if buf[1] & 0x08 == 0x08 else 1
|
||||
status['bat_3'] = 0 if buf[1] & 0x04 == 0x04 else 1
|
||||
status['bat_2'] = 0 if buf[1] & 0x02 == 0x02 else 1
|
||||
status['bat_1'] = 0 if buf[1] & 0x01 == 0x01 else 1
|
||||
if DEBUG_DECODE:
|
||||
logdbg("BAT rain=%s wind=%s uv=%s th5=%s th4=%s th3=%s th2=%s th1=%s" %
|
||||
(status['bat_rain'], status['bat_wind'], status['bat_uv'],
|
||||
status['bat_5'], status['bat_4'], status['bat_3'],
|
||||
status['bat_2'], status['bat_1']))
|
||||
return status
|
||||
|
||||
# FIXME: is this any different than get_alt?
|
||||
@@ -1828,34 +1851,42 @@ class TE923Station(object):
|
||||
data['dateTime'] = int(time.time() + 0.5)
|
||||
return data
|
||||
|
||||
def gen_records(self, since_ts=0, count=None):
|
||||
"""return requested records from station from oldest to newest. If
|
||||
since_ts is specified, then all records since that time. If count
|
||||
is specified, then at most the count most recent records. If both
|
||||
are specified then at most count records newer than the timestamp."""
|
||||
if not count:
|
||||
count = self._num_rec
|
||||
if count > self._num_rec:
|
||||
count = self._num_rec
|
||||
|
||||
def _get_current_index(self):
|
||||
"""get the index of the current history record"""
|
||||
buf = self._read(0xfb)
|
||||
latest_addr = 0x101 + (buf[3] * 0x100 + buf[5] - 1) * 0x26
|
||||
oldest_addr = latest_addr - count * 0x26
|
||||
if DEBUG_DECODE:
|
||||
logdbg("HIS BUF[3]=%02x BUF[5]=%02x" % (buf[3], buf[5]))
|
||||
record_index = buf[3] * 0x100 + buf[5]
|
||||
if DEBUG_DECODE:
|
||||
logdbg("HIS record_index=%d" % record_index)
|
||||
if record_index > self._num_rec:
|
||||
msg = "record index of %d exceeds memory size of %d" % (
|
||||
record_index, self._num_rec)
|
||||
logerr(msg)
|
||||
raise weewx.WeeWxIOError(msg)
|
||||
return record_index
|
||||
|
||||
n = 0
|
||||
tt = time.localtime(time.time())
|
||||
while n < count and oldest_addr + n * 0x26 < latest_addr:
|
||||
addr = oldest_addr + n * 0x26
|
||||
if addr < 0x101:
|
||||
addr += self._num_rec * 0x26
|
||||
record = self.get_record(addr, tt.tm_year, tt.tm_mon)
|
||||
if record and record['dateTime'] > since_ts:
|
||||
yield record
|
||||
n += 1
|
||||
def _get_addresses(self, requested):
|
||||
"""calculate the oldest and latest addresses"""
|
||||
count = requested
|
||||
if count is None:
|
||||
count = self._num_rec
|
||||
elif count > self._num_rec:
|
||||
count = self._num_rec
|
||||
loginf("too many records requested (%d), using %d instead" %
|
||||
(requested, count))
|
||||
idx = self._get_current_index()
|
||||
latest_addr = self.START_ADDRESS + (idx - 1) * self.RECORD_SIZE
|
||||
oldest_addr = latest_addr - (count - 1) * self.RECORD_SIZE
|
||||
logdbg("count=%s oldest_addr=0x%06x latest_addr=0x%06x" %
|
||||
(count, oldest_addr, latest_addr))
|
||||
return oldest_addr, count
|
||||
|
||||
def get_record(self, addr=None, now_year=None, now_month=None):
|
||||
"""Return a single record from station and the address of the record
|
||||
immediately preceding the single record.
|
||||
def gen_records(self, since_ts=0, requested=None):
|
||||
"""return requested records from station from oldest to newest. If
|
||||
since_ts is specified, then all records since that time. If requested
|
||||
is specified, then at most that many most recent records. If both
|
||||
are specified then at most requested records newer than the timestamp.
|
||||
|
||||
Each historical record is 38 bytes (0x26) long. Records start at
|
||||
memory address 0x101 (257). The index of the record after the latest
|
||||
@@ -1865,9 +1896,62 @@ class TE923Station(object):
|
||||
On small memory stations, the last 32 bytes of memory are never used.
|
||||
On large memory stations, the last 20 bytes of memory are never used.
|
||||
"""
|
||||
|
||||
logdbg("gen_records: since_ts=%s requested=%s" % (since_ts, requested))
|
||||
if since_ts is None:
|
||||
since_ts = 0
|
||||
start_ts = time.time()
|
||||
tt = time.localtime(start_ts)
|
||||
oldest_addr, count = self._get_addresses(requested)
|
||||
more_records = True
|
||||
while more_records:
|
||||
n = 0
|
||||
while n < count:
|
||||
addr = oldest_addr + n * self.RECORD_SIZE
|
||||
if addr < self.START_ADDRESS:
|
||||
addr += self._num_rec * self.RECORD_SIZE
|
||||
record = self.get_record(addr, tt.tm_year, tt.tm_mon)
|
||||
n += 1
|
||||
msg = "record %d of %d addr=0x%06x" % (n, count, addr)
|
||||
if record and record['dateTime'] > since_ts:
|
||||
msg += " %s" % timestamp_to_string(record['dateTime'])
|
||||
logdbg("gen_records: yield %s" % msg)
|
||||
yield record
|
||||
else:
|
||||
if record:
|
||||
msg += " since_ts=%d %s" % (
|
||||
since_ts, timestamp_to_string(record['dateTime']))
|
||||
logdbg("gen_records: skip %s" % msg)
|
||||
# use the sleep to simulate slow reads
|
||||
# time.sleep(5)
|
||||
|
||||
# see if reading has taken so much time that more records have
|
||||
# arrived, but only if no specific number of records was specified.
|
||||
# if so, read whatever records have come in since the read began.
|
||||
if requested is None:
|
||||
arcint = self.get_interval_seconds()
|
||||
now = time.time()
|
||||
if now - start_ts > arcint:
|
||||
newreq = int((now - start_ts) / arcint) + 1
|
||||
logdbg("gen_records: reading %d more records" % newreq)
|
||||
oldest_addr, count = self._get_addresses(newreq)
|
||||
start_ts = now
|
||||
else:
|
||||
more_records = False
|
||||
else:
|
||||
more_records = False
|
||||
|
||||
def get_record(self, addr, now_year=None, now_month=None):
|
||||
"""Return a single record from station."""
|
||||
|
||||
logdbg("get_record at address 0x%06x (year=%s month=%s)" %
|
||||
(addr, now_year, now_month))
|
||||
buf = self._read(addr)
|
||||
if DEBUG_DECODE:
|
||||
logdbg("REC %02x %02x %02x %02x" %
|
||||
(buf[1], buf[2], buf[3], buf[4]))
|
||||
if buf[1] == 0xff:
|
||||
# no data at this address
|
||||
logdbg("get_record: no data at address 0x%06x" % addr)
|
||||
return None
|
||||
|
||||
if now_year is None or now_month is None:
|
||||
@@ -1885,21 +1969,16 @@ class TE923Station(object):
|
||||
minute = bcd2int(buf[4])
|
||||
ts = time.mktime((year, month, day, hour, minute, 0, 0, 0, -1))
|
||||
if DEBUG_DECODE:
|
||||
logdbg("REC %02x %02x %02x %02x" %
|
||||
(buf[1], buf[2], buf[3], buf[4]))
|
||||
logdbg("REC %d/%02d/%02d %02d:%02d = %d" %
|
||||
(year, month, day, hour, minute, ts))
|
||||
|
||||
tmpbuf = buf[5:16]
|
||||
crc1 = buf[16]
|
||||
buf = self._read(addr + 0x10)
|
||||
tmpbuf.extend(buf[1:22])
|
||||
crc2 = buf[22]
|
||||
if DEBUG_DECODE:
|
||||
logdbg("CRC %02x %02x" % (crc1, crc2))
|
||||
|
||||
data = decode(tmpbuf)
|
||||
data['dateTime'] = int(ts)
|
||||
logdbg("get_record: found record %s" % data)
|
||||
return data
|
||||
|
||||
def _read_minmax(self):
|
||||
@@ -1975,17 +2054,13 @@ class TE923Station(object):
|
||||
self._write_date(buf)
|
||||
|
||||
def _read_loc(self, loc_type):
|
||||
if loc_type == 0:
|
||||
buf = self._read(0x0)
|
||||
else:
|
||||
buf = self._read(0x16)
|
||||
addr = 0x0 if loc_type == 0 else 0x16
|
||||
buf = self._read(addr)
|
||||
return buf[1:33]
|
||||
|
||||
def _write_loc(self, loc_type, buf):
|
||||
if loc_type == 0:
|
||||
self._write(0x00, buf)
|
||||
else:
|
||||
self._write(0x16, buf)
|
||||
addr = 0x0 if loc_type == 0 else 0x16
|
||||
self._write(addr, buf)
|
||||
|
||||
def get_loc(self, loc_type):
|
||||
buf = self._read_loc(loc_type)
|
||||
|
||||
@@ -50,6 +50,9 @@ The vantage and ws23xx drivers now include the fix for the policy of
|
||||
"wind direction is undefined when no wind speed". This was applied to other
|
||||
drivers in weewx 3.3.0.
|
||||
|
||||
Fixed te923 driver behavior when reading from logger, especially on stations
|
||||
with large memory configuration. Thanks to users Joep and Nico.
|
||||
|
||||
|
||||
3.5.0 03/13/2016
|
||||
|
||||
|
||||
Reference in New Issue
Block a user