ProtocolsCan.__digit()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
1
from obdlib.obd.protocols.base import Base
2
3
4
class ProtocolsCan(Base):
5
    """
6
        Supports the CAN protocol (from 6 ...)
7
    """
8
9
    def __init__(self, number, head=True):
10
        """
11
            :param number - the number of protocol
12
            :param head - flag for init header
13
        """
14
        Base.__init__(self)
15
        self.header = head
16
        self.add_bits = '00000'
17
        # message types, see ELM spec. page 44
18
        self.mess_SF = 0  # the Single Frame
19
        self.mess_FF = 1  # the First Frame (of a multi frame message)
20
        self.mess_CF = 2  # the Consecutive Frame
21
        # The header bits depends on protocol number.
22
        # It uses for CAN protocol only
23
        self.header_bits = self.__get_bits(number)
24
        self.header_11 = 11
25
        self.header_29 = 29
26
        self.frame_start = 10
27
        self.data_start_byte = 4
28
29
    def create_data(self, raw_data):
30
        """
31
            Analyzes raw data
32
            :param raw_data - OBDII response
33
            :return dict
34
        """
35
        data = {}
36
        if raw_data:
37
            ecu_messages = self.remove_searching(raw_data)
38
39
            if self.check_message(ecu_messages):
40
                # if the header enabled
41
                if self.header:
42
                    data = self.process_data(ecu_messages)
43
        return data
44
45
    def process_data(self, ecu_messages):
46
        data = {}
47
        # multi line (ELM spec page 42) or single frame response
48
        self.data_start_byte = 4
49
        # sorts ECU's messages
50
        ecu_messages = sorted(ecu_messages)
51
52
        ecu_messages = self.check_frame(ecu_messages)
53
        for message in ecu_messages:
54
            ecu_number, f_type, response_mode = self.__get_frame_params(message)
55
            # check if response trouble codes
56
            if response_mode == 43:
57
                # add fake byte after the mode one
58
                # nothing to do
59
                self.data_start_byte = 2
60
61
            self.__process(data, message, ecu_number, f_type)
62
        return data
63
64
    def check_frame(self, frame):
65
        if self.check_header():
66
            # align CAN header (11 bits to 29 bits)
67
            # PCI byte are 8 and 9 indexes
68
            frame = self.__align_frame(frame)
69
        return frame
70
71
    def check_header(self):
72
        """
73
            Checks header. If header bits are 11, returns True
74
        """
75
        return self.header_bits == self.header_11
76
77
    def __process(self, data, message, ecu_number, f_type):
78
        # Single Frame
79
        if f_type == self.mess_SF:
80
            # 11 bits header:
81
            # 7E8 06 41 00 FF FF FF FF FC
82
            #
83
            # 29 bits header:
84
            # 18 DA F1 10 06 41 00 FF FF FF FF FC
85
            data[ecu_number] = self.__get_single_data(message)
86
87
        # multi line frame
88
        # the First Frame (of a multi frame message)
89
        #
90
        # 11 bits header:
91
        # [ecu][type][order][        data       ]
92
        # 7E8    1      0   13 49 04 01 35 36 30
93
        # 7E8 21 32 38 39 34 39 41 43
94
        # 7E8 22 00 00 00 00 00 00 31
95
        #
96
        # 29 bits header:
97
        # ........[ecu][type][order][       data       ]
98
        # 18 DA F1 10    1      0   32 38 39 34 39 41 43
99
        # 18 DA F1 10 21 32 38 39 34 39 41 43
100
        # 18 DA F1 10 22 00 00 00 00 00 00 31
101
        elif f_type == self.mess_FF:
102
            data[ecu_number] = message[self.frame_start:]
103
104
        # the Consecutive Frame
105
        elif f_type == self.mess_CF:
106
            data[ecu_number] += message[self.frame_start:]
107
108
    def __get_single_data(self, message):
109
        """
110
            Retrieves data from the single frame
111
            :param message - the OBD frame
112
            :return string
113
        """
114
        return message[self.frame_start:self.__last_bytes(
115
            self.__digit(message[9]))][self.data_start_byte:]
116
117
    def __last_bytes(self, count_byte):
118
        """
119
            Counts the last bytes
120
        """
121
        return self.frame_start + count_byte * 2
122
123
    def __get_frame_params(self, frame):
124
        """
125
            Retrieves some params from the frame
126
            :param frame - the OBD frame
127
            :return tuple of ecu_number, frame_type, response_mode
128
        """
129
        return (
130
            frame[6:8],
131
            self.__digit(frame[8]),
132
            int(frame[10:12])
133
        )
134
135
    def __align_frame(self, frame):
136
        """
137
            Align CAN header (11 bits to 29 bits)
138
            :param frame - the OBD frame
139
        """
140
        return [self.add_bits + mess for mess in frame]
141
142
    @staticmethod
143
    def __digit(value):
144
        """
145
            Hex to int
146
        """
147
        return int(value, 16)
148
149
    def __get_bits(self, n):
150
        """
151
            Retrieves header bits count
152
            :param n - protocol number
153
        """
154
        return self.protocols.get(n)[1] if n else None
155