You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
67 lines
2.3 KiB
67 lines
2.3 KiB
import math
|
|
|
|
from opendbc.can.dbc import DBC, Signal, SignalType
|
|
|
|
|
|
class CANPacker:
|
|
def __init__(self, dbc_name: str):
|
|
self.dbc = DBC(dbc_name)
|
|
self.counters: dict[int, int] = {}
|
|
|
|
def pack(self, address: int, values: dict[str, float]) -> bytearray:
|
|
msg = self.dbc.addr_to_msg.get(address)
|
|
if msg is None:
|
|
return bytearray()
|
|
dat = bytearray(msg.size)
|
|
counter_set = False
|
|
for name, value in values.items():
|
|
sig = msg.sigs.get(name)
|
|
if sig is None:
|
|
continue
|
|
ival = int(math.floor((value - sig.offset) / sig.factor + 0.5))
|
|
if ival < 0:
|
|
ival = (1 << sig.size) + ival
|
|
set_value(dat, sig, ival)
|
|
if sig.type == SignalType.COUNTER or sig.name == "COUNTER":
|
|
self.counters[address] = int(value)
|
|
counter_set = True
|
|
sig_counter = next((s for s in msg.sigs.values() if s.type == SignalType.COUNTER or s.name == "COUNTER"), None)
|
|
if sig_counter and not counter_set:
|
|
if address not in self.counters:
|
|
self.counters[address] = 0
|
|
set_value(dat, sig_counter, self.counters[address])
|
|
self.counters[address] = (self.counters[address] + 1) % (1 << sig_counter.size)
|
|
sig_checksum = next((s for s in msg.sigs.values() if s.type > SignalType.COUNTER), None)
|
|
if sig_checksum and sig_checksum.calc_checksum:
|
|
checksum = sig_checksum.calc_checksum(address, sig_checksum, dat)
|
|
set_value(dat, sig_checksum, checksum)
|
|
return dat
|
|
|
|
def make_can_msg(self, name_or_addr, bus: int, values: dict[str, float]):
|
|
if isinstance(name_or_addr, int):
|
|
addr = name_or_addr
|
|
else:
|
|
msg = self.dbc.name_to_msg.get(name_or_addr)
|
|
if msg is None:
|
|
return 0, b'', bus
|
|
addr = msg.address
|
|
dat = self.pack(addr, values)
|
|
if len(dat) == 0:
|
|
return 0, b'', bus
|
|
return addr, bytes(dat), bus
|
|
|
|
|
|
def set_value(msg: bytearray, sig: Signal, ival: int) -> None:
|
|
i = sig.lsb // 8
|
|
bits = sig.size
|
|
if sig.size < 64:
|
|
ival &= (1 << sig.size) - 1
|
|
while 0 <= i < len(msg) and bits > 0:
|
|
shift = sig.lsb % 8 if (sig.lsb // 8) == i else 0
|
|
size = min(bits, 8 - shift)
|
|
mask = ((1 << size) - 1) << shift
|
|
msg[i] &= ~mask
|
|
msg[i] |= (ival & ((1 << size) - 1)) << shift
|
|
bits -= size
|
|
ival >>= size
|
|
i = i + 1 if sig.is_little_endian else i - 1
|
|
|