diff options
author | Daniel_M_Williams <dwilliams@sea-machines.com> | 2018-05-18 15:18:29 -0700 |
---|---|---|
committer | Gary E. Miller <gem@rellim.com> | 2018-05-18 15:18:29 -0700 |
commit | 9b27b45ab5244db738bd65f86463528384e337d2 (patch) | |
tree | 47c0d4ad0daca7bf30f6fa05fb0edec10637e43e /gps | |
parent | 9429b1a5498e0e1ea68d5e53245c897dda52e5aa (diff) | |
download | gpsd-9b27b45ab5244db738bd65f86463528384e337d2.tar.gz |
re-implement reconnect code in python-client-library
Signed-off-by: Gary E. Miller <gem@rellim.com>
Diffstat (limited to 'gps')
-rw-r--r-- | gps/client.py | 114 | ||||
-rwxr-xr-x | gps/gps.py | 51 |
2 files changed, 96 insertions, 69 deletions
diff --git a/gps/client.py b/gps/client.py index c660d920..2f1b5201 100644 --- a/gps/client.py +++ b/gps/client.py @@ -12,6 +12,7 @@ import sys import time from .misc import polystr, polybytes +from .watch_options import * GPSD_PORT = "2947" @@ -19,12 +20,18 @@ GPSD_PORT = "2947" class gpscommon(object): "Isolate socket handling and buffering from the protocol interpretation." - def __init__(self, host="127.0.0.1", port=GPSD_PORT, verbose=0): + def __init__(self, host="127.0.0.1", port=GPSD_PORT, verbose=0, + should_reconnect=False): self.sock = None # in case we blow up in connect self.linebuffer = b'' + self.received = time.time() self.verbose = verbose + self.reconnect = should_reconnect + if host is not None: - self.connect(host, port) + self.host = host + if port is not None: + self.port = port def connect(self, host, port): """Connect to a host on a given port. @@ -51,14 +58,21 @@ class gpscommon(object): self.sock = socket.socket(af, socktype, proto) # if self.debuglevel > 0: print 'connect:', (host, port) self.sock.connect(sa) + if self.verbose > 0: + print('connected to tcp://{}:{}'.format(host, port)) + break + except ConnectionRefusedError as cre: + if self.verbose > 1: + msg = str(cre) + ' (to {}:{})'.format(host, port) + sys.stderr.write("error: {}\n".format(msg.strip())) + self.close() + return # relatively routine error except socket.error as e: - msg = str(e) - # if self.debuglevel > 0: print 'connect fail:', (host, port) + if self.verbose > 1: + msg = str(e) + ' (to {}:{})'.format(host, port) + sys.stderr.write("error: {}\n".format(msg.strip())) self.close() - continue - break - if not self.sock: - raise socket.error(msg) + raise # propogate error to caller def close(self): if self.sock: @@ -78,24 +92,30 @@ class gpscommon(object): def read(self): "Wait for and read data being streamed from the daemon." - if self.verbose > 1: - sys.stderr.write("poll: reading from daemon...\n") + + if None is self.sock: + self.connect(self.host, self.port) + if None is self.sock: + return -1 + self.stream() + eol = self.linebuffer.find(b'\n') if eol == -1: # RTCM3 JSON can be over 4.4k long, so go big frag = self.sock.recv(8192) + self.linebuffer += frag - if self.verbose > 1: - sys.stderr.write("poll: read complete.\n") if not self.linebuffer: if self.verbose > 1: - sys.stderr.write("poll: returning -1.\n") + sys.stderr.write( + "poll: no available data: returning -1.\n") # Read failed return -1 + eol = self.linebuffer.find(b'\n') if eol == -1: if self.verbose > 1: - sys.stderr.write("poll: returning 0.\n") + sys.stderr.write("poll: partial message: returning 0.\n") # Read succeeded, but only got a fragment self.response = '' # Don't duplicate last response return 0 @@ -113,7 +133,7 @@ class gpscommon(object): # Can happen if daemon terminates while we're reading. if not self.response: return -1 - if self.verbose: + if 1 < self.verbose: sys.stderr.write("poll: data is %s\n" % repr(self.response)) self.received = time.time() # We got a \n-terminated line @@ -132,19 +152,8 @@ class gpscommon(object): "Ship commands to the daemon." if not commands.endswith("\n"): commands += "\n" - self.sock.send(polybytes(commands)) - -WATCH_ENABLE = 0x000001 # enable streaming -WATCH_DISABLE = 0x000002 # disable watching -WATCH_JSON = 0x000010 # JSON output -WATCH_NMEA = 0x000020 # output in NMEA -WATCH_RARE = 0x000040 # output of packets in hex -WATCH_RAW = 0x000080 # output of raw packets -WATCH_SCALED = 0x000100 # scale output to floats -WATCH_TIMING = 0x000200 # timing information -WATCH_SPLIT24 = 0x001000 # split AIS Type 24s -WATCH_PPS = 0x002000 # enable PPS in raw/NMEA -WATCH_DEVICE = 0x000800 # watch specific device + if None is not self.sock: + self.sock.send(polybytes(commands)) class json_error(BaseException): @@ -173,6 +182,46 @@ class gpsjson(object): def stream(self, flags=0, devpath=None): "Control streaming reports from the daemon," + + if 0 < flags: + self.stream_command = self.generate_stream_command(flags, devpath) + + if self.stream_command: + if self.verbose > 1: + sys.stderr.write( + "send: stream as: {}\n".format(self.stream_command)) + self.send(self.stream_command) + else: + raise TypeError("Could not request a stream: " + "Invalid streaming command!!") + + def generate_stream_command(self, flags=0, devpath=None): + if flags & WATCH_OLDSTYLE: + return self.generate_stream_command_old_style(flags) + else: + return self.generate_stream_command_new_style(flags, devpath) + + @staticmethod + def generate_stream_command_old_style(flags=0): + if flags & WATCH_DISABLE: + arg = "w-" + if flags & WATCH_NMEA: + arg += 'r-' + + elif flags & WATCH_ENABLE: + arg = 'w+' + if flags & WATCH_NMEA: + arg += 'r+' + + return arg + + @staticmethod + def generate_stream_command_new_style(flags=0, devpath=None): + + if (flags & (WATCH_JSON | WATCH_OLDSTYLE | WATCH_NMEA | + WATCH_RAW)) == 0: + flags |= WATCH_JSON + if flags & WATCH_DISABLE: arg = '?WATCH={"enable":false' if flags & WATCH_JSON: @@ -211,7 +260,8 @@ class gpsjson(object): arg += ',"pps":true' if flags & WATCH_DEVICE: arg += ',"device":"%s"' % devpath - return self.send(arg + "}") + arg += "}" + return arg class dictwrapper(object): @@ -230,6 +280,9 @@ class dictwrapper(object): "Emulate dictionary, for new-style interface." return self.__dict__[key] + def __iter__(self): + return self.__dict__.__iter__() + def __setitem__(self, key, val): "Emulate dictionary, for new-style interface." self.__dict__[key] = val @@ -241,6 +294,9 @@ class dictwrapper(object): return "<dictwrapper: " + str(self.__dict__) + ">" __repr__ = __str__ + def __len__(self): + return len(self.__dict__) + # # Someday a cleaner Python interface using this machinery will live here # @@ -19,14 +19,15 @@ # Preserve this property! from __future__ import absolute_import, print_function, division +# since Python 2.6 +from math import isnan + from .client import * +from .watch_options import * NaN = float('nan') -def isnan(x): - return str(x) == 'nan' - # Don't hand-hack this list, it's generated. ONLINE_SET = (1 << 1) TIME_SET = (1 << 2) @@ -62,8 +63,8 @@ ERROR_SET = (1 << 31) TIMEDRIFT_SET = (1 << 32) EOF_SET = (1 << 33) SET_HIGH_BIT = 34 -UNION_SET = (RTCM2_SET | RTCM3_SET | SUBFRAME_SET | AIS_SET | VERSION_SET - | DEVICELIST_SET | ERROR_SET | GST_SET) +UNION_SET = (RTCM2_SET | RTCM3_SET | SUBFRAME_SET | AIS_SET | VERSION_SET | + DEVICELIST_SET | ERROR_SET | GST_SET) STATUS_NO_FIX = 0 STATUS_FIX = 1 STATUS_DGPS_FIX = 2 @@ -73,20 +74,6 @@ MODE_3D = 3 MAXCHANNELS = 72 # Copied from gps.h, but not required to match SIGNAL_STRENGTH_UNKNOWN = NaN -WATCH_ENABLE = 0x000001 # enable streaming -WATCH_DISABLE = 0x000002 # disable watching -WATCH_JSON = 0x000010 # JSON output -WATCH_NMEA = 0x000020 # output in NMEA -WATCH_RARE = 0x000040 # output of packets in hex -WATCH_RAW = 0x000080 # output of raw packets -WATCH_SCALED = 0x000100 # scale output to floats -WATCH_TIMING = 0x000200 # timing information -WATCH_DEVICE = 0x000800 # watch specific device -WATCH_SPLIT24 = 0x001000 # split AIS Type 24s -WATCH_PPS = 0x002000 # enable PPS JSON -WATCH_NEWSTYLE = 0x010000 # force JSON streaming -WATCH_OLDSTYLE = 0x020000 # force old-style streaming - class gpsfix(object): def __init__(self): @@ -182,8 +169,9 @@ class gpsdata(object): class gps(gpscommon, gpsdata, gpsjson): "Client interface to a running gpsd instance." - def __init__(self, host="127.0.0.1", port=GPSD_PORT, verbose=0, mode=0): - gpscommon.__init__(self, host, port, verbose) + def __init__(self, host="127.0.0.1", port=GPSD_PORT, verbose=0, mode=0, + reconnect=False): + gpscommon.__init__(self, host, port, verbose, reconnect) gpsdata.__init__(self) if mode: self.stream(mode) @@ -288,25 +276,8 @@ class gps(gpscommon, gpsdata, gpsjson): def stream(self, flags=0, devpath=None): "Ask gpsd to stream reports at your client." - if (flags & (WATCH_JSON | WATCH_OLDSTYLE | WATCH_NMEA - | WATCH_RAW)) == 0: - flags |= WATCH_JSON - if flags & WATCH_DISABLE: - if flags & WATCH_OLDSTYLE: - arg = "w-" - if flags & WATCH_NMEA: - arg += 'r-' - return self.send(arg) - else: - gpsjson.stream(self, flags, devpath) - else: # flags & WATCH_ENABLE: - if flags & WATCH_OLDSTYLE: - arg = 'w+' - if flags & WATCH_NMEA: - arg += 'r+' - return self.send(arg) - else: - gpsjson.stream(self, flags, devpath) + + gpsjson.stream(self, flags, devpath) def is_sbas(prn): |