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.
4 import time, socket, sys, select
6 if sys.hexversion >= 0x2060000:
7 import json # For Python 2.6
9 import simplejson as json # For Python 2.4 and 2.5
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
18 self.verbose = verbose
19 self.connect(host, port)
21 def connect(self, host, port):
22 """Connect to a host on a given port.
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.
28 if not port and (host.find(':') == host.rfind(':')):
31 host, port = host[:i], host[i+1:]
34 raise socket.error, "nonnumeric port"
36 # print 'connect:', (host, port)
37 msg = "getaddrinfo returns an empty list"
39 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
40 af, socktype, proto, canonname, sa = res
42 self.sock = socket.socket(af, socktype, proto)
43 #if self.debuglevel > 0: print 'connect:', (host, port)
45 except socket.error, msg:
46 #if self.debuglevel > 0: print 'connect fail:', (host, port)
51 raise socket.error, msg
62 "Return True if data is ready for the client."
65 (winput, woutput, wexceptions) = select.select((self.sock,), (), (), 0)
69 "Wait for and read data being streamed from the daemon."
71 sys.stderr.write("poll: reading from daemon...\n")
72 eol = self.linebuffer.find('\n')
74 frag = self.sock.recv(4096)
75 self.linebuffer += frag
77 sys.stderr.write("poll: read complete.\n")
78 if not self.linebuffer:
80 sys.stderr.write("poll: returning -1.\n")
83 eol = self.linebuffer.find('\n')
86 sys.stderr.write("poll: returning 0.\n")
87 # Read succeeded, but only got a fragment
91 sys.stderr.write("poll: fetching from buffer.\n")
95 self.response = self.linebuffer[:eol]
96 self.linebuffer = self.linebuffer[eol:]
98 # Can happen if daemon terminates while we're reading.
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)
107 def send(self, commands):
108 "Ship commands to the daemon."
109 if not commands.endswith("\n"):
111 self.sock.send(commands)
113 WATCH_DISABLE = 0x0000
114 WATCH_ENABLE = 0x0001
119 WATCH_SCALED = 0x0020
120 WATCH_DEVICE = 0x0040
122 class gpsjson(gpscommon):
123 "Basic JSON decoding."
127 def json_unpack(self, buf):
129 "De-Unicodify everything so we can copy dicts into Python objects."
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({}):
137 elif type(v) == type([]):
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)
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:
158 if flags & WATCH_RAW:
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:
170 if flags & WATCH_RARE:
172 if flags & WATCH_SCALED:
173 arg += ',"scaled":true'
174 if flags & WATCH_DEVICE:
175 arg += ',"device":"%s"' % outfile
176 return self.send(arg + "}")
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)
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__
195 return "<dictwrapper: " + str(self.__dict__) + ">"
199 # Someday a cleaner Python iterface using this machiner will live here