From ba6cf9e875f28165fefd447d078771c989289eee Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Sat, 8 Apr 2017 15:10:56 -0400 Subject: call Disconnect before re-connecting dexcom --- dexcom_reader/dexcom_dumper.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dexcom_reader/dexcom_dumper.py b/dexcom_reader/dexcom_dumper.py index 18724ac..be5b0d8 100755 --- a/dexcom_reader/dexcom_dumper.py +++ b/dexcom_reader/dexcom_dumper.py @@ -158,6 +158,8 @@ def print_cgm_bg(now, r): def dexcom_reconnect(): global dr + if dr: + dr.Disconnect() dr = get_dexcom_reader() def poll(): -- cgit v1.2.3 From 572aad546bb8ee9ccd095a2098e1b292176c53da Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Sat, 8 Apr 2017 16:16:51 -0400 Subject: re-implement polling using "devicer" library --- dexcom_reader/devicer.py | 72 ++++++++++++++++++++++++++++++++++++++++++ dexcom_reader/dexcom_dumper.py | 61 ++++++++++++++++++++++++++++++++++- 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 dexcom_reader/devicer.py diff --git a/dexcom_reader/devicer.py b/dexcom_reader/devicer.py new file mode 100644 index 0000000..79a0a72 --- /dev/null +++ b/dexcom_reader/devicer.py @@ -0,0 +1,72 @@ +import pyudev +import threading + +class DevicePoller: + def __init__(self, dev): + self.device_added = threading.Event() + self.device_removed = threading.Event() + self.device = dev + self.wait_add = self.device_added.wait + self.wait_remove = self.device_removed.wait + self.have = self.device_added.is_set + + if dev: + self.device_added.set() + + def launch_observer(self, monitor, device_match): + + if not monitor: + monitor = pyudev.Monitor.from_netlink(pyudev.Context()) + + def handle_event(action, device): + if action == 'add' and device_match(device): + self.device = device + self.device_removed.clear() + self.device_added.set() + elif action == 'remove' and device == self.device: + self.device = None + self.device_added.clear() + self.device_removed.set() + + self.observer = pyudev.MonitorObserver(monitor, handle_event) + self.observer.start() + + def stop_observer(): + self.observer.stop() + +def simple_devicer(subsystem, predicate): + dev = None + context = pyudev.Context() + for try_dev in context.list_devices(subsystem=subsystem): + if predicate(try_dev): + dev = try_dev + break + + monitor = pyudev.Monitor.from_netlink(context) + monitor.filter_by(subsystem) + + devicer = DevicePoller(dev) + devicer.launch_observer(monitor, predicate) + + return devicer + +if __name__ == '__main__': + + dexcom_g5_product = '22a3/47/100' + + def is_dexcom_g5(dev): + try: return dev.parent.get('PRODUCT') == dexcom_g5_product + except: return False + + devicer = simple_devicer('tty', is_dexcom_g5) + + while True: + print("waiting for device") + devicer.wait_add() + while devicer.have(): + print("poll device {0}".format(devicer.device)) + print("sleep(5)") + if devicer.wait_remove(5): + print("device removed") + + devicer.stop_observer() diff --git a/dexcom_reader/dexcom_dumper.py b/dexcom_reader/dexcom_dumper.py index be5b0d8..e7db00e 100755 --- a/dexcom_reader/dexcom_dumper.py +++ b/dexcom_reader/dexcom_dumper.py @@ -4,6 +4,7 @@ import readdata import sys import json import traceback +from devicer import simple_devicer # import requests # As this takes SEVEN SECONDS, it's delayed until needed from sys import stdout, stderr from datetime import timedelta, datetime @@ -206,6 +207,64 @@ def poll_remote(): else: sleep_verbose(10) +def poll_remote_redux(): + (n, r) = remote_update('EGV_DATA') + if n is None: + return 10 + else: + now = dr.ReadSystemTime() + if n == 0: + send_ping(now) + if r: + for t in ['METER_DATA', 'INSERTION_TIME', 'USER_EVENT_DATA']: + # TODO: track how long this takes & adjust sleep accordingly + try: + remote_update(t) + except: + traceback.print_exc() + next_reading = (r.system_time - now + timedelta(minutes=5, seconds=2)).total_seconds() + return max(10, next_reading) + else: + return 10 + +def poll_redux(): + poll = poll_remote_redux + + dexcom_g5_product = '22a3/47/100' + + def is_dexcom_g5(dev): + try: return dev.parent.get('PRODUCT') == dexcom_g5_product + except: return False + + devicer = simple_devicer('tty', is_dexcom_g5) + + while True: + try: + print_verbose("Waiting for device.") + if not devicer.wait_add(600): + continue + print_verbose("Device found.") + + global dr + if dr: + dr.Disconnect() + dr = readdata.DexcomG5(devicer.device.device_node) + + print("Device opened.") + while devicer.have(): + print_verbose("Polling device.") + try: + sleeptime = poll() + except: + sleeptime = 10 + + if devicer.wait_remove(sleeptime): + print_verbose("Device unplugged.") + dr.Disconnect() + dr = None + except KeyboardInterrupt: + break + def poll_stdout(): (r, now) = read_recent_egv_data() connected(True) @@ -343,5 +402,5 @@ def toJSON(o): {"dump_everything": dump_everything, "dump_cgm": dump_cgm, "poll": poll, - "test": test, + "test": poll_redux, }[command]() -- cgit v1.2.3 From 17984a16a763dee4b1f20f3383b2f39721cfeb95 Mon Sep 17 00:00:00 2001 From: Andrew Cady Date: Sat, 8 Apr 2017 16:31:27 -0400 Subject: repeal & replace the old polling code --- dexcom_reader/dexcom_dumper.py | 75 +++++++++--------------------------------- 1 file changed, 16 insertions(+), 59 deletions(-) diff --git a/dexcom_reader/dexcom_dumper.py b/dexcom_reader/dexcom_dumper.py index e7db00e..78e9841 100755 --- a/dexcom_reader/dexcom_dumper.py +++ b/dexcom_reader/dexcom_dumper.py @@ -53,7 +53,10 @@ def get_dexcom_reader(): dd = readdata.Dexcom.FindDevice() return readdata.Dexcom(dd) -dr = get_dexcom_reader() +dr = None + +if command is not 'poll': + dr = get_dexcom_reader() def parseable_record_types(): unparseable = ['FIRMWARE_PARAMETER_DATA', 'RECEIVER_LOG_DATA', 'USER_SETTING_DATA', 'MAX_VALUE'] @@ -157,57 +160,7 @@ def print_cgm_bg(now, r): print '%s: %s %s' % (format_times(now, r.system_time), r.glucose, r.trend_arrow) stdout.flush() -def dexcom_reconnect(): - global dr - if dr: - dr.Disconnect() - dr = get_dexcom_reader() - -def poll(): - print_verbose('Started dexcom_dumper.') - while True: - try: - poll_remote() if HOST else poll_stdout() - - except constants.SerialPortError: - connected(False) - dexcom_reconnect() - sleep_verbose(10) - except KeyboardInterrupt: - print_verbose('Exiting.') - return - except: - # Check for requests.ConnectionError without necessarily importing requests - if (str(sys.exc_info()[0]) == ""): - print_verbose('Error: could not connect to remote host.') - else: - print_verbose('Exception: %s' % str(sys.exc_info()[0])) - traceback.print_exc() - dexcom_reconnect() - sleep_verbose(10) - def poll_remote(): - (n, r) = remote_update('EGV_DATA') - connected(True) - if n is None: - sleep_verbose(10) - else: - now = dr.ReadSystemTime() - if n == 0: - send_ping(now) - if r: - for t in ['METER_DATA', 'INSERTION_TIME', 'USER_EVENT_DATA']: - # TODO: track how long this takes & adjust sleep accordingly - try: - remote_update(t) - except: - traceback.print_exc() - next_reading = (r.system_time - now + timedelta(minutes=5, seconds=2)).total_seconds() - sleep_verbose(max(10, next_reading)) - else: - sleep_verbose(10) - -def poll_remote_redux(): (n, r) = remote_update('EGV_DATA') if n is None: return 10 @@ -227,8 +180,9 @@ def poll_remote_redux(): else: return 10 -def poll_redux(): - poll = poll_remote_redux +def poll(): + print_verbose('Started dexcom_dumper.') + poll_once = poll_remote if HOST else poll_stdout dexcom_g5_product = '22a3/47/100' @@ -242,6 +196,10 @@ def poll_redux(): try: print_verbose("Waiting for device.") if not devicer.wait_add(600): + # This does not really need to time out, but if there + # is no timeout, then keyboard interrupt does not work. + # Might as well use the opportunity to print more + # garbage into the log, though. continue print_verbose("Device found.") @@ -254,7 +212,7 @@ def poll_redux(): while devicer.have(): print_verbose("Polling device.") try: - sleeptime = poll() + sleeptime = poll_once() except: sleeptime = 10 @@ -267,14 +225,13 @@ def poll_redux(): def poll_stdout(): (r, now) = read_recent_egv_data() - connected(True) if r is None: - sleep_verbose(10) + return 10 else: print_cgm_bg(now, r) next_reading = (r.system_time - now + timedelta(minutes=5, seconds=2)).total_seconds() - if (next_reading > 0): - sleep_verbose(next_reading) + return next_reading if (next_reading > 0) else 10 + def since(when, rectype): filt = lambda r: (r.system_time > when) if when else True @@ -402,5 +359,5 @@ def toJSON(o): {"dump_everything": dump_everything, "dump_cgm": dump_cgm, "poll": poll, - "test": poll_redux, + "test": poll, }[command]() -- cgit v1.2.3