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.
176 lines
4.7 KiB
176 lines
4.7 KiB
# distutils: language = c++
|
|
# cython: c_string_encoding=ascii, language_level=3
|
|
|
|
from libcpp.pair cimport pair
|
|
from libcpp.string cimport string
|
|
from libcpp.vector cimport vector
|
|
from libc.stdint cimport uint32_t, int
|
|
|
|
from .common cimport CANParser as cpp_CANParser
|
|
from .common cimport dbc_lookup, Msg, DBC, CanData
|
|
|
|
import numbers
|
|
from collections import defaultdict
|
|
|
|
|
|
cdef class CANParser:
|
|
cdef:
|
|
cpp_CANParser *can
|
|
const DBC *dbc
|
|
set addresses
|
|
|
|
cdef readonly:
|
|
dict vl
|
|
dict vl_all
|
|
dict ts_nanos
|
|
string dbc_name
|
|
uint32_t bus
|
|
|
|
def __init__(self, dbc_name, messages, bus=0):
|
|
self.dbc_name = dbc_name
|
|
self.bus = bus
|
|
self.dbc = dbc_lookup(dbc_name)
|
|
if not self.dbc:
|
|
raise RuntimeError(f"Can't find DBC: {dbc_name}")
|
|
|
|
self.vl = {}
|
|
self.vl_all = {}
|
|
self.ts_nanos = {}
|
|
self.addresses = set()
|
|
|
|
# Convert message names into addresses and check existence in DBC
|
|
cdef vector[pair[uint32_t, int]] message_v
|
|
for i in range(len(messages)):
|
|
c = messages[i]
|
|
try:
|
|
m = self.dbc.addr_to_msg.at(c[0]) if isinstance(c[0], numbers.Number) else self.dbc.name_to_msg.at(c[0])
|
|
except IndexError:
|
|
raise RuntimeError(f"could not find message {repr(c[0])} in DBC {self.dbc_name}")
|
|
|
|
address = m.address
|
|
message_v.push_back((address, c[1]))
|
|
self.addresses.add(address)
|
|
|
|
name = m.name.decode("utf8")
|
|
signal_names = [sig.name.decode("utf-8") for sig in (<Msg*>m).sigs]
|
|
|
|
self.vl[address] = {name: 0.0 for name in signal_names}
|
|
self.vl[name] = self.vl[address]
|
|
self.vl_all[address] = defaultdict(list)
|
|
self.vl_all[name] = self.vl_all[address]
|
|
self.ts_nanos[address] = {name: 0.0 for name in signal_names}
|
|
self.ts_nanos[name] = self.ts_nanos[address]
|
|
|
|
cdef string cpp_dbc_name
|
|
if isinstance(dbc_name, str):
|
|
cpp_dbc_name = (<str>dbc_name).encode('utf-8')
|
|
else:
|
|
cpp_dbc_name = dbc_name # Assume bytes
|
|
cdef int cpp_bus = bus
|
|
with nogil:
|
|
self.can = new cpp_CANParser(cpp_bus, cpp_dbc_name, message_v)
|
|
|
|
def __dealloc__(self):
|
|
if self.can:
|
|
with nogil:
|
|
del self.can
|
|
|
|
def update_strings(self, strings, sendcan=False):
|
|
# input format:
|
|
# [nanos, [[address, data, src], ...]]
|
|
# [[nanos, [[address, data, src], ...], ...]]
|
|
for address in self.addresses:
|
|
self.vl_all[address].clear()
|
|
|
|
cdef vector[CanData] can_data_array
|
|
|
|
try:
|
|
if len(strings) and not isinstance(strings[0], (list, tuple)):
|
|
strings = [strings]
|
|
|
|
can_data_array.reserve(len(strings))
|
|
for s in strings:
|
|
can_data = &(can_data_array.emplace_back())
|
|
can_data.nanos = s[0]
|
|
can_data.frames.reserve(len(s[1]))
|
|
for address, dat, src in s[1]:
|
|
source_bus = <uint32_t>src
|
|
if source_bus == self.bus:
|
|
frame = &(can_data.frames.emplace_back())
|
|
frame.address = address
|
|
frame.dat = dat
|
|
frame.src = source_bus
|
|
except TypeError:
|
|
raise RuntimeError("invalid parameter")
|
|
|
|
with nogil:
|
|
updated_addrs = self.can.update(can_data_array)
|
|
|
|
for addr in updated_addrs:
|
|
vl = self.vl[addr]
|
|
vl_all = self.vl_all[addr]
|
|
ts_nanos = self.ts_nanos[addr]
|
|
|
|
with nogil:
|
|
state = self.can.getMessageState(addr)
|
|
for i in range(state.parse_sigs.size()):
|
|
name = <unicode>state.parse_sigs[i].name
|
|
vl[name] = state.vals[i]
|
|
vl_all[name] = state.all_vals[i]
|
|
ts_nanos[name] = state.last_seen_nanos
|
|
|
|
return updated_addrs
|
|
|
|
@property
|
|
def can_valid(self):
|
|
cdef bint valid
|
|
with nogil:
|
|
valid = self.can.can_valid
|
|
return valid
|
|
|
|
@property
|
|
def bus_timeout(self):
|
|
cdef bint timeout
|
|
with nogil:
|
|
timeout = self.can.bus_timeout
|
|
return timeout
|
|
|
|
|
|
cdef class CANDefine():
|
|
cdef:
|
|
const DBC *dbc
|
|
|
|
cdef public:
|
|
dict dv
|
|
string dbc_name
|
|
|
|
def __init__(self, dbc_name):
|
|
self.dbc_name = dbc_name
|
|
self.dbc = dbc_lookup(dbc_name)
|
|
if not self.dbc:
|
|
raise RuntimeError(f"Can't find DBC: '{dbc_name}'")
|
|
|
|
dv = defaultdict(dict)
|
|
|
|
for i in range(self.dbc[0].vals.size()):
|
|
val = self.dbc[0].vals[i]
|
|
|
|
sgname = val.name.decode("utf8")
|
|
def_val = val.def_val.decode("utf8")
|
|
address = val.address
|
|
try:
|
|
m = self.dbc.addr_to_msg.at(address)
|
|
except IndexError:
|
|
raise KeyError(address)
|
|
msgname = m.name.decode("utf-8")
|
|
|
|
# separate definition/value pairs
|
|
def_val = def_val.split()
|
|
values = [int(v) for v in def_val[::2]]
|
|
defs = def_val[1::2]
|
|
|
|
# two ways to lookup: address or msg name
|
|
dv[address][sgname] = dict(zip(values, defs))
|
|
dv[msgname][sgname] = dv[address][sgname]
|
|
|
|
self.dv = dict(dv)
|
|
|