Skip to content

Commit a7937d9

Browse files
author
Brendan Whitfield
committed
wrote legacy multiline handler, started updating tests
1 parent b2e43b3 commit a7937d9

6 files changed

Lines changed: 92 additions & 28 deletions

File tree

obd/OBDCommand.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,6 @@ def __call__(self, message):
6666
# create the response object with the raw data recieved
6767
# and reference to original command
6868
r = Response(self, message)
69-
70-
# discard the header/echo of the given command
71-
# 0101 ----> [41 01] 83 00 00 00
72-
# vs.
73-
# 03 ----> [43] 01 04 80 03 41 23
74-
header_bytes_expected = len(self.get_command()) // 2
7569

7670
# combine the bytes back into a hex string
7771
# TODO: rewrite decoders to handle raw byte arrays

obd/protocols/protocol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def create_frame(self, raw):
128128
"""
129129
override in subclass for each protocol
130130
131-
Function recieves the raw string data for a frame.
131+
Function recieves a list of byte values for a frame.
132132
133133
Function should return a Frame instance. If fatal errors were
134134
found, this function should return None (the Frame is dropped).

obd/protocols/protocol_can.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ def create_frame(self, raw):
5757

5858
# read header information
5959
if self.id_bits == 11:
60+
# Ex.
61+
# 00 00 07 E8 06 41 00 BE 7F B8 13
62+
6063
frame.priority = raw_bytes[2] & 0x0F # always 7
6164
frame.addr_mode = raw_bytes[3] & 0xF0 # 0xD0 = functional, 0xE0 = physical
6265

obd/protocols/protocol_legacy.py

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,16 @@ def create_frame(self, raw):
4545
raw_bytes = ascii_to_bytes(raw)
4646

4747
if len(raw_bytes) < 5:
48+
debug("Discarded frame for being too short")
4849
return None
4950

50-
frame.data_bytes = raw_bytes[3:-1] # exclude trailing checksum (handled by ELM adapter)
51+
# Ex.
52+
# [Header] [ Frame ]
53+
# 48 6B 10 41 00 BE 7F B8 13 ck
54+
# ck = checksum byte
55+
56+
# exclude header and trailing checksum (handled by ELM adapter)
57+
frame.data_bytes = raw_bytes[3:-1]
5158

5259
# read header information
5360
frame.priority = raw_bytes[0]
@@ -60,11 +67,66 @@ def create_message(self, frames, tx_id):
6067

6168
message = Message(frames, tx_id)
6269

63-
if len(frames) == 1:
64-
message.data_bytes = message.frames[0].data_bytes
70+
# len(frames) will always be >= 1 (see the caller, protocol.py)
71+
mode = frames[0].data_bytes[0]
72+
73+
# test that all frames are responses to the same Mode (SID)
74+
if len(frames) > 1:
75+
if not all([mode == f.data_bytes[0] for f in frames[1:]]):
76+
debug("Recieved frames from multiple commands")
77+
return None
78+
79+
# legacy protocols have different re-assembly
80+
# procedures for different Modes
81+
82+
if mode == 0x43:
83+
# GET_DTC requests return frames with no PID or order bytes
84+
# accumulate all of the data, minus the Mode bytes of each frame
85+
86+
# Ex.
87+
# [ Frame ]
88+
# 48 6B 10 43 03 00 03 02 03 03 ck
89+
# 48 6B 10 43 03 04 00 00 00 00 ck
90+
# [ Data ]
91+
92+
for f in frames:
93+
message.data_bytes += f.data_bytes[1:]
94+
6595
else:
66-
debug("Recieved multi-frame response. Can't parse those yet")
67-
return None
96+
if len(frames) == 1:
97+
# return data, excluding the mode/pid bytes
98+
99+
# Ex.
100+
# [ Frame ]
101+
# 48 6B 10 41 00 BE 7F B8 13 ck
102+
# [ Data ]
103+
104+
message.data_bytes = frames[0].data_bytes[2:]
105+
106+
else: # len(frames) > 1:
107+
# generic multiline requests carry an order byte
108+
109+
# Ex.
110+
# [ Frame ]
111+
# 48 6B 10 49 02 01 00 00 00 31 ck
112+
# 48 6B 10 49 02 02 44 34 47 50 ck
113+
# 48 6B 10 49 02 03 30 30 52 35 ck
114+
# etc... [] [ Data ]
115+
116+
# sort the frames by the order byte
117+
frames = sorted(frames, key=lambda f: f[2])
118+
119+
# ensure that each order byte is consecutive by looking at
120+
# them in pairs. (see if anything's missing)
121+
indices = [f[2] for f in frames]
122+
pairs = zip(indices, indices[1:])
123+
if not all([p[0]+1 == p[1] for p in pairs]):
124+
debug("Recieved multiline response with missing frames")
125+
return None
126+
127+
# now that they're in order, accumulate the data from each one
128+
for f in frames:
129+
message.data_bytes += f.data_bytes[3:]
68130

69131
return message
70132

obd/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@ class Unit:
6262

6363

6464
class Response():
65-
def __init__(self, command=None, raw_data=None):
65+
def __init__(self, command=None, message=None):
6666
self.command = command
67-
self.raw_data = raw_data
67+
self.message = message
6868
self.value = None
6969
self.unit = Unit.NONE
7070
self.time = time.time()
7171

7272
def is_null(self):
73-
return (self.raw_data == None) or (self.value == None)
73+
return (self.message == None) or (self.value == None)
7474

7575
def __str__(self):
7676
return "%s %s" % (str(self.value), str(self.unit))

tests/test_protocols.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,36 +36,41 @@ def test_legacy():
3636

3737
# single frame cases
3838

39-
r = p("48 6B 10 41 00 BE 1F B8 11 AA\r\r")
39+
r = p(["48 6B 10 41 00 BE 1F B8 11 AA"])
4040
assert len(r) == 1
41-
check_message(r[0], 1, 16, [65, 0, 190, 31, 184, 17])
41+
check_message(r[0], 1, 16, [190, 31, 184, 17])
4242

43-
r = p("48 6B 10 41 00 BE 1F B8 11 AA")
43+
r = p(["48 6B 10 41 00 BE 1F B8 11 AA"])
4444
assert len(r) == 1
45-
check_message(r[0], 1, 16, [65, 0, 190, 31, 184, 17])
45+
check_message(r[0], 1, 16, [190, 31, 184, 17])
4646

47-
r = p("NO DATA")
47+
r = p(["NO DATA"])
4848
assert len(r) == 0
4949

50-
r = p("TOTALLY NOT HEX")
50+
r = p(["TOTALLY NOT HEX"])
5151
assert len(r) == 0
5252

5353
# multi-frame cases
5454

5555
# seperate ECUs, single frames each
56-
r = p("48 6B 10 41 00 BE 1F B8 11 AA\r\r48 6B 11 41 00 01 02 03 04 AA\r\r")
56+
r = p(["48 6B 10 41 00 BE 1F B8 11 AA", "48 6B 11 41 00 01 02 03 04 AA"])
5757
assert len(r) == 2
58-
check_message(r[0], 1, 16, [65, 0, 190, 31, 184, 17])
59-
check_message(r[1], 1, 17, [65, 0, 1, 2, 3, 4 ])
58+
check_message(r[0], 1, 16, [190, 31, 184, 17])
59+
check_message(r[1], 1, 17, [1, 2, 3, 4 ])
6060

61-
r = p("NO DATA\r\r48 6B 10 41 00 BE 1F B8 11 AA\r\r")
61+
r = p(["NO DATA", "48 6B 10 41 00 BE 1F B8 11 AA"])
6262
assert len(r) == 1
63-
check_message(r[0], 1, 16, [65, 0, 190, 31, 184, 17])
63+
check_message(r[0], 1, 16, [190, 31, 184, 17])
6464

65-
r = p("NO DATA\r\rNO DATA\r\r")
65+
r = p(["NO DATA", "NO DATA"])
6666
assert len(r) == 0
6767

68+
# GET_DTC requests
69+
r = p(["48 6B 10 43 03 00 03 02 03 03 14", "48 6B 10 43 03 04 00 00 00 00 0D"])
70+
assert len(r) == 1
71+
check_message(r[0], 2, 16, [0x03, 0x0, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x0, 0x0, 0x0, 0x0])
6872

73+
'''
6974
def test_can_11():
7075
for protocol in CAN_11_PROTOCOLS:
7176
p = protocol()
@@ -102,7 +107,7 @@ def test_can_11():
102107
103108
r = p("NO DATA\r\rNO DATA\r\r")
104109
assert len(r) == 0
105-
110+
'''
106111

107112
def test_can_29():
108113
pass

0 commit comments

Comments
 (0)