Skip to content
This repository was archived by the owner on Aug 19, 2026. It is now read-only.

Commit 0cc539f

Browse files
author
Brendan Whitfield
committed
reimplemented status, wrote test
1 parent 6331a7f commit 0cc539f

5 files changed

Lines changed: 131 additions & 50 deletions

File tree

obd/OBDResponse.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,25 @@ def __init__(self):
7777
self.MIL = False
7878
self.DTC_count = 0
7979
self.ignition_type = ""
80-
self.tests = []
80+
81+
# make sure each test is available by name
82+
# until real data comes it. This also prevents things from
83+
# breaking when the user looks up a standard test that's null.
84+
null_test = StatusTest()
85+
for name in BASE_TESTS + SPARK_TESTS + COMPRESSION_TESTS:
86+
if name: # filter out None/reserved tests
87+
self.__dict__[name] = null_test
8188

8289

83-
class Test():
84-
def __init__(self, name, available, incomplete):
85-
self.name = name
86-
self.available = available
87-
self.incomplete = incomplete
90+
class StatusTest():
91+
def __init__(self, name="", available=False, complete=False):
92+
self.name = name
93+
self.available = available
94+
self.complete = complete
8895

8996
def __str__(self):
9097
a = "Available" if self.available else "Unavailable"
91-
c = "Incomplete" if self.incomplete else "Complete"
98+
c = "Complete" if self.complete else "Incomplete"
9299
return "Test %s: %s, %s" % (self.name, a, c)
93100

94101

obd/codes.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2101,30 +2101,36 @@
21012101
}
21022102

21032103
IGNITION_TYPE = [
2104-
"Spark",
2105-
"Compression",
2104+
"spark",
2105+
"compression",
2106+
]
2107+
2108+
BASE_TESTS = [
2109+
"MISFIRE_MONITORING",
2110+
"FUEL_SYSTEM_MONITORING",
2111+
"COMPONENT_MONITORING",
21062112
]
21072113

21082114
SPARK_TESTS = [
2109-
"EGR System",
2110-
"Oxygen Sensor Heater",
2111-
"Oxygen Sensor",
2112-
"A/C Refrigerant",
2113-
"Secondary Air System",
2114-
"Evaporative System",
2115-
"Heated Catalyst",
2116-
"Catalyst",
2115+
"CATALYST_MONITORING",
2116+
"HEATED_CATALYST_MONITORING",
2117+
"EVAPORATIVE_SYSTEM_MONITORING",
2118+
"SECONDARY_AIR_SYSTEM_MONITORING",
2119+
None,
2120+
"OXYGEN_SENSOR_MONITORING",
2121+
"OXYGEN_SENSOR_HEATER_MONITORING",
2122+
"EGR_VVT_SYSTEM_MONITORING"
21172123
]
21182124

21192125
COMPRESSION_TESTS = [
2120-
"EGR and/or VVT System",
2121-
"PM filter monitoring",
2122-
"Exhaust Gas Sensor",
2123-
"None",
2124-
"Boost Pressure",
2125-
"None",
2126-
"NOx/SCR Monitor",
2127-
"NMHC Catalyst",
2126+
"NMHC_CATALYST_MONITORING",
2127+
"NOX_SCR_AFTERTREATMENT_MONITORING",
2128+
None,
2129+
"BOOST_PRESSURE_MONITORING",
2130+
None,
2131+
"EXHAUST_GAS_SENSOR_MONITORING",
2132+
"PM_FILTER_MONITORING",
2133+
"EGR_VVT_SYSTEM_MONITORING",
21282134
]
21292135

21302136
FUEL_STATUS = [

obd/decoders.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import functools
3434
from .utils import *
3535
from .codes import *
36-
from .OBDResponse import Status, Test, Monitor, MonitorTest
36+
from .OBDResponse import Status, StatusTest, Monitor, MonitorTest
3737
from .UnitsAndScaling import Unit, UAS_IDS
3838

3939
import logging
@@ -245,27 +245,39 @@ def status(messages):
245245
d = messages[0].data
246246
bits = bitarray(d)
247247

248+
# ┌Components not ready
249+
# |┌Fuel not ready
250+
# ||┌Misfire not ready
251+
# |||┌Spark vs. Compression
252+
# ||||┌Components supported
253+
# |||||┌Fuel supported
254+
# ┌MIL ||||||┌Misfire supported
255+
# | |||||||
256+
# 10000011 00000111 11111111 00000000
257+
# [# DTC] X [supprt] [~ready]
258+
248259
output = Status()
249-
output.MIL = bits[7]
250-
output.DTC_count = bits[0:7]
260+
output.MIL = bits[0]
261+
output.DTC_count = bits[1:8]
251262
output.ignition_type = IGNITION_TYPE[int(bits[12])]
252263

253-
output.tests.append(Test("Misfire", bits[15], bits[11]))
254-
output.tests.append(Test("Fuel System", bits[14], bits[10]))
255-
output.tests.append(Test("Components", bits[13], bits[9]))
264+
# load the 3 base tests that are always present
265+
for i, name in enumerate(BASE_TESTS[::-1]):
266+
t = StatusTest(name, bits[13 + i], not bits[9 + i])
267+
output.__dict__[name] = t
256268

257269
# different tests for different ignition types
258-
if bits[12]: # ignition type: compression
259-
for i, name in enumerate(COMPRESSION_TESTS):
260-
t = Test(name, bits[(2 * 8) + i],
261-
bits[(3 * 8) + i])
262-
output.tests.append(t)
263-
264-
else: # ignition type: spark
265-
for i, name in enumerate(SPARK_TESTS):
266-
t = Test(name, bits[(2 * 8) + i],
267-
bits[(3 * 8) + i])
268-
output.tests.append(t)
270+
if bits[12]: # compression
271+
for i, name in enumerate(COMPRESSION_TESTS[::-1]): # reverse to correct for bit vs. indexing order
272+
t = StatusTest(name, bits[(2 * 8) + i],
273+
not bits[(3 * 8) + i])
274+
output.__dict__[name] = t
275+
276+
else: # spark
277+
for i, name in enumerate(SPARK_TESTS[::-1]): # reverse to correct for bit vs. indexing order
278+
t = StatusTest(name, bits[(2 * 8) + i],
279+
not bits[(3 * 8) + i])
280+
output.__dict__[name] = t
269281

270282
return output
271283

obd/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class bitarray:
6161

6262
def __init__(self, _bytearray):
6363
self.bits = ""
64-
for b in _bytearray: # put the bytes in bit-number order
64+
for b in _bytearray:
6565
v = bin(b)[2:]
6666
self.bits += ("0" * (8 - len(v))) + v # pad it with zeros
6767

@@ -72,7 +72,7 @@ def __getitem__(self, key):
7272
else:
7373
return False
7474
elif isinstance(key, slice):
75-
bits = self.bits[key] # reverse back into correct bit-order
75+
bits = self.bits[key]
7676
if bits:
7777
return int(bits, 2)
7878
else:

tests/test_decoders.py

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from obd.UnitsAndScaling import Unit
55
from obd.protocols.protocol import Frame, Message
6-
from obd.codes import TEST_IDS
6+
from obd.codes import BASE_TESTS, COMPRESSION_TESTS, SPARK_TESTS, TEST_IDS
77
import obd.decoders as d
88

99

@@ -41,9 +41,9 @@ def test_raw_string():
4141
assert d.raw_string([ Message([ Frame("A") ]), Message([ Frame("B") ]) ]) == "A\nB"
4242

4343
def test_pid():
44-
assert d.pid(m("00000000")) == "00000000000000000000000000000000"
45-
assert d.pid(m("F00AA00F")) == "11110000000010101010000000001111"
46-
assert d.pid(m("11")) == "00010001"
44+
assert d.pid(m("00000000")).bits == "00000000000000000000000000000000"
45+
assert d.pid(m("F00AA00F")).bits == "11110000000010101010000000001111"
46+
assert d.pid(m("11")).bits == "00010001"
4747

4848
def test_percent():
4949
assert d.percent(m("00")) == 0.0 * Unit.percent
@@ -147,13 +147,69 @@ def test_elm_voltage():
147147
assert d.elm_voltage([ Message([ Frame("12ABCD") ]) ]) == None
148148

149149
def test_status():
150-
status = d.status(m("83E0FF00"))
150+
status = d.status(m("8307FF00"))
151151
assert status.MIL
152152
assert status.DTC_count == 3
153+
assert status.ignition_type == "spark"
154+
155+
for name in BASE_TESTS:
156+
assert status.__dict__[name].available
157+
assert status.__dict__[name].complete
158+
159+
# check that NONE of the compression tests are available
160+
for name in COMPRESSION_TESTS:
161+
if name and name not in SPARK_TESTS: # there's one test name in common between spark/compression
162+
assert not status.__dict__[name].available
163+
assert not status.__dict__[name].complete
164+
165+
# check that ALL of the spark tests are availablex
166+
for name in SPARK_TESTS:
167+
if name:
168+
assert status.__dict__[name].available
169+
assert status.__dict__[name].complete
170+
171+
# a different test
172+
status = d.status(m("00790303"))
173+
assert not status.MIL
174+
assert status.DTC_count == 0
175+
assert status.ignition_type == "compression"
176+
177+
# availability
178+
assert status.MISFIRE_MONITORING.available
179+
assert not status.FUEL_SYSTEM_MONITORING.available
180+
assert not status.COMPONENT_MONITORING.available
181+
182+
# completion
183+
assert not status.MISFIRE_MONITORING.complete
184+
assert not status.FUEL_SYSTEM_MONITORING.complete
185+
assert not status.COMPONENT_MONITORING.complete
186+
187+
# check that NONE of the spark tests are availablex
188+
for name in SPARK_TESTS:
189+
if name and name not in COMPRESSION_TESTS:
190+
assert not status.__dict__[name].available
191+
assert not status.__dict__[name].complete
192+
193+
# availability
194+
assert status.NMHC_CATALYST_MONITORING.available
195+
assert status.NOX_SCR_AFTERTREATMENT_MONITORING.available
196+
assert not status.BOOST_PRESSURE_MONITORING.available
197+
assert not status.EXHAUST_GAS_SENSOR_MONITORING.available
198+
assert not status.PM_FILTER_MONITORING.available
199+
assert not status.EGR_VVT_SYSTEM_MONITORING.available
200+
201+
# completion
202+
assert not status.NMHC_CATALYST_MONITORING.complete
203+
assert not status.NOX_SCR_AFTERTREATMENT_MONITORING.complete
204+
assert status.BOOST_PRESSURE_MONITORING.complete
205+
assert status.EXHAUST_GAS_SENSOR_MONITORING.complete
206+
assert status.PM_FILTER_MONITORING.complete
207+
assert status.EGR_VVT_SYSTEM_MONITORING.complete
208+
153209

154210
def test_single_dtc():
155211
assert d.single_dtc(m("0104")) == ("P0104", "Mass or Volume Air Flow Circuit Intermittent")
156-
assert d.single_dtc(m("4123")) == ("C0123", "")
212+
assert d.single_dtc(m("4123")) == ("C0123", "") # reverse back into correct bit-order
157213
assert d.single_dtc(m("01")) == None
158214
assert d.single_dtc(m("010400")) == None
159215

0 commit comments

Comments
 (0)