Skip to content

Commit ed835a4

Browse files
committed
Merge pull request swift-nav#37 from mookerji/table-and-log
Moves JSON and pickle iterators into libsbp.
2 parents a187f0e + 7038d98 commit ed835a4

15 files changed

+3031
-31
lines changed
Binary file not shown.

python/data/serial_link_log_20150310-115522-test.log.dat

+2,650
Large diffs are not rendered by default.

python/sbp/__init__.py

100644100755
+9-7
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151

5252
def crc16(s, crc=0):
5353
"""CRC16 implementation acording to CCITT standards.
54-
54+
5555
"""
5656
for ch in s:
5757
crc = ((crc<<8)&0xFFFF) ^ crc16_tab[ ((crc>>8)&0xFF) ^ (ord(ch)&0xFF) ]
@@ -60,9 +60,9 @@ def crc16(s, crc=0):
6060

6161
class SBP(object):
6262
"""Swift Binary Protocol container.
63-
63+
6464
"""
65-
65+
6666
def __init__(self, msg_type=None, sender=None,
6767
length=None, payload=None, crc=None):
6868
self.preamble = SBP_PREAMBLE
@@ -71,11 +71,13 @@ def __init__(self, msg_type=None, sender=None,
7171
self.length = length
7272
self.payload = payload
7373
self.crc = crc
74-
74+
75+
def __eq__(self, other):
76+
return self.__dict__ == other.__dict__
77+
7578
def pack(self):
7679
"""Pack to framed binary message.
77-
78-
80+
7981
"""
8082
framed_msg = struct.pack('<BHHB',
8183
self.preamble,
@@ -92,7 +94,7 @@ def __repr__(self):
9294
self.payload, self.crc)
9395
fmt = "<SBP (preamble=0x%X, msg_type=0x%X, sender=%s, length=%d, payload=%s, crc=0x%X)>"
9496
return fmt % p
95-
97+
9698
@staticmethod
9799
def from_json_dict(data):
98100
msg_type = data['msg_type']

python/sbp/client/drivers/file_driver.py

+38-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
99
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
1010

11-
from base_driver import BaseDriver
11+
from .base_driver import BaseDriver
1212

1313
class FileDriver(BaseDriver):
1414
"""
@@ -24,3 +24,40 @@ class FileDriver(BaseDriver):
2424
def __init__(self, filename):
2525
handle = open(filename, "r")
2626
super(FileDriver, self).__init__(handle)
27+
28+
def __iter__(self):
29+
return self
30+
31+
def seek(self, pos, whence=0):
32+
"""
33+
Wrapper around file seek.
34+
35+
Parameters
36+
----------
37+
pos : int
38+
Position of the read/write pointer within the file.
39+
whence : int
40+
Optional and defaults to 0 which means absolute file
41+
positioning, other values are 1 which means seek relative to the
42+
current position and 2 means seek relative to the file's end
43+
44+
"""
45+
self.handle.seek(pos, whence)
46+
47+
def next(self):
48+
"""
49+
Call handle's iterator.
50+
51+
"""
52+
line = self.handle.readline()
53+
if not line:
54+
raise StopIteration
55+
return line
56+
57+
def readline(self):
58+
"""
59+
Readline wrapper: read a line which is terminated with end-of-line
60+
(EOL) character ('\n' by default), or until timeout.
61+
62+
"""
63+
return self.handle.readline()

python/sbp/client/drivers/pyftdi_driver.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
99
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
1010

11-
from base_driver import BaseDriver
11+
from .base_driver import BaseDriver
1212

1313
class PyFTDIDriver(BaseDriver):
1414
"""
15-
PyFTDIDriver
15+
PyFTDIDriver
1616
1717
The :class:`PyFTDIDriver` class reads SBP messages from a serial port
1818
using the pyftdi driver.

python/sbp/client/drivers/pyserial_driver.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
99
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
1010

11-
from base_driver import BaseDriver
11+
from .base_driver import BaseDriver
1212

1313
class PySerialDriver(BaseDriver):
1414
"""

python/sbp/client/loggers/base_logger.py

+41
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
99
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
1010

11+
from ...table import dispatch
1112
import calendar
1213
import time
1314

@@ -21,6 +22,7 @@ class BaseLogger(object):
2122
----------
2223
filename : string
2324
File to log to.
25+
2426
"""
2527
def __init__(self, filename):
2628
self.handle = open(filename, "w+")
@@ -47,3 +49,42 @@ def delta(self):
4749
"""
4850
return int((time.time() - self.base_time) * 1000)
4951

52+
53+
class LogIterator(object):
54+
"""
55+
LogIterator
56+
57+
The :class: `LogIterator` provides an abstract interface for reading
58+
serialized logs of SBP data.
59+
60+
Parameters
61+
----------
62+
handle : File-like handle
63+
Any file-like handle providing SBP messages.
64+
65+
"""
66+
def __init__(self, handle, dispatcher=dispatch):
67+
self.handle = handle
68+
self.dispatcher = dispatcher
69+
70+
def __enter__(self):
71+
return self
72+
73+
def __exit__(self, *args):
74+
self.handle.close()
75+
76+
def __iter__(self):
77+
return self
78+
79+
def next(self):
80+
"""Return the next record tuple from the log file. If an unknown SBP
81+
message type is found, it'll return the raw SBP. If EOF, throws
82+
exception and then returns to start of file.
83+
84+
Returns
85+
-------
86+
(float, float, object)
87+
(elapsed msec since beginning of log, UTC timestamp, msg)
88+
89+
"""
90+
raise NotImplementedError("next() not implemented!")

python/sbp/client/loggers/byte_logger.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010

1111
import struct
1212

13-
from base_logger import BaseLogger
13+
from .base_logger import BaseLogger
1414

1515
class ByteLogger(BaseLogger):
1616
"""
1717
ByteLogger
18-
18+
1919
The :class:`ByteLogger` logs SBP messages to bytes.
2020
"""
2121
def __call__(self, msg):

python/sbp/client/loggers/json_logger.py

+47-5
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,63 @@
88
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
99
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
1010

11+
from ... import SBP
12+
from .base_logger import BaseLogger, LogIterator
1113
import json
1214

13-
from base_logger import BaseLogger
14-
1515
class JSONLogger(BaseLogger):
1616
"""
1717
JSONLogger
18-
18+
1919
The :class:`JSONLogger` logs JSON records.
2020
"""
2121
def __call__(self, msg):
2222
self.call(msg)
2323

2424
def fmt_msg(self, msg):
25-
return {"delta": self.delta(), "timestamp": self.timestamp(), "data": msg.to_json_dict()}
26-
25+
return {"delta": self.delta(),
26+
"timestamp": self.timestamp(),
27+
"data": msg.to_json_dict()}
28+
2729
def call(self, msg):
2830
self.handle.write(json.dumps(self.fmt_msg(msg)) + "\n")
31+
32+
33+
class JSONLogIterator(LogIterator):
34+
"""
35+
JSONLogIterator
36+
37+
The :class:`JSONLogIterator` is an iterator for reading JSON logs
38+
of SBP data.
39+
40+
Parameters
41+
----------
42+
handle : File-like handle
43+
Any file-like handle providing SBP messages.
44+
45+
"""
46+
47+
def next(self):
48+
"""
49+
Return the next record tuple from log file containing
50+
JSON-serialized SBP. If an unknown SBP message type is found,
51+
it'll return the raw SBP. If EOF, throws exception and then
52+
returns to start of file.
53+
54+
Returns
55+
-------
56+
(float, float, object)
57+
(elapsed msec since beginning of log, UTC timestamp, msg)
58+
59+
"""
60+
for line in self.handle:
61+
data = json.loads(line)
62+
delta = data['delta']
63+
timestamp = data['timestamp']
64+
item = SBP.from_json_dict(data['data'])
65+
try:
66+
yield (delta, timestamp, self.dispatcher(item))
67+
except KeyError:
68+
yield (delta, timestamp, item)
69+
self.handle.seek(0, 0)
70+
raise StopIteration

python/sbp/client/loggers/pickle_logger.py

+41-3
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
99
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
1010

11+
from .base_logger import BaseLogger, LogIterator
1112
import cPickle as pickle
1213

13-
from base_logger import BaseLogger
14-
1514
class PickleLogger(BaseLogger):
1615
"""
1716
PickleLogger
18-
17+
1918
The :class:`PickleLogger` logs pickled structures.
2019
"""
2120
def __call__(self, msg):
@@ -26,3 +25,42 @@ def fmt_msg(self, msg):
2625

2726
def call(self, msg):
2827
pickle.dump(self.fmt_msg(msg), self.handle, 2)
28+
29+
30+
class PickleLogIterator(LogIterator):
31+
"""
32+
PickleLogIterator
33+
34+
The :class:`PickleLogIterator` is an iterator for reading pickled logs
35+
of SBP data.
36+
37+
Parameters
38+
----------
39+
handle : File-like handle
40+
Any file-like handle providing SBP messages.
41+
42+
"""
43+
44+
def next(self):
45+
"""
46+
Return the next record tuple from log file containing
47+
Pickle SBP. If an unknown SBP message type is found,
48+
it'll return the raw SBP. If EOF, throws exception and then
49+
returns to start of file.
50+
51+
Returns
52+
-------
53+
(float, float, object)
54+
(elapsed msec since beginning of log, UTC timestamp, msg)
55+
56+
"""
57+
try:
58+
while True:
59+
delta, timestamp, item = pickle.load(self.handle)
60+
try:
61+
yield (delta, timestamp, self.dispatcher(item))
62+
except KeyError:
63+
yield (delta, timestamp, item)
64+
except EOFError:
65+
self.handle.seek(0, 0)
66+
raise StopIteration

python/sbp/client/main.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@
1717
import sys
1818
import time
1919

20-
from ..piksi import SBP_MSG_PRINT
21-
from drivers.file_driver import FileDriver
22-
from drivers.pyserial_driver import PySerialDriver
23-
from drivers.pyftdi_driver import PyFTDIDriver
24-
from loggers.byte_logger import ByteLogger
25-
from loggers.json_logger import JSONLogger
26-
from loggers.null_logger import NullLogger
27-
from loggers.pickle_logger import PickleLogger
28-
from handler import Handler
20+
from ..piksi import SBP_MSG_PRINT
21+
from .drivers.file_driver import FileDriver
22+
from .drivers.pyserial_driver import PySerialDriver
23+
from .drivers.pyftdi_driver import PyFTDIDriver
24+
from .loggers.byte_logger import ByteLogger
25+
from .loggers.json_logger import JSONLogger
26+
from .loggers.null_logger import NullLogger
27+
from .loggers.pickle_logger import PickleLogger
28+
from .handler import Handler
2929

3030
DEFAULT_PORT = "/dev/ttyUSB0"
3131
DEFAULT_BAUD = 1000000

0 commit comments

Comments
 (0)