Command   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Importance

Changes 15
Bugs 0 Features 0
Metric Value
wmc 35
c 15
b 0
f 0
dl 0
loc 157
rs 9

14 Methods

Rating   Name   Duplication   Size   Complexity  
A _process() 0 7 2
A is_pids() 0 14 4
A is_ecus() 0 2 1
B _process_pid() 0 23 4
A __getitem__() 0 12 3
A is_not_access() 0 2 1
A init() 0 7 1
A _set_value() 0 10 4
A ecus() 0 8 2
A sensors() 0 10 4
A get_pid() 0 7 2
A _is_supported() 0 5 1
A check_pids() 0 11 2
A add_pids() 0 6 4
1
from obdlib.elm327 import NO_RESULT
2
from obdlib.obd.modes import Modes
3
from obdlib.logging import logger
4
5
6
class Command(object):
7
    """
8
        This class to provide the common functionality
9
        to make PID's request
10
    """
11
12
    def __init__(self, call_obd, units):
13
        """
14
            Init the common params
15
            :param call_obd: request function
16
            :param units: flag of conversion (0 - Europe, 1 - English,
17
            ex: km/h - > mph)
18
        """
19
        self.__modes = Modes(units)
20
        self.__call = call_obd
21
        self.__decoder = lambda: None
22
        self.title = None
23
        self.description = None
24
        self.pid = None
25
        self.bytes = None
26
        self.value = None
27
        self.unit = None
28
        self.kwargs = None
29
        # list of pids (available or not)
30
        self.__pids = {}
31
        self.__ecus = {}
32
33
    def init(self, args):
34
        """
35
            Unpacks modes params
36
            :param args - mode params
37
        """
38
        self.title, self.description, self.pid, \
39
        self.bytes, self.unit, self.__decoder, self.kwargs = args
40
41
    @property
42
    def ecus(self):
43
        """
44
            Generates available ECU's value
45
            :return tuple (ecu, value)
46
        """
47
        for ecu, value in self.__ecus.items():
48
            yield (ecu, value)
49
50
    def sensors(self, mode=1):
51
        """
52
            Generates available ECU's and PID's value
53
        """
54
        for ecu, pids in self.__pids.items():
55
            for pid, access in pids.items():
56
                if self.is_not_access(access, pid):
57
                    continue
58
                self[mode](pid)
59
                yield self
60
61
    def is_not_access(self, access, pid):
62
        return not access or int(pid, 16) in (0, 32, 64,)
63
64
    def check_pids(self):
65
        """
66
            Checks available PIDs. If return data, it means connected True
67
            Prepares the ELM327 for communicating with vehicle - 01 pid 00
68
        """
69
        self.__pids = {}
70
        pids = self[1]('00')  # 01 00
71
        if self.is_ecus(pids):
72
            self.__pids.update(pids.__ecus)
73
            self.add_pids()
74
            return True
75
76
    def add_pids(self):
77
        for ecu in self.__pids.keys():
78
            if self.__pids[ecu].get('20'):  # add 21-40 pids if available
79
                self.__pids[ecu].update(self[1]('20').__ecus[ecu])
80
            if self.__pids[ecu].get('40'):  # add 41-60 pids if available
81
                self.__pids[ecu].update(self[1]('40').__ecus[ecu])
82
83
    def is_ecus(self, pids):
84
        return pids and isinstance(pids.__ecus, dict) and len(pids.__ecus)
85
86
    def is_pids(self, check=True):
87
        """
88
            Returns True, if some of the PID's are available
89
        """
90
        resp = False
91
        for ecu, pids in self.__pids.items():
92
            items = pids.values()
93
            if isinstance(check, str):
94
                # for check pids
95
                items = pids.keys()
96
            if check in items:
97
                resp = True
98
                break
99
        return resp
100
101
    def _set_value(self, value):
102
        """
103
            Converts (if needed) and sets current value of sensor
104
        """
105
        if NO_RESULT != value and value is not None:
106
            value = self.__decoder(
107
                value,
108
                **self.kwargs) if self.kwargs else self.__decoder(value)
109
110
        return value
111
112
    def _process(self):
113
        """
114
            Decodes OBD data
115
        """
116
        return dict(
117
            [k, self._set_value(v)]
118
            for k, v in self.__call(self.pid).value.items()
119
        )
120
121
    def _process_pid(self, mode, pid):
122
        if not isinstance(pid, str):
123
            raise Exception("PID {} must be a string.".format(pid))
124
125
        # checks unsupported PIDs
126
        if self._is_supported(pid):
127
            raise Exception(
128
                "Unsupported command. {} PID {}".format(
129
                    mode,
130
                    pid))
131
132
        pid = int(pid, 16)
133
        pid_info = self.__modes.modes[mode][pid]
134
135
        if not pid_info:
136
            # if command does not describe in the modes class
137
            raise Exception(
138
                "Unsupported command. {} PID {}".format(
139
                    mode,
140
                    pid))
141
142
        self.init(pid_info)
143
        self.__ecus.update(self._process())
144
145
    def _is_supported(self, pid):
146
        """
147
            Checks an available PID
148
        """
149
        return pid != '00' and not self.is_pids(pid)
150
151
    def __getitem__(self, mode):
152
        self.__ecus = {}
153
154
        def get_pid(pid='00'):
155
            try:
156
                self._process_pid(mode, pid)
157
            except Exception as err:
158
                # logging
159
                logger.error(err)
160
            return self
161
162
        return get_pid
163