Package killerbee :: Package zbwardrive :: Package gps :: Module client
[hide private]
[frames] | no frames]

Source Code for Module killerbee.zbwardrive.gps.client

  1  # This file is Copyright (c) 2010 by the GPSD project 
  2  # BSD terms apply: see the file COPYING in the distribution root for details. 
  3  # 
  4  import time, socket, sys, select 
  5   
  6  if sys.hexversion >= 0x2060000: 
  7      import json                 # For Python 2.6 
  8  else: 
  9      import simplejson as json   # For Python 2.4 and 2.5 
 10   
 11  GPSD_PORT="2947" 
 12   
13 -class gpscommon:
14 "Isolate socket handling and buffering from the protcol interpretation."
15 - def __init__(self, host="127.0.0.1", port=GPSD_PORT, verbose=0):
16 self.sock = None # in case we blow up in connect 17 self.linebuffer = "" 18 self.verbose = verbose 19 self.connect(host, port)
20
21 - def connect(self, host, port):
22 """Connect to a host on a given port. 23 24 If the hostname ends with a colon (`:') followed by a number, and 25 there is no port specified, that suffix will be stripped off and the 26 number interpreted as the port number to use. 27 """ 28 if not port and (host.find(':') == host.rfind(':')): 29 i = host.rfind(':') 30 if i >= 0: 31 host, port = host[:i], host[i+1:] 32 try: port = int(port) 33 except ValueError: 34 raise socket.error, "nonnumeric port" 35 #if self.verbose > 0: 36 # print 'connect:', (host, port) 37 msg = "getaddrinfo returns an empty list" 38 self.sock = None 39 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): 40 af, socktype, proto, canonname, sa = res 41 try: 42 self.sock = socket.socket(af, socktype, proto) 43 #if self.debuglevel > 0: print 'connect:', (host, port) 44 self.sock.connect(sa) 45 except socket.error, msg: 46 #if self.debuglevel > 0: print 'connect fail:', (host, port) 47 self.close() 48 continue 49 break 50 if not self.sock: 51 raise socket.error, msg
52
53 - def close(self):
54 if self.sock: 55 self.sock.close() 56 self.sock = None
57
58 - def __del__(self):
59 self.close()
60
61 - def waiting(self):
62 "Return True if data is ready for the client." 63 if self.linebuffer: 64 return True 65 (winput, woutput, wexceptions) = select.select((self.sock,), (), (), 0) 66 return winput != []
67
68 - def read(self):
69 "Wait for and read data being streamed from the daemon." 70 if self.verbose > 1: 71 sys.stderr.write("poll: reading from daemon...\n") 72 eol = self.linebuffer.find('\n') 73 if eol == -1: 74 frag = self.sock.recv(4096) 75 self.linebuffer += frag 76 if self.verbose > 1: 77 sys.stderr.write("poll: read complete.\n") 78 if not self.linebuffer: 79 if self.verbose > 1: 80 sys.stderr.write("poll: returning -1.\n") 81 # Read failed 82 return -1 83 eol = self.linebuffer.find('\n') 84 if eol == -1: 85 if self.verbose > 1: 86 sys.stderr.write("poll: returning 0.\n") 87 # Read succeeded, but only got a fragment 88 return 0 89 else: 90 if self.verbose > 1: 91 sys.stderr.write("poll: fetching from buffer.\n") 92 93 # We got a line 94 eol += 1 95 self.response = self.linebuffer[:eol] 96 self.linebuffer = self.linebuffer[eol:] 97 98 # Can happen if daemon terminates while we're reading. 99 if not self.response: 100 return -1 101 if self.verbose: 102 sys.stderr.write("poll: data is %s\n" % repr(self.response)) 103 self.received = time.time() 104 # We got a \n-terminated line 105 return len(self.response)
106
107 - def send(self, commands):
108 "Ship commands to the daemon." 109 if not commands.endswith("\n"): 110 commands += "\n" 111 self.sock.send(commands)
112 113 WATCH_DISABLE = 0x0000 114 WATCH_ENABLE = 0x0001 115 WATCH_JSON = 0x0002 116 WATCH_NMEA = 0x0004 117 WATCH_RARE = 0x0008 118 WATCH_RAW = 0x0010 119 WATCH_SCALED = 0x0020 120 WATCH_DEVICE = 0x0040 121
122 -class gpsjson(gpscommon):
123 "Basic JSON decoding."
124 - def __iter__(self):
125 return self
126
127 - def json_unpack(self, buf):
128 def asciify(d): 129 "De-Unicodify everything so we can copy dicts into Python objects." 130 t = {} 131 for (k, v) in d.items(): 132 ka = k.encode("ascii") 133 if type(v) == type(u"x"): 134 va = v.encode("ascii") 135 elif type(v) == type({}): 136 va = asciify(v) 137 elif type(v) == type([]): 138 va = map(asciify, v) 139 else: 140 va = v 141 t[ka] = va 142 return t
143 self.data = dictwrapper(**asciify(json.loads(buf.strip(), encoding="ascii"))) 144 # Should be done for any other array-valued subobjects, too. 145 if self.data["class"] == "SKY" and hasattr(self.data, "satellites"): 146 self.data.satellites = map(lambda x: dictwrapper(**x), self.data.satellites)
147
148 - def stream(self, flags=0, outfile=None):
149 "Control streaming reports from the daemon," 150 if flags & WATCH_DISABLE: 151 arg = '?WATCH={"enable":false' 152 if flags & WATCH_JSON: 153 arg += ',"json":false' 154 if flags & WATCH_NMEA: 155 arg += ',"nmea":false' 156 if flags & WATCH_RARE: 157 arg += ',"raw":1' 158 if flags & WATCH_RAW: 159 arg += ',"raw":2' 160 if flags & WATCH_SCALED: 161 arg += ',"scaled":false' 162 else: # flags & WATCH_ENABLE: 163 arg = '?WATCH={"enable":true' 164 if flags & WATCH_JSON: 165 arg += ',"json":true' 166 if flags & WATCH_NMEA: 167 arg += ',"nmea":true' 168 if flags & WATCH_RAW: 169 arg += ',"raw":1' 170 if flags & WATCH_RARE: 171 arg += ',"raw":0' 172 if flags & WATCH_SCALED: 173 arg += ',"scaled":true' 174 if flags & WATCH_DEVICE: 175 arg += ',"device":"%s"' % outfile 176 return self.send(arg + "}")
177
178 -class dictwrapper:
179 "Wrapper that yields both class and dictionary behavior,"
180 - def __init__(self, **ddict):
181 self.__dict__ = ddict
182 - def get(self, k, d=None):
183 return self.__dict__.get(k, d)
184 - def keys(self):
185 return self.__dict__.keys()
186 - def __getitem__(self, key):
187 "Emulate dictionary, for new-style interface." 188 return self.__dict__[key]
189 - def __setitem__(self, key, val):
190 "Emulate dictionary, for new-style interface." 191 self.__dict__[key] = val
192 - def __contains__(self, key):
193 return key in self.__dict__
194 - def __str__(self):
195 return "<dictwrapper: " + str(self.__dict__) + ">"
196 __repr__ = __str__
197 198 # 199 # Someday a cleaner Python iterface using this machiner will live here 200 # 201 202 # End 203