Protocols   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 2
Metric Value
wmc 20
c 5
b 0
f 2
dl 0
loc 123
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A _get_multi_data() 0 5 1
A _get_frame_params() 0 10 1
A _process() 0 23 3
A _parse_headers() 0 15 3
A _add_additional_byte() 0 6 1
A get_data() 0 13 3
A process_data() 0 19 3
A __init__() 0 4 1
A create_data() 0 14 4
1
from obdlib.obd.protocols.base import Base
2
from obdlib.logging import logger
3
4
5
class Protocols(Base):
6
    """
7
        Supports next protocols - PWM, VPW, KWP (from 0 to 5)
8
    """
9
10
    def __init__(self, head=True):
11
        Base.__init__(self)
12
        self.header = head
13
        self.check_sum = -2
14
15
    def create_data(self, raw_data):
16
        """
17
            Analyzes raw data
18
            :param raw_data - OBDII response
19
            :return dict
20
        """
21
        data = {}
22
        if raw_data:
23
            ecu_messages = self.remove_searching(raw_data)
24
25
            if self.check_message(ecu_messages):
26
                if self.header:
27
                    data = self.process_data(ecu_messages)
28
        return data
29
30
    def process_data(self, ecu_messages):
31
        data = {}
32
        self.check_sum = -2
33
        # sorts ECU's messages
34
        ecu_messages = sorted(ecu_messages)
35
        service_data = self._parse_headers(ecu_messages)
36
        for message in ecu_messages:
37
            # if one ECU returns multi line
38
            # multi line includes line number byte
39
            ecu_number, response_mode = self._get_frame_params(message)
40
41
            # check if response trouble codes
42
            if response_mode == 43:
43
                # add fake byte after the mode one
44
                message = self._add_additional_byte(message)
45
                self.check_sum = None
46
47
            self._process(data, message, ecu_number, service_data[ecu_number])
48
        return data
49
50
    def _process(self, data, message, ecu_number, multi):
51
        if multi > 1:
52
            # multi line response - ELM spec page 42
53
            # response format priority:receiver:transmitter:mode:pid:line_number:data:checksum
54
            # >0902 - (ex: get VIN)
55
            # 86 F1 10 49 02 01 00 00 00 31 FC
56
            # 86 F1 10 49 02 02 44 34 47 50 FC
57
            # 86 F1 10 49 02 03 30 30 52 35 FC
58
            # 86 F1 10 49 02 04 35 42 31 32 FC
59
            # 86 F1 10 49 02 05 33 34 35 36 FC
60
            # 6 * 2: means that we are removed
61
            # "mode:pid:line" info from the record
62
            try:
63
                data[ecu_number] += self._get_multi_data(message)
64
            except KeyError:
65
                data[ecu_number] = self._get_multi_data(message)
66
        # frame without line number byte
67
        # removes header and checksum
68
        # format priority:receiver:transmitter:mode:pid:data:checksum
69
        # [ header][serv][   data   ][CS]
70
        # 86 F1 10 41 00 FF FF FF FF FC  - ELM spec page 38
71
        else:
72
            data[ecu_number] = self.get_data(message)
73
74
    def _get_multi_data(self, record):
75
        """
76
            Retrieves the necessary content
77
        """
78
        return record[12:self.check_sum]
79
80
    @staticmethod
81
    def _get_frame_params(frame):
82
        """
83
            Retrieves some params from the frame
84
            :param frame - the OBD frame
85
            :return tuple of ecu_number, response_mode
86
        """
87
        return (
88
            frame[4:6],
89
            int(frame[6:8])
90
        )
91
92
    @staticmethod
93
    def _add_additional_byte(record):
94
        """
95
            Adds fake byte after the mode one (byte is 00)
96
        """
97
        return record[:8] + '00' + record[8:]
98
99
    @staticmethod
100
    def _parse_headers(frames):
101
        """
102
            Collects info, how many times ECU met
103
            :param frames - OBDII response
104
            :return dict
105
        """
106
        ecu_headers = {}
107
        for mess in frames:
108
            try:
109
                ecu_headers[mess[4:6]] += 1
110
            except KeyError:
111
                ecu_headers[mess[4:6]] = 1
112
113
        return ecu_headers
114
115
    def get_data(self, record):
116
        record = record[6:self.check_sum]
117
        if len(record) >= 6 and len(record) <= 16:
118
            # remove first 4 characters. This are service bytes from ELM
119
            # format mode:pid:data
120
            # ex: 4100FFFFFFFF - ELM spec page 38
121
            record = record[4:]
122
        else:
123
            mess = "The frame size is not suitable."
124
            logger.error(mess)
125
            raise Exception(mess)
126
127
        return record
128