def parse_message(self, message): frames = message.frames if len(frames) == 1: frame = frames[0] if frame.type != self.FRAME_TYPE_SF: debug("Recieved lone frame not marked as single frame") return False # extract data, ignore PCI byte and anything after the marked length # [ Frame ] # [ Data ] # 00 00 07 E8 06 41 00 BE 7F B8 13 xx xx xx xx, anything else is ignored message.data = frame.data[1:1 + frame.data_len] else: # sort FF and CF into their own lists ff = [] cf = [] for f in frames: if f.type == self.FRAME_TYPE_FF: ff.append(f) elif f.type == self.FRAME_TYPE_CF: cf.append(f) else: debug( "Dropping frame in multi-frame response not marked as FF or CF" ) # check that we captured only one first-frame if len(ff) > 1: debug("Recieved multiple frames marked FF") return False elif len(ff) == 0: debug("Never received frame marked FF") return False # check that there was at least one consecutive-frame if len(cf) == 0: debug("Never received frame marked CF") return False # calculate proper sequence indices from the lower 4 bits given for prev, curr in zip(cf, cf[1:]): # Frame sequence numbers only specify the low order bits, so compute the # full sequence number from the frame number and the last sequence number seen: # 1) take the high order bits from the last_sn and low order bits from the frame seq = (prev.seq_index & ~0x0F) + (curr.seq_index) # 2) if this is more than 7 frames away, we probably just wrapped (e.g., # last=0x0F current=0x01 should mean 0x11, not 0x01) if seq < prev.seq_index - 7: # untested seq += 0x10 curr.seq_index = seq # sort the sequence indices cf = sorted(cf, key=lambda f: f.seq_index) # check contiguity, and that we aren't missing any frames indices = [f.seq_index for f in cf] if not contiguous(indices, 1, len(cf)): debug("Recieved multiline response with missing frames") return False # first frame: # [ Frame ] # [PCI] <-- first frame has a 2 byte PCI # [L ] [ Data ] L = length of message in bytes # 00 00 07 E8 10 13 49 04 01 35 36 30 # consecutive frame: # [ Frame ] # [] <-- consecutive frames have a 1 byte PCI # N [ Data ] N = current frame number (rolls over to 0 after F) # 00 00 07 E8 21 32 38 39 34 39 41 43 # 00 00 07 E8 22 00 00 00 00 00 00 31 # original data: # [ specified message length (from first-frame) ] # 49 04 01 35 36 30 32 38 39 34 39 41 43 00 00 00 00 00 00 31 # on the first frame, skip PCI byte AND length code message.data = ff[0].data[2:] # now that they're in order, load/accumulate the data from each CF frame for f in cf: message.data += f.data[1:] # chop off the PCI byte # chop to the correct size (as specified in the first frame) message.data = message.data[:ff[0].data_len] # chop off the Mode/PID bytes based on the mode number mode = message.data[0] if mode == 0x43: # TODO: confirm this logic. I don't have any raw test data for it yet # fetch the DTC count, and use it as a length code num_dtc_bytes = message.data[1] * 2 # skip the PID byte and the DTC count, message.data = message.data[2:][:num_dtc_bytes] else: # skip the Mode and PID bytes # # single line response: # [ Data ] # 00 00 07 E8 06 41 00 BE 7F B8 13 # # OR, the data from a multiline response: # [ Data ] # 49 04 01 35 36 30 32 38 39 34 39 41 43 00 00 00 00 00 00 message.data = message.data[2:] return True
def parse_message(self, message): frames = message.frames # len(frames) will always be >= 1 (see the caller, protocol.py) mode = frames[0].data[0] # test that all frames are responses to the same Mode (SID) if len(frames) > 1: if not all([mode == f.data[0] for f in frames[1:]]): logger.warning("Recieved frames from multiple commands") return False # legacy protocols have different re-assembly # procedures for different Modes # ~~~~ # NOTE: THERE ARE HACKS IN HERE to make some output compatible with CAN # since CAN is the standard, and this is considered legacy, I'm # fixing ugly inconsistencies between the two protocols here. # ~~~~ if mode == 0x43: # GET_DTC requests return frames with no PID or order bytes # accumulate all of the data, minus the Mode bytes of each frame # Ex. # insert faux-byte to mimic the CAN style DTC requests # | # [ | Frame ] # 48 6B 10 43 03 00 03 02 03 03 ck # 48 6B 10 43 03 04 00 00 00 00 ck # [ Data ] message.data = bytearray([0x43, 0x00]) # forge the mode byte and CAN's DTC_count byte for f in frames: message.data += f.data[1:] else: if len(frames) == 1: # return data, excluding the mode/pid bytes # Ex. # [ Frame/Data ] # 48 6B 10 41 00 BE 7F B8 13 ck message.data = frames[0].data else: # len(frames) > 1: # generic multiline requests carry an order byte # Ex. # [ Frame ] # 48 6B 10 49 02 01 00 00 00 31 ck # 48 6B 10 49 02 02 44 34 47 50 ck # 48 6B 10 49 02 03 30 30 52 35 ck # etc... [] [ Data ] # becomes: # 49 02 [] 00 00 00 31 44 34 47 50 30 30 52 35 # | [ ] [ ] [ ] # order byte is removed # sort the frames by the order byte frames = sorted(frames, key=lambda f: f.data[2]) # check contiguity indices = [f.data[2] for f in frames] if not contiguous(indices, 1, len(frames)): logger.warning("Recieved multiline response with missing frames") return False # now that they're in order, accumulate the data from each frame # preserve the first frame's mode and PID bytes (for consistency with CAN) frames[0].data.pop(2) # remove the sequence byte message.data = frames[0].data # add the data from the remaining frames for f in frames[1:]: message.data += f.data[3:] # loose the mode/pid/seq bytes return True
def parse_message(self, message): frames = message.frames # len(frames) will always be >= 1 (see the caller, protocol.py) mode = frames[0].data[0] # test that all frames are responses to the same Mode (SID) if len(frames) > 1: if not all([mode == f.data[0] for f in frames[1:]]): logger.debug("Recieved frames from multiple commands") return False # legacy protocols have different re-assembly # procedures for different Modes if mode == 0x43: # GET_DTC requests return frames with no PID or order bytes # accumulate all of the data, minus the Mode bytes of each frame # Ex. # [ Frame ] # 48 6B 10 43 03 00 03 02 03 03 ck # 48 6B 10 43 03 04 00 00 00 00 ck # [ Data ] for f in frames: message.data += f.data[1:] else: if len(frames) == 1: # return data, excluding the mode/pid bytes # Ex. # [ Frame ] # 48 6B 10 41 00 BE 7F B8 13 ck # [ Data ] message.data = frames[0].data[2:] else: # len(frames) > 1: # generic multiline requests carry an order byte # Ex. # [ Frame ] # 48 6B 10 49 02 01 00 00 00 31 ck # 48 6B 10 49 02 02 44 34 47 50 ck # 48 6B 10 49 02 03 30 30 52 35 ck # etc... [] [ Data ] # sort the frames by the order byte frames = sorted(frames, key=lambda f: f.data[2]) # check contiguity indices = [f.data[2] for f in frames] if not contiguous(indices, 1, len(frames)): logger.debug( "Recieved multiline response with missing frames") return False # now that they're in order, accumulate the data from each frame for f in frames: message.data += f.data[3:] # loose the mode/pid/seq bytes return True
def parse_message(self, message): frames = message.frames if len(frames) == 1: frame = frames[0] if frame.type != self.FRAME_TYPE_SF: logger.debug("Recieved lone frame not marked as single frame") return False # extract data, ignore PCI byte and anything after the marked length # [ Frame ] # [ Data ] # 00 00 07 E8 06 41 00 BE 7F B8 13 xx xx xx xx, anything else is ignored message.data = frame.data[1:1+frame.data_len] else: # sort FF and CF into their own lists ff = [] cf = [] for f in frames: if f.type == self.FRAME_TYPE_FF: ff.append(f) elif f.type == self.FRAME_TYPE_CF: cf.append(f) else: logger.debug("Dropping frame in multi-frame response not marked as FF or CF") # check that we captured only one first-frame if len(ff) > 1: logger.debug("Recieved multiple frames marked FF") return False elif len(ff) == 0: logger.debug("Never received frame marked FF") return False # check that there was at least one consecutive-frame if len(cf) == 0: logger.debug("Never received frame marked CF") return False # calculate proper sequence indices from the lower 4 bits given for prev, curr in zip(cf, cf[1:]): # Frame sequence numbers only specify the low order bits, so compute the # full sequence number from the frame number and the last sequence number seen: # 1) take the high order bits from the last_sn and low order bits from the frame seq = (prev.seq_index & ~0x0F) + (curr.seq_index) # 2) if this is more than 7 frames away, we probably just wrapped (e.g., # last=0x0F current=0x01 should mean 0x11, not 0x01) if seq < prev.seq_index - 7: # untested seq += 0x10 curr.seq_index = seq # sort the sequence indices cf = sorted(cf, key=lambda f: f.seq_index) # check contiguity, and that we aren't missing any frames indices = [f.seq_index for f in cf] if not contiguous(indices, 1, len(cf)): logger.debug("Recieved multiline response with missing frames") return False # first frame: # [ Frame ] # [PCI] <-- first frame has a 2 byte PCI # [L ] [ Data ] L = length of message in bytes # 00 00 07 E8 10 13 49 04 01 35 36 30 # consecutive frame: # [ Frame ] # [] <-- consecutive frames have a 1 byte PCI # N [ Data ] N = current frame number (rolls over to 0 after F) # 00 00 07 E8 21 32 38 39 34 39 41 43 # 00 00 07 E8 22 00 00 00 00 00 00 31 # original data: # [ specified message length (from first-frame) ] # 49 04 01 35 36 30 32 38 39 34 39 41 43 00 00 00 00 00 00 31 # on the first frame, skip PCI byte AND length code message.data = ff[0].data[2:] # now that they're in order, load/accumulate the data from each CF frame for f in cf: message.data += f.data[1:] # chop off the PCI byte # chop to the correct size (as specified in the first frame) message.data = message.data[:ff[0].data_len] # TODO: this is an ugly solution, maybe move mode/pid byte ignoring to the decoders? # chop off the Mode/PID bytes based on the mode number mode = message.data[0] if mode == 0x43: # [] # 43 03 11 11 22 22 33 33 # [DTC] [DTC] [DTC] # fetch the DTC count, and use it as a length code num_dtc_bytes = message.data[1] * 2 # skip the PID byte and the DTC count, message.data = message.data[2:][:num_dtc_bytes] elif mode == 0x46: # the monitor test mode only has a mode number # the MID (mode 6's version of a PID) is repeated, # and handled in the decoder message.data = message.data[1:] else: # skip the Mode and PID bytes # # single line response: # [ Data ] # 00 00 07 E8 06 41 00 BE 7F B8 13 # # OR, the data from a multiline response: # [ Data ] # 49 04 01 35 36 30 32 38 39 34 39 41 43 00 00 00 00 00 00 message.data = message.data[2:] return True
def parse_message(self, message): frames = message.frames # len(frames) will always be >= 1 (see the caller, protocol.py) mode = frames[0].data[0] # test that all frames are responses to the same Mode (SID) if len(frames) > 1: if not all([mode == f.data[0] for f in frames[1:]]): debug("Recieved frames from multiple commands") return False # legacy protocols have different re-assembly # procedures for different Modes if mode == 0x43: # GET_DTC requests return frames with no PID or order bytes # accumulate all of the data, minus the Mode bytes of each frame # Ex. # [ Frame ] # 48 6B 10 43 03 00 03 02 03 03 ck # 48 6B 10 43 03 04 00 00 00 00 ck # [ Data ] for f in frames: message.data += f.data[1:] else: if len(frames) == 1: # return data, excluding the mode/pid bytes # Ex. # [ Frame ] # 48 6B 10 41 00 BE 7F B8 13 ck # [ Data ] message.data = frames[0].data[2:] else: # len(frames) > 1: # generic multiline requests carry an order byte # Ex. # [ Frame ] # 48 6B 10 49 02 01 00 00 00 31 ck # 48 6B 10 49 02 02 44 34 47 50 ck # 48 6B 10 49 02 03 30 30 52 35 ck # etc... [] [ Data ] # sort the frames by the order byte frames = sorted(frames, key=lambda f: f.data[2]) # check contiguity indices = [f.data[2] for f in frames] if not contiguous(indices, 1, len(frames)): debug("Recieved multiline response with missing frames") return False # now that they're in order, accumulate the data from each frame for f in frames: message.data += f.data[3:] # loose the mode/pid/seq bytes return True
def parse_message(self, message): frames = message.frames # len(frames) will always be >= 1 (see the caller, protocol.py) mode = frames[0].data[0] # test that all frames are responses to the same Mode (SID) if len(frames) > 1: if not all([mode == f.data[0] for f in frames[1:]]): logger.debug("Recieved frames from multiple commands") return False # legacy protocols have different re-assembly # procedures for different Modes # ~~~~ # NOTE: THERE ARE HACKS IN HERE to make some output compatible with CAN # since CAN is the standard, and this is considered legacy, I'm # fixing ugly inconsistencies between the two protocols here. # ~~~~ if mode == 0x43: # GET_DTC requests return frames with no PID or order bytes # accumulate all of the data, minus the Mode bytes of each frame # Ex. # insert faux-byte to mimic the CAN style DTC requests # | # [ | Frame ] # 48 6B 10 43 03 00 03 02 03 03 ck # 48 6B 10 43 03 04 00 00 00 00 ck # [ Data ] message.data = bytearray([0x43, 0x00]) # forge the mode byte and CAN's DTC_count byte for f in frames: message.data += f.data[1:] else: if len(frames) == 1: # return data, excluding the mode/pid bytes # Ex. # [ Frame/Data ] # 48 6B 10 41 00 BE 7F B8 13 ck message.data = frames[0].data else: # len(frames) > 1: # generic multiline requests carry an order byte # Ex. # [ Frame ] # 48 6B 10 49 02 01 00 00 00 31 ck # 48 6B 10 49 02 02 44 34 47 50 ck # 48 6B 10 49 02 03 30 30 52 35 ck # etc... [] [ Data ] # becomes: # 49 02 [] 00 00 00 31 44 34 47 50 30 30 52 35 # | [ ] [ ] [ ] # order byte is removed # sort the frames by the order byte frames = sorted(frames, key=lambda f: f.data[2]) # check contiguity indices = [f.data[2] for f in frames] if not contiguous(indices, 1, len(frames)): logger.debug("Recieved multiline response with missing frames") return False # now that they're in order, accumulate the data from each frame # preserve the first frame's mode and PID bytes (for consistency with CAN) frames[0].data.pop(2) # remove the sequence byte message.data = frames[0].data # add the data from the remaining frames for f in frames[1:]: message.data += f.data[3:] # loose the mode/pid/seq bytes return True
def create_message(self, frames, tx_id): message = Message(frames, tx_id) if len(message.frames) == 1: frame = frames[0] if frame.type != self.FRAME_TYPE_SF: debug("Recieved lone frame not marked as single frame") return None # extract data, ignore PCI byte and anything after the marked length message.data_bytes = frame.data_bytes[1:1 + frame.data_len] else: # sort FF and CF into their own lists ff = [] cf = [] for f in frames: if f.type == self.FRAME_TYPE_FF: ff.append(f) elif f.type == self.FRAME_TYPE_CF: cf.append(f) else: debug( "Dropping frame in multi-frame response not marked as FF or CF" ) # check that we captured only one first-frame if len(ff) > 1: debug("Recieved multiple frames marked FF") return None elif len(ff) == 0: debug("Never received frame marked FF") return None # check that there was at least one consecutive-frame if len(cf) == 0: debug("Never received frame marked CF") return None # calculate proper sequence indices from the lower 4 bits given for prev, curr in zip(cf, cf[1:]): # Frame sequence numbers only specify the low order bits, so compute the # full sequence number from the frame number and the last sequence number seen: # 1) take the high order bits from the last_sn and low order bits from the frame seq = (prev.seq_index & ~0x0F) + (curr.seq_index) # 2) if this is more than 7 frames away, we probably just wrapped (e.g., # last=0x0F current=0x01 should mean 0x11, not 0x01) if seq < prev.seq_index - 7: # untested seq += 0x10 curr.seq_index = seq # sort the sequence indices cf = sorted(cf, key=lambda f: f.seq_index) # check contiguity indices = [f.seq_index for f in cf] if not contiguous(indices, 1, len(cf)): debug("Recieved multiline response with missing frames") return None # on the first frame, skip PCI byte AND length code message.data_bytes += ff[0].data_bytes[2:] # now that they're in order, load/accumulate the data from each CF frame for f in cf: message.data_bytes += f.data_bytes[1:] # chop off the PCI byte # chop off the Mode/PID bytes based on the mode number mode = message.data_bytes[0] if mode == 0x43: # fetch the DTC count, and use it as a length code num_dtc_bytes = message.data_bytes[1] * 2 # skip the PID byte and the DTC count, message.data_bytes = message.data_bytes[2:][:num_dtc_bytes] else: # handles cases when there is both a Mode and PID byte message.data_bytes = message.data_bytes[2:] return message
def create_message(self, frames, tx_id): message = Message(frames, tx_id) if len(message.frames) == 1: frame = frames[0] if frame.type != self.FRAME_TYPE_SF: debug("Recieved lone frame not marked as single frame") return None # extract data, ignore PCI byte and anything after the marked length message.data_bytes = frame.data_bytes[1:1+frame.data_len] else: # sort FF and CF into their own lists ff = [] cf = [] for f in frames: if f.type == self.FRAME_TYPE_FF: ff.append(f) elif f.type == self.FRAME_TYPE_CF: cf.append(f) else: debug("Dropping frame in multi-frame response not marked as FF or CF") # check that we captured only one first-frame if len(ff) > 1: debug("Recieved multiple frames marked FF") return None elif len(ff) == 0: debug("Never received frame marked FF") return None # check that there was at least one consecutive-frame if len(cf) == 0: debug("Never received frame marked CF") return None # calculate proper sequence indices from the lower 4 bits given for prev, curr in zip(cf, cf[1:]): # Frame sequence numbers only specify the low order bits, so compute the # full sequence number from the frame number and the last sequence number seen: # 1) take the high order bits from the last_sn and low order bits from the frame seq = (prev.seq_index & ~0x0F) + (curr.seq_index) # 2) if this is more than 7 frames away, we probably just wrapped (e.g., # last=0x0F current=0x01 should mean 0x11, not 0x01) if seq < prev.seq_index - 7: # untested seq += 0x10 curr.seq_index = seq # sort the sequence indices cf = sorted(cf, key=lambda f: f.seq_index) # check contiguity indices = [f.seq_index for f in cf] if not contiguous(indices, 1, len(cf)): debug("Recieved multiline response with missing frames") return None # on the first frame, skip PCI byte AND length code message.data_bytes += ff[0].data_bytes[2:] # now that they're in order, load/accumulate the data from each CF frame for f in cf: message.data_bytes += f.data_bytes[1:] # chop off the PCI byte # chop off the Mode/PID bytes based on the mode number mode = message.data_bytes[0] if mode == 0x43: # fetch the DTC count, and use it as a length code num_dtc_bytes = message.data_bytes[1] * 2 # skip the PID byte and the DTC count, message.data_bytes = message.data_bytes[2:][:num_dtc_bytes] else: # handles cases when there is both a Mode and PID byte message.data_bytes = message.data_bytes[2:] return message