|
|
|
#!/usr/bin/env python
|
|
|
|
import os
|
|
|
|
import glob
|
|
|
|
import sys
|
|
|
|
|
|
|
|
import jinja2
|
|
|
|
|
|
|
|
from collections import Counter
|
|
|
|
from common.dbc import dbc
|
|
|
|
|
|
|
|
def main():
|
|
|
|
if len(sys.argv) != 3:
|
|
|
|
print "usage: %s dbc_directory output_directory" % (sys.argv[0],)
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
dbc_dir = sys.argv[1]
|
|
|
|
out_dir = sys.argv[2]
|
|
|
|
|
|
|
|
template_fn = os.path.join(os.path.dirname(__file__), "dbc_template.cc")
|
|
|
|
template_mtime = os.path.getmtime(template_fn)
|
|
|
|
|
|
|
|
this_file_mtime = os.path.getmtime(__file__)
|
|
|
|
|
|
|
|
with open(template_fn, "r") as template_f:
|
|
|
|
template = jinja2.Template(template_f.read(), trim_blocks=True, lstrip_blocks=True)
|
|
|
|
|
|
|
|
for dbc_path in glob.iglob(os.path.join(dbc_dir, "*.dbc")):
|
|
|
|
dbc_mtime = os.path.getmtime(dbc_path)
|
|
|
|
dbc_fn = os.path.split(dbc_path)[1]
|
|
|
|
dbc_name = os.path.splitext(dbc_fn)[0]
|
|
|
|
can_dbc = dbc(dbc_path)
|
|
|
|
out_fn = os.path.join(os.path.dirname(__file__), out_dir, dbc_name + ".cc")
|
|
|
|
if os.path.exists(out_fn):
|
|
|
|
out_mtime = os.path.getmtime(out_fn)
|
|
|
|
else:
|
|
|
|
out_mtime = 0
|
|
|
|
|
|
|
|
if dbc_mtime < out_mtime and template_mtime < out_mtime and this_file_mtime < out_mtime:
|
|
|
|
continue #skip output is newer than template and dbc
|
|
|
|
|
|
|
|
msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in ("COUNTER", "CHECKSUM"))) # process counter and checksums first
|
getting ready for Python 3 (#619)
* tabs to spaces
python 2 to 3: https://portingguide.readthedocs.io/en/latest/syntax.html#tabs-and-spaces
* use the new except syntax
python 2 to 3: https://portingguide.readthedocs.io/en/latest/exceptions.html#the-new-except-syntax
* make relative imports absolute
python 2 to 3: https://portingguide.readthedocs.io/en/latest/imports.html#absolute-imports
* Queue renamed to queue in python 3
Use the six compatibility library to support both python 2 and 3: https://portingguide.readthedocs.io/en/latest/stdlib-reorg.html#renamed-modules
* replace dict.has_key() with in
python 2 to 3: https://portingguide.readthedocs.io/en/latest/dicts.html#removed-dict-has-key
* make dict views compatible with python 3
python 2 to 3: https://portingguide.readthedocs.io/en/latest/dicts.html#dict-views-and-iterators
Where needed, wrapping things that will be a view in python 3 with a list(). For example, if it's accessed with []
Python 3 has no iter*() methods, so just using the values() instead of itervalues() as long as it's not too performance intensive. Note that any minor performance hit of using a list instead of a view will go away when switching to python 3. If it is intensive, we could use the six version.
* Explicitly use truncating division
python 2 to 3: https://portingguide.readthedocs.io/en/latest/numbers.html#division
python 3 treats / as float division. When we want the result to be an integer, use //
* replace map() with list comprehension where a list result is needed.
In python 3, map() returns an iterator.
python 2 to 3: https://portingguide.readthedocs.io/en/latest/iterators.html#new-behavior-of-map-and-filter
* replace filter() with list comprehension
In python 3, filter() returns an interatoooooooooooor.
python 2 to 3: https://portingguide.readthedocs.io/en/latest/iterators.html#new-behavior-of-map-and-filter
* wrap zip() in list() where we need the result to be a list
python 2 to 3: https://portingguide.readthedocs.io/en/latest/iterators.html#new-behavior-of-zip
* clean out some lint
Removes these pylint warnings:
************* Module selfdrive.car.chrysler.chryslercan
W: 15, 0: Unnecessary semicolon (unnecessary-semicolon)
W: 16, 0: Unnecessary semicolon (unnecessary-semicolon)
W: 25, 0: Unnecessary semicolon (unnecessary-semicolon)
************* Module common.dbc
W:101, 0: Anomalous backslash in string: '\?'. String constant might be missing an r prefix. (anomalous-backslash-in-string)
************* Module selfdrive.car.gm.interface
R:102, 6: Redefinition of ret.minEnableSpeed type from float to int (redefined-variable-type)
R:103, 6: Redefinition of ret.mass type from int to float (redefined-variable-type)
************* Module selfdrive.updated
R: 20, 6: Redefinition of r type from int to str (redefined-variable-type)
6 years ago
|
|
|
for address, ((msg_name, msg_size), msg_sigs) in sorted(can_dbc.msgs.items()) if msg_sigs]
|
|
|
|
|
|
|
|
def_vals = {a: set(b) for a,b in can_dbc.def_vals.items()} #remove duplicates
|
getting ready for Python 3 (#619)
* tabs to spaces
python 2 to 3: https://portingguide.readthedocs.io/en/latest/syntax.html#tabs-and-spaces
* use the new except syntax
python 2 to 3: https://portingguide.readthedocs.io/en/latest/exceptions.html#the-new-except-syntax
* make relative imports absolute
python 2 to 3: https://portingguide.readthedocs.io/en/latest/imports.html#absolute-imports
* Queue renamed to queue in python 3
Use the six compatibility library to support both python 2 and 3: https://portingguide.readthedocs.io/en/latest/stdlib-reorg.html#renamed-modules
* replace dict.has_key() with in
python 2 to 3: https://portingguide.readthedocs.io/en/latest/dicts.html#removed-dict-has-key
* make dict views compatible with python 3
python 2 to 3: https://portingguide.readthedocs.io/en/latest/dicts.html#dict-views-and-iterators
Where needed, wrapping things that will be a view in python 3 with a list(). For example, if it's accessed with []
Python 3 has no iter*() methods, so just using the values() instead of itervalues() as long as it's not too performance intensive. Note that any minor performance hit of using a list instead of a view will go away when switching to python 3. If it is intensive, we could use the six version.
* Explicitly use truncating division
python 2 to 3: https://portingguide.readthedocs.io/en/latest/numbers.html#division
python 3 treats / as float division. When we want the result to be an integer, use //
* replace map() with list comprehension where a list result is needed.
In python 3, map() returns an iterator.
python 2 to 3: https://portingguide.readthedocs.io/en/latest/iterators.html#new-behavior-of-map-and-filter
* replace filter() with list comprehension
In python 3, filter() returns an interatoooooooooooor.
python 2 to 3: https://portingguide.readthedocs.io/en/latest/iterators.html#new-behavior-of-map-and-filter
* wrap zip() in list() where we need the result to be a list
python 2 to 3: https://portingguide.readthedocs.io/en/latest/iterators.html#new-behavior-of-zip
* clean out some lint
Removes these pylint warnings:
************* Module selfdrive.car.chrysler.chryslercan
W: 15, 0: Unnecessary semicolon (unnecessary-semicolon)
W: 16, 0: Unnecessary semicolon (unnecessary-semicolon)
W: 25, 0: Unnecessary semicolon (unnecessary-semicolon)
************* Module common.dbc
W:101, 0: Anomalous backslash in string: '\?'. String constant might be missing an r prefix. (anomalous-backslash-in-string)
************* Module selfdrive.car.gm.interface
R:102, 6: Redefinition of ret.minEnableSpeed type from float to int (redefined-variable-type)
R:103, 6: Redefinition of ret.mass type from int to float (redefined-variable-type)
************* Module selfdrive.updated
R: 20, 6: Redefinition of r type from int to str (redefined-variable-type)
6 years ago
|
|
|
def_vals = [(address, sig) for address, sig in sorted(def_vals.items())]
|
|
|
|
|
|
|
|
if can_dbc.name.startswith("honda") or can_dbc.name.startswith("acura"):
|
|
|
|
checksum_type = "honda"
|
|
|
|
checksum_size = 4
|
|
|
|
elif can_dbc.name.startswith("toyota") or can_dbc.name.startswith("lexus"):
|
|
|
|
checksum_type = "toyota"
|
|
|
|
checksum_size = 8
|
|
|
|
else:
|
|
|
|
checksum_type = None
|
|
|
|
|
|
|
|
for address, msg_name, msg_size, sigs in msgs:
|
|
|
|
for sig in sigs:
|
|
|
|
if checksum_type is not None and sig.name == "CHECKSUM":
|
|
|
|
if sig.size != checksum_size:
|
|
|
|
sys.exit("CHECKSUM is not %d bits longs %s" % (checksum_size, msg_name))
|
|
|
|
if checksum_type == "honda" and sig.start_bit % 8 != 3:
|
|
|
|
sys.exit("CHECKSUM starts at wrong bit %s" % msg_name)
|
|
|
|
if checksum_type == "toyota" and sig.start_bit % 8 != 7:
|
|
|
|
sys.exit("CHECKSUM starts at wrong bit %s" % msg_name)
|
|
|
|
if checksum_type == "honda" and sig.name == "COUNTER":
|
|
|
|
if sig.size != 2:
|
|
|
|
sys.exit("COUNTER is not 2 bits longs %s" % msg_name)
|
|
|
|
if sig.start_bit % 8 != 5:
|
|
|
|
sys.exit("COUNTER starts at wrong bit %s" % msg_name)
|
|
|
|
if address in [0x200, 0x201]:
|
|
|
|
if sig.name == "COUNTER_PEDAL" and sig.size != 4:
|
|
|
|
sys.exit("PEDAL COUNTER is not 4 bits longs %s" % msg_name)
|
|
|
|
if sig.name == "CHECKSUM_PEDAL" and sig.size != 8:
|
|
|
|
sys.exit("PEDAL CHECKSUM is not 8 bits longs %s" % msg_name)
|
|
|
|
|
|
|
|
# Fail on duplicate message names
|
|
|
|
c = Counter([msg_name for address, msg_name, msg_size, sigs in msgs])
|
|
|
|
for name, count in c.items():
|
|
|
|
if count > 1:
|
|
|
|
sys.exit("Duplicate message name in DBC file %s" % name)
|
|
|
|
|
|
|
|
parser_code = template.render(dbc=can_dbc, checksum_type=checksum_type, msgs=msgs, def_vals=def_vals, len=len)
|
|
|
|
|
|
|
|
|
|
|
|
with open(out_fn, "w") as out_f:
|
|
|
|
out_f.write(parser_code)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|