Skip to content

Commit d494c30

Browse files
author
Brendan Whitfield
committed
protocols now override parse_frame and parse_message functions
1 parent 6b8ff23 commit d494c30

6 files changed

Lines changed: 136 additions & 51 deletions

File tree

obd/protocols/frame.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11

22

33
class Frame(object):
4-
def __init__(self, protocol, raw_bytes):
5-
self.protocol = protocol
6-
self.raw_bytes = raw_bytes
7-
4+
def __init__(self, raw):
5+
self.raw = raw
86
self.data_bytes = []
97
self.priority = None
8+
self.addr_mode = None
109
self.rx_id = None
1110
self.tx_id = None

obd/protocols/message.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
3+
class Message(object):
4+
def __init__(self, frames, tx_id):
5+
self.frames = frames
6+
self.tx_id = tx_id
7+
self.data_bytes = []

obd/protocols/protocol.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,61 @@
11

22
"""
33
4-
Protocol objects are factories for Frames.
5-
They are __called__ with a byte array, and return the parsed frame objects.
6-
They are stateless
4+
Protocol objects are stateless factories for Frames and Messages.
5+
They are __called__ with the raw string response, and
6+
return a list of Messages.
77
88
"""
99

10+
from frame import Frame
11+
from message import Message
12+
import re
13+
14+
1015

1116
class Protocol(object):
12-
def __init__(self, baud=38400):
13-
self.baud = baud
17+
def __init__(self, baud=38400):
18+
self.baud = baud
19+
20+
21+
def __call__(self, raw):
22+
23+
# split by lines into frames, and remove empty lines
24+
lines = filter(bool, re.split("[\r\n]", raw))
25+
26+
# ditch spaces
27+
lines = [line.replace(' ', '') for line in lines]
28+
29+
# create frame objects for each line
30+
frames = [Frame(line) for line in lines]
31+
32+
# subclass function to load the frame parameters
33+
for frame in frames:
34+
self.parse_frame(frame)
35+
36+
# group frames by transmitting ECU (tx_id)
37+
ecus = {}
38+
for frame in frames:
39+
if frame.tx_id not in ecus:
40+
ecus[frame.tx_id] = [frame]
41+
else:
42+
ecus[frame.tx_id].append(frame)
43+
44+
messages = []
45+
46+
for ecu in ecus:
47+
message = Message(ecus[ecu], ecu)
48+
# subclass function to assemble frames into data_bytes
49+
self.parse_message(message)
50+
messages.append(message)
51+
52+
return messages
53+
1454

15-
def create_frame(self, raw_bytes):
16-
""" override in subclass for each protocol """
17-
raise NotImplementedError()
55+
def parse_frame(self, frame):
56+
""" override in subclass for each protocol """
57+
raise NotImplementedError()
1858

19-
def parse_frames(self, frames):
20-
""" override in subclass for each protocol """
21-
raise NotImplementedError()
59+
def parse_message(self, message):
60+
""" override in subclass for each protocol """
61+
raise NotImplementedError()

obd/protocols/protocol_can.py

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,51 @@
22

33
from protocol import Protocol
44
from frame import Frame
5+
from obd.utils import ascii_to_bytes
56

67

78
class CANProtocol(Protocol):
8-
def __init__(self, baud, id_bits):
9-
Protocol.__init__(baud)
10-
self.id_bits = id_bits
11-
12-
13-
def create_frame(self, raw_bytes):
14-
frame = Frame(self, raw_bytes)
15-
return frame
16-
17-
18-
def parse_frames(self, frames):
9+
def __init__(self, baud, id_bits):
10+
Protocol.__init__(self, baud)
11+
self.id_bits = id_bits
12+
13+
14+
def parse_frame(self, frame):
15+
16+
# pad 11-bit CAN headers out to 32 bits for consistency,
17+
# since ELM already does this for 29-bit CAN headers
18+
if self.id_bits == 11:
19+
frame.raw = "00000" + frame.raw
20+
21+
raw_bytes = ascii_to_bytes(frame.raw)
22+
23+
# read header information
24+
if self.id_bits == 11:
25+
frame.priority = raw_bytes[2] & 0x0F # always 7
26+
frame.addr_mode = raw_bytes[3] & 0xF0 # 0xD0 = functional, 0xE0 = physical
27+
28+
if frame.addr_mode == 0xD0:
29+
#untested("11-bit functional request from tester")
30+
frame.rx_id = raw_bytes[3] & 0x0F # usually (always?) 0x0F for broadcast
31+
frame.tx_id = 0xF1 # made-up to mimic all other protocols
32+
elif raw_bytes[3] & 0x08:
33+
frame.rx_id = 0xF1 # made-up to mimic all other protocols
34+
frame.tx_id = raw_bytes[3] & 0x07
35+
else:
36+
#untested("11-bit message header from tester (functional or physical)")
37+
frame.tx_id = 0xF1 # made-up to mimic all other protocols
38+
frame.rx_id = raw_bytes[3] & 0x07
39+
40+
else: # self.id_bits == 29:
41+
frame.priority = raw_bytes[0] # usually (always?) 0x18
42+
frame.addr_mode = raw_bytes[1] # DB = functional, DA = physical
43+
frame.rx_id = raw_bytes[2] # 0x33 = broadcast (functional)
44+
frame.tx_id = raw_bytes[3] # 0xF1 = tester ID
45+
46+
frame.data_bytes = raw_bytes[5:]
47+
48+
49+
def parse_message(self, message):
1950
pass
2051

2152

@@ -27,26 +58,27 @@ def parse_frames(self, frames):
2758
##############################################
2859

2960

61+
3062
class ISO_15765_4_11bit_500k(CANProtocol):
31-
def __init__(self):
32-
CANProtocol.__init__(baud=500000, id_bits=11)
63+
def __init__(self):
64+
CANProtocol.__init__(self, baud=500000, id_bits=11)
3365

3466

3567
class ISO_15765_4_29bit_500k(CANProtocol):
36-
def __init__(self):
37-
CANProtocol.__init__(baud=500000, id_bits=29)
68+
def __init__(self):
69+
CANProtocol.__init__(self, baud=500000, id_bits=29)
3870

3971

4072
class ISO_15765_4_11bit_250k(CANProtocol):
41-
def __init__(self):
42-
CANProtocol.__init__(baud=250000, id_bits=11)
73+
def __init__(self):
74+
CANProtocol.__init__(self, baud=250000, id_bits=11)
4375

4476

4577
class ISO_15765_4_29bit_250k(CANProtocol):
46-
def __init__(self):
47-
CANProtocol.__init__(baud=250000, id_bits=29)
78+
def __init__(self):
79+
CANProtocol.__init__(self, baud=250000, id_bits=29)
4880

4981

5082
class SAE_J1939(CANProtocol):
51-
def __init__(self):
52-
CANProtocol.__init__(baud=250000, id_bits=29)
83+
def __init__(self):
84+
CANProtocol.__init__(self, baud=250000, id_bits=29)

obd/protocols/protocol_legacy.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

33
from protocol import Protocol
44
from frame import Frame
5+
from obd.utils import ascii_to_bytes
6+
from obd.debug import debug
57

68

79
class LegacyProtocol(Protocol):
8-
def __init__(self, baud):
9-
Protocol.__init__(baud)
1010

11-
def create_frame(self, raw_bytes):
11+
def __init__(self, baud):
12+
Protocol.__init__(self, baud)
1213

13-
frame = Frame(self, raw_bytes)
14+
def parse_frame(self, frame):
15+
raw_bytes = ascii_to_bytes(frame.raw)
1416

1517
frame.data_bytes = raw_bytes[3:-1] # exclude trailing checksum (handled by ELM adapter)
1618

@@ -19,10 +21,13 @@ def create_frame(self, raw_bytes):
1921
frame.rx_id = raw_bytes[1]
2022
frame.tx_id = raw_bytes[2]
2123

22-
return frame
24+
def parse_message(self, message):
25+
if len(message.frames) == 1:
26+
message.data_bytes = message.frames[0].data_bytes
27+
else:
28+
debug("Recieved multi-frame response. Can't parse those yet")
29+
2330

24-
def parse_frames(self, frames):
25-
pass
2631

2732

2833

@@ -35,28 +40,25 @@ def parse_frames(self, frames):
3540

3641

3742
class SAE_J1850_PWM(LegacyProtocol):
38-
3943
def __init__(self):
40-
LegacyProtocol.__init__(baud=41600)
44+
LegacyProtocol.__init__(self, baud=41600)
4145

4246

4347
class SAE_J1850_VPW(LegacyProtocol):
44-
4548
def __init__(self):
46-
LegacyProtocol.__init__(baud=10400)
49+
LegacyProtocol.__init__(self, baud=10400)
4750

4851

4952
class ISO_9141_2(LegacyProtocol):
50-
5153
def __init__(self):
52-
LegacyProtocol.__init__(baud=10400)
54+
LegacyProtocol.__init__(self, baud=10400)
5355

5456

5557
class ISO_14230_4_5baud(LegacyProtocol):
5658
def __init__(self):
57-
LegacyProtocol.__init__(baud=10400)
59+
LegacyProtocol.__init__(self, baud=10400)
5860

5961

6062
class ISO_14230_4_fast(LegacyProtocol):
6163
def __init__(self):
62-
LegacyProtocol.__init__(baud=10400)
64+
LegacyProtocol.__init__(self, baud=10400)

obd/utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ def __str__(self):
9090
return "Test %s: %s, %s" % (name, a, c)
9191

9292

93+
def ascii_to_bytes(a):
94+
b = []
95+
for i in range(0, len(a), 2):
96+
b.append(int(a[i:i+2], 16))
97+
return b
9398

9499
def unhex(_hex):
95100
_hex = "0" if _hex == "" else _hex

0 commit comments

Comments
 (0)