Add CAN inputs for drivetrain display values with DBC and bench evidence
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
"""Send AMI 0x182 display values through a python-can adapter (no motor actuation)."""
|
||||
import argparse
|
||||
from decimal import Decimal, InvalidOperation
|
||||
import os
|
||||
import struct
|
||||
import time
|
||||
|
||||
|
||||
def temperature_raw(value):
|
||||
try:
|
||||
scaled = Decimal(value) * 10
|
||||
if not scaled.is_finite() or scaled != scaled.to_integral_value():
|
||||
raise ValueError('temperature must have at most one decimal place')
|
||||
raw = int(scaled)
|
||||
except (InvalidOperation, OverflowError) as exc:
|
||||
raise ValueError('invalid temperature') from exc
|
||||
if not -32768 <= raw <= 32767:
|
||||
raise ValueError('temperature must be -3276.8..3276.7 degC')
|
||||
return raw
|
||||
|
||||
|
||||
def build_payload(torque, motor_temp, mcu_temp, valid=7):
|
||||
if not -1000 <= torque <= 1000:
|
||||
raise ValueError('torque must be -1000..1000 permille')
|
||||
if not 0 <= valid <= 7:
|
||||
raise ValueError('valid must be 0..7 (bit0 torque, bit1 motor, bit2 MCU)')
|
||||
return struct.pack('<hhhBB', torque, temperature_raw(motor_temp),
|
||||
temperature_raw(mcu_temp), valid, 1)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--torque', type=int, required=True)
|
||||
parser.add_argument('--motor-temp', required=True, help='degC, up to one decimal place')
|
||||
parser.add_argument('--mcu-temp', required=True, help='motor-controller temperature, degC')
|
||||
parser.add_argument('--valid', type=int, default=7, help='validity mask 0..7, default all valid')
|
||||
parser.add_argument('--interface', default='pcan')
|
||||
parser.add_argument('--channel', default='PCAN_USBBUS1')
|
||||
parser.add_argument('--bitrate', type=int, choices=(250000, 500000), default=250000)
|
||||
parser.add_argument('--period', type=float, default=0.1, help='seconds, default 100 ms')
|
||||
parser.add_argument('--duration', type=float, default=10, help='seconds; finite transmission')
|
||||
parser.add_argument('--dll-dir', help='Windows adapter DLL directory, if required')
|
||||
parser.add_argument('--dry-run', action='store_true', help='print payload without opening CAN')
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
payload = build_payload(args.torque, args.motor_temp, args.mcu_temp, args.valid)
|
||||
except ValueError as exc:
|
||||
parser.error(str(exc))
|
||||
if not (0 < args.period <= 2 and 0 < args.duration < float('inf')):
|
||||
parser.error('period must be >0 and <=2 seconds; duration must be positive and finite')
|
||||
print(f'Standard ID 0x182, DLC 8, data {payload.hex(" ").upper()}, bitrate {args.bitrate}', flush=True)
|
||||
if args.dry_run:
|
||||
return
|
||||
dll_handle = None
|
||||
if args.dll_dir:
|
||||
path = os.path.abspath(args.dll_dir)
|
||||
os.environ['PATH'] = path + os.pathsep + os.environ['PATH']
|
||||
if hasattr(os, 'add_dll_directory'):
|
||||
dll_handle = os.add_dll_directory(path)
|
||||
import can
|
||||
count = 0
|
||||
try:
|
||||
with can.Bus(interface=args.interface, channel=args.channel, bitrate=args.bitrate) as bus:
|
||||
message = can.Message(arbitration_id=0x182, is_extended_id=False, data=payload)
|
||||
started = time.monotonic()
|
||||
deadline = started
|
||||
while time.monotonic() < started + args.duration:
|
||||
bus.send(message, timeout=0.2)
|
||||
count += 1
|
||||
deadline += args.period
|
||||
time.sleep(max(0, min(deadline, started + args.duration) - time.monotonic()))
|
||||
finally:
|
||||
if dll_handle is not None:
|
||||
dll_handle.close()
|
||||
print(f'Sent {count} frames. Values expire after 2 seconds without valid input.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user