brendan-w / python-OBD

OBD-II serial module for reading engine data
GNU General Public License v2.0
1.06k stars 370 forks source link

revise __repr__ to produce a working function #147

Closed Ircama closed 5 years ago

Ircama commented 5 years ago

This is a small enhancement to __repr__ to produce a working function. Notice anyway that the decoding function is fixed to raw_string and needs from obd.decoders import raw_string to be tested.

Test program:

import obd
from obd import OBDCommand, Unit
from obd.protocols import ECU
from obd.decoders import raw_string

# We will now define a custom PID to allow testing repr of OBDCommand
# for both standard and custom PIDs.

# This is an example of custom decoding function (A*256*256+B*256+C)
# for custom PID 7E2-2128 ("Total Distance Traveled") of Hybrid Toyota cars
def ABC(messages): # integer value of the first, second and third byte
    try:
        return(int("\n".join([n.raw() for n in messages])[9:15], 16))
    except:
        return None

# This is the custom PID 7E2-2128 taken from a CSV file for Torque (https://torque-bhp.com/wiki/PIDs)
# Custom PID: Total Distance Traveled,ODO,2128,A*256*256+B*256+C,0,16777215,km,7E2
odo = OBDCommand(
        "Odometer",
        "Total Distance Traveled",
        b"2128",
        5,
        lambda m: None if ABC(m) == None else Unit.Quantity(ABC(m), Unit.kilometer),
        ECU.ALL,
        True,
        header=b'7E2'
        )

Output:

Before:

>>> odo
OBDCommand(Odometer, b'2128')
>>> obd.commands.SPEED
OBDCommand(SPEED, b'010D')

Short description and PID are shown. Header for instance is not displayed. There might be commands with same PID and different headers.

If you test the obtained commands (e.g., eval(repr(obd.commands.SPEED))), they will not work:

>>> eval(repr(odo))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'Odometer' is not defined
>>> eval(repr(obd.commands.SPEED))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'SPEED' is not defined

After:

>>> odo
OBDCommand('Odometer', 'Total Distance Traveled', b'2128', 5, raw_string, ecu=ECU.ALL, fast=True, header=b'7E2')
>>> obd.commands.SPEED
OBDCommand('SPEED', 'Vehicle Speed', b'010D', 3, raw_string, ecu=ECU.ENGINE, fast=True)

The two above OBDCommand functions can be directly tested:

a=eval(repr(odo))
b=eval(repr(obd.commands.SPEED))
>>> a
OBDCommand('Odometer', 'Total Distance Traveled', b'2128', 5, raw_string, ecu=ECU.ALL, fast=True, header=b'7E2')
>>> b
OBDCommand('SPEED', 'Vehicle Speed', b'010D', 3, raw_string, ecu=ECU.ENGINE, fast=True)

Notice anyway that the existing decoding function is always substituted by the raw_string one.

Now we will test the obtained functions. The resulting test works:

connection = obd.OBD('/dev/USB0', fast=False)
connection.query(a, force=True).value
[obd.elm327] write: b'AT SH 7E2 \r'
[obd.elm327] read: b'OK\r\r>'
[obd.obd] Sending command: b'7E22128': Total Distance Traveled
[obd.elm327] write: b'2128\r'
[obd.elm327] read: b'7EA 05 61 28 00 EA 5C \r\r>'
'7EA05612800EA5C'
>>> connection.query(b, force=True).value
[obd.elm327] write: b'AT SH 7E0 \r'
[obd.elm327] read: b'OK\r\r>'
[obd.obd] Sending command: b'010D': Vehicle Speed
[obd.elm327] write: b'010D\r'
[obd.elm327] read: b'7E8 03 41 0D 0A \r\r>'
'7E803410D0A'

The produced output is in raw_string format at the moment.

Ircama commented 5 years ago

Can you add a comment describing what is happening? I think it is a little confusing if just reading the code.

I edited the description

Also if possible can we avoid a one line if statement?

OK I'll revise the return statement.