2021-10-20 07:05:43 -07:00
|
|
|
#!/usr/bin/env python3
|
2019-11-22 21:46:33 -08:00
|
|
|
import re
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import numbers
|
|
|
|
from collections import namedtuple, defaultdict
|
|
|
|
|
|
|
|
def int_or_float(s):
|
|
|
|
# return number, trying to maintain int format
|
|
|
|
if s.isdigit():
|
|
|
|
return int(s, 10)
|
|
|
|
else:
|
|
|
|
return float(s)
|
|
|
|
|
2020-06-01 02:03:03 -07:00
|
|
|
|
2022-02-24 22:58:37 -08:00
|
|
|
DBCSignal = namedtuple("DBCSignal", ["name", "start_bit", "size", "is_little_endian", "is_signed",
|
|
|
|
"factor", "offset", "tmin", "tmax", "units"])
|
2019-11-22 21:46:33 -08:00
|
|
|
|
|
|
|
|
|
|
|
class dbc():
|
|
|
|
def __init__(self, fn):
|
|
|
|
self.name, _ = os.path.splitext(os.path.basename(fn))
|
|
|
|
with open(fn, encoding="ascii") as f:
|
|
|
|
self.txt = f.readlines()
|
|
|
|
self._warned_addresses = set()
|
|
|
|
|
|
|
|
# regexps from https://github.com/ebroecker/canmatrix/blob/master/canmatrix/importdbc.py
|
|
|
|
bo_regexp = re.compile(r"^BO\_ (\w+) (\w+) *: (\w+) (\w+)")
|
|
|
|
sg_regexp = re.compile(r"^SG\_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*)")
|
|
|
|
sgm_regexp = re.compile(r"^SG\_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*)")
|
|
|
|
val_regexp = re.compile(r"VAL\_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*)")
|
|
|
|
|
|
|
|
# A dictionary which maps message ids to tuples ((name, size), signals).
|
|
|
|
# name is the ASCII name of the message.
|
|
|
|
# size is the size of the message in bytes.
|
|
|
|
# signals is a list signals contained in the message.
|
|
|
|
# signals is a list of DBCSignal in order of increasing start_bit.
|
|
|
|
self.msgs = {}
|
|
|
|
|
|
|
|
# A dictionary which maps message ids to a list of tuples (signal name, definition value pairs)
|
|
|
|
self.def_vals = defaultdict(list)
|
|
|
|
|
|
|
|
# lookup to bit reverse each byte
|
2020-06-01 02:03:03 -07:00
|
|
|
self.bits_index = [(i & ~0b111) + ((-i - 1) & 0b111) for i in range(64)]
|
2019-11-22 21:46:33 -08:00
|
|
|
|
|
|
|
for l in self.txt:
|
|
|
|
l = l.strip()
|
|
|
|
|
|
|
|
if l.startswith("BO_ "):
|
|
|
|
# new group
|
|
|
|
dat = bo_regexp.match(l)
|
|
|
|
|
|
|
|
if dat is None:
|
|
|
|
print("bad BO {0}".format(l))
|
|
|
|
|
|
|
|
name = dat.group(2)
|
|
|
|
size = int(dat.group(3))
|
2020-05-31 00:56:04 -07:00
|
|
|
ids = int(dat.group(1), 0) # could be hex
|
2019-11-22 21:46:33 -08:00
|
|
|
if ids in self.msgs:
|
|
|
|
sys.exit("Duplicate address detected %d %s" % (ids, self.name))
|
|
|
|
|
|
|
|
self.msgs[ids] = ((name, size), [])
|
|
|
|
|
|
|
|
if l.startswith("SG_ "):
|
|
|
|
# new signal
|
|
|
|
dat = sg_regexp.match(l)
|
|
|
|
go = 0
|
|
|
|
if dat is None:
|
|
|
|
dat = sgm_regexp.match(l)
|
|
|
|
go = 1
|
|
|
|
|
|
|
|
if dat is None:
|
|
|
|
print("bad SG {0}".format(l))
|
|
|
|
|
|
|
|
sgname = dat.group(1)
|
2020-05-31 00:33:22 -07:00
|
|
|
start_bit = int(dat.group(go + 2))
|
|
|
|
signal_size = int(dat.group(go + 3))
|
|
|
|
is_little_endian = int(dat.group(go + 4)) == 1
|
|
|
|
is_signed = dat.group(go + 5) == '-'
|
|
|
|
factor = int_or_float(dat.group(go + 6))
|
|
|
|
offset = int_or_float(dat.group(go + 7))
|
|
|
|
tmin = int_or_float(dat.group(go + 8))
|
|
|
|
tmax = int_or_float(dat.group(go + 9))
|
|
|
|
units = dat.group(go + 10)
|
2019-11-22 21:46:33 -08:00
|
|
|
|
|
|
|
self.msgs[ids][1].append(
|
|
|
|
DBCSignal(sgname, start_bit, signal_size, is_little_endian,
|
|
|
|
is_signed, factor, offset, tmin, tmax, units))
|
|
|
|
|
|
|
|
if l.startswith("VAL_ "):
|
|
|
|
# new signal value/definition
|
|
|
|
dat = val_regexp.match(l)
|
|
|
|
|
|
|
|
if dat is None:
|
|
|
|
print("bad VAL {0}".format(l))
|
|
|
|
|
2020-05-31 00:56:04 -07:00
|
|
|
ids = int(dat.group(1), 0) # could be hex
|
2019-11-22 21:46:33 -08:00
|
|
|
sgname = dat.group(2)
|
|
|
|
defvals = dat.group(3)
|
|
|
|
|
2020-05-31 12:48:54 -07:00
|
|
|
defvals = defvals.replace("?", r"\?") # escape sequence in C++
|
2019-11-22 21:46:33 -08:00
|
|
|
defvals = defvals.split('"')[:-1]
|
|
|
|
|
|
|
|
# convert strings to UPPER_CASE_WITH_UNDERSCORES
|
2020-05-31 12:48:54 -07:00
|
|
|
defvals[1::2] = [d.strip().upper().replace(" ", "_") for d in defvals[1::2]]
|
2020-06-01 02:03:03 -07:00
|
|
|
defvals = '"' + "".join(str(i) for i in defvals) + '"'
|
2019-11-22 21:46:33 -08:00
|
|
|
|
|
|
|
self.def_vals[ids].append((sgname, defvals))
|
|
|
|
|
|
|
|
for msg in self.msgs.values():
|
|
|
|
msg[1].sort(key=lambda x: x.start_bit)
|
|
|
|
|
|
|
|
self.msg_name_to_address = {}
|
|
|
|
for address, m in self.msgs.items():
|
|
|
|
name = m[0][0]
|
|
|
|
self.msg_name_to_address[name] = address
|
|
|
|
|
|
|
|
def lookup_msg_id(self, msg_id):
|
|
|
|
if not isinstance(msg_id, numbers.Number):
|
|
|
|
msg_id = self.msg_name_to_address[msg_id]
|
|
|
|
return msg_id
|
|
|
|
|
|
|
|
def get_signals(self, msg):
|
|
|
|
msg = self.lookup_msg_id(msg)
|
|
|
|
return [sgs.name for sgs in self.msgs[msg][1]]
|