mirror of
https://github.com/infiniteCable2/opendbc.git
synced 2026-06-08 10:54:51 +08:00
try no scons (#3194)
* try no scons * lil more * lil more * lazy * fix ty
This commit is contained in:
5
.github/workflows/tests.yml
vendored
5
.github/workflows/tests.yml
vendored
@@ -49,7 +49,6 @@ jobs:
|
|||||||
- name: Run mutation tests
|
- name: Run mutation tests
|
||||||
run: |
|
run: |
|
||||||
source setup.sh
|
source setup.sh
|
||||||
scons -j8
|
|
||||||
python opendbc/safety/tests/mutation.py
|
python opendbc/safety/tests/mutation.py
|
||||||
|
|
||||||
car_diff:
|
car_diff:
|
||||||
@@ -61,10 +60,6 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Build opendbc
|
|
||||||
run: |
|
|
||||||
source setup.sh
|
|
||||||
scons -j8
|
|
||||||
- name: Test car diff
|
- name: Test car diff
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
run: source setup.sh && python opendbc/car/tests/car_diff.py | tee diff.txt
|
run: source setup.sh && python opendbc/car/tests/car_diff.py | tee diff.txt
|
||||||
|
|||||||
1
.github/workflows/update-cars-docs.yml
vendored
1
.github/workflows/update-cars-docs.yml
vendored
@@ -15,7 +15,6 @@ jobs:
|
|||||||
- name: Generate Car Docs
|
- name: Generate Car Docs
|
||||||
run: |
|
run: |
|
||||||
pip install -e .
|
pip install -e .
|
||||||
scons -c && scons -j$(nproc)
|
|
||||||
python -m pip install jinja2==3.1.4
|
python -m pip install jinja2==3.1.4
|
||||||
python opendbc/car/docs.py
|
python opendbc/car/docs.py
|
||||||
- uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842
|
- uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -9,7 +9,6 @@
|
|||||||
*.dylib
|
*.dylib
|
||||||
.*.swp
|
.*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.sconsign.dblite
|
|
||||||
.hypothesis
|
.hypothesis
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
*.html
|
*.html
|
||||||
@@ -21,14 +20,10 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.profraw
|
*.profraw
|
||||||
.sconf_temp/
|
|
||||||
|
|
||||||
opendbc/can/build/
|
opendbc/can/build/
|
||||||
opendbc/can/obj/
|
opendbc/can/obj/
|
||||||
opendbc/dbc/*_generated.dbc
|
|
||||||
|
|
||||||
cppcheck-addon-ctu-file-list
|
cppcheck-addon-ctu-file-list
|
||||||
opendbc/safety/tests/coverage-out
|
opendbc/safety/tests/coverage-out
|
||||||
|
|
||||||
compile_commands.json
|
|
||||||
.mull/
|
.mull/
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
SConscript(['opendbc/dbc/SConscript'])
|
|
||||||
|
|
||||||
# test files
|
|
||||||
if GetOption('extras'):
|
|
||||||
SConscript('opendbc/safety/tests/libsafety/SConscript')
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
AddOption('--minimal',
|
|
||||||
action='store_false',
|
|
||||||
dest='extras',
|
|
||||||
default=True,
|
|
||||||
help='the minimum build. no tests, tools, etc.')
|
|
||||||
|
|
||||||
SConscript(['SConscript'])
|
|
||||||
@@ -4,3 +4,14 @@ DBC_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dbc')
|
|||||||
|
|
||||||
# -I include path for e.g. "#include <opendbc/safety/safety.h>"
|
# -I include path for e.g. "#include <opendbc/safety/safety.h>"
|
||||||
INCLUDE_PATH = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
INCLUDE_PATH = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||||
|
|
||||||
|
_generated_dbc_cache: dict[str, str] | None = None
|
||||||
|
|
||||||
|
def get_generated_dbcs() -> dict[str, str]:
|
||||||
|
"""Lazily generate all *_generated DBC content in memory.
|
||||||
|
Returns {name: content} where name has no .dbc extension."""
|
||||||
|
global _generated_dbc_cache
|
||||||
|
if _generated_dbc_cache is None:
|
||||||
|
from opendbc.dbc.generator.generator import generate_all
|
||||||
|
_generated_dbc_cache = generate_all()
|
||||||
|
return _generated_dbc_cache
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from collections.abc import Callable
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import cache
|
from functools import cache
|
||||||
|
|
||||||
from opendbc import DBC_PATH
|
from opendbc import DBC_PATH, get_generated_dbcs
|
||||||
|
|
||||||
# TODO: these should just be passed in along with the DBC file
|
# TODO: these should just be passed in along with the DBC file
|
||||||
from opendbc.car.honda.hondacan import honda_checksum
|
from opendbc.car.honda.hondacan import honda_checksum
|
||||||
@@ -77,16 +77,32 @@ VAL_SPLIT_RE = re.compile(r'["]+')
|
|||||||
@cache
|
@cache
|
||||||
class DBC:
|
class DBC:
|
||||||
def __init__(self, name: str):
|
def __init__(self, name: str):
|
||||||
dbc_path = name
|
if os.path.exists(name):
|
||||||
if not os.path.exists(dbc_path):
|
self._parse_file(name)
|
||||||
|
else:
|
||||||
dbc_path = os.path.join(DBC_PATH, name + ".dbc")
|
dbc_path = os.path.join(DBC_PATH, name + ".dbc")
|
||||||
|
if os.path.exists(dbc_path):
|
||||||
|
self._parse_file(dbc_path)
|
||||||
|
else:
|
||||||
|
# try in-memory generated DBC
|
||||||
|
generated = get_generated_dbcs()
|
||||||
|
content = generated.get(name)
|
||||||
|
if content is None:
|
||||||
|
raise FileNotFoundError(f"DBC not found: {name}")
|
||||||
|
self._parse_content(name, content)
|
||||||
|
|
||||||
self._parse(dbc_path)
|
def _parse_file(self, path: str):
|
||||||
|
|
||||||
def _parse(self, path: str):
|
|
||||||
self.name = os.path.basename(path).replace(".dbc", "")
|
self.name = os.path.basename(path).replace(".dbc", "")
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
|
self._parse_lines(lines)
|
||||||
|
|
||||||
|
def _parse_content(self, name: str, content: str):
|
||||||
|
self.name = name
|
||||||
|
lines = content.splitlines(keepends=True)
|
||||||
|
self._parse_lines(lines)
|
||||||
|
|
||||||
|
def _parse_lines(self, lines: list[str]):
|
||||||
|
|
||||||
checksum_state = get_checksum_state(self.name)
|
checksum_state = get_checksum_state(self.name)
|
||||||
be_bits = [j + i * 8 for i in range(64) for j in range(7, -1, -1)]
|
be_bits = [j + i * 8 for i in range(64) for j in range(7, -1, -1)]
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from opendbc import DBC_PATH
|
from opendbc import DBC_PATH, get_generated_dbcs
|
||||||
|
|
||||||
ALL_DBCS = [os.path.basename(dbc).split('.')[0] for dbc in
|
static_dbcs = [os.path.basename(dbc).split('.')[0] for dbc in
|
||||||
glob.glob(f"{DBC_PATH}/*.dbc")]
|
glob.glob(f"{DBC_PATH}/*.dbc")]
|
||||||
|
ALL_DBCS = sorted(set(static_dbcs + list(get_generated_dbcs().keys())))
|
||||||
TEST_DBC = os.path.abspath(os.path.join(os.path.dirname(__file__), "test.dbc"))
|
TEST_DBC = os.path.abspath(os.path.join(os.path.dirname(__file__), "test.dbc"))
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
env = Environment(ENV=os.environ)
|
|
||||||
|
|
||||||
generator = File("generator/generator.py")
|
|
||||||
|
|
||||||
source_files = [
|
|
||||||
File(str(f))
|
|
||||||
for f in Path("generator").rglob("*")
|
|
||||||
if f.is_file() and f.suffix in {".py", ".dbc"}
|
|
||||||
]
|
|
||||||
|
|
||||||
output_files = [
|
|
||||||
f.name.replace(".dbc", "_generated.dbc")
|
|
||||||
for f in Path("generator").rglob("*.dbc")
|
|
||||||
if not f.name.startswith("_")
|
|
||||||
]
|
|
||||||
|
|
||||||
# include DBCs generated by python scripts
|
|
||||||
output_files += [
|
|
||||||
f.name.replace(".py", "_generated.dbc")
|
|
||||||
for f in Path("generator").rglob("*.py")
|
|
||||||
if not f.name.startswith(("_", "test_")) and f.name != "generator.py"
|
|
||||||
]
|
|
||||||
|
|
||||||
generated = env.Command(
|
|
||||||
target=list(set(output_files)),
|
|
||||||
source=[generator] + source_files,
|
|
||||||
action="python3 ${SOURCES[0]}",
|
|
||||||
)
|
|
||||||
@@ -31,13 +31,15 @@ chrysler_to_ram = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
def generate():
|
||||||
src = '_stellantis_common.dbc'
|
src = '_stellantis_common.dbc'
|
||||||
chrysler_path = os.path.dirname(os.path.realpath(__file__))
|
chrysler_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
result = {}
|
||||||
|
|
||||||
for out, addr_lookup in chrysler_to_ram.items():
|
for out, addr_lookup in chrysler_to_ram.items():
|
||||||
with open(os.path.join(chrysler_path, src), encoding='utf-8') as in_f, open(os.path.join(chrysler_path, out), 'w', encoding='utf-8') as out_f:
|
with open(os.path.join(chrysler_path, src), encoding='utf-8') as in_f:
|
||||||
out_f.write(f'CM_ "Generated from {src}"\n\n')
|
parts = [f'CM_ "Generated from {src}"\n\n']
|
||||||
|
|
||||||
wrote_addrs = set()
|
wrote_addrs = set()
|
||||||
for line in in_f.readlines():
|
for line in in_f.readlines():
|
||||||
@@ -45,10 +47,13 @@ if __name__ == "__main__":
|
|||||||
sl = line.split(' ')
|
sl = line.split(' ')
|
||||||
addr = int(sl[1])
|
addr = int(sl[1])
|
||||||
wrote_addrs.add(addr)
|
wrote_addrs.add(addr)
|
||||||
|
|
||||||
sl[1] = str(addr_lookup.get(addr, addr))
|
sl[1] = str(addr_lookup.get(addr, addr))
|
||||||
line = ' '.join(sl)
|
line = ' '.join(sl)
|
||||||
out_f.write(line)
|
parts.append(line)
|
||||||
|
|
||||||
missing_addrs = set(addr_lookup.keys()) - wrote_addrs
|
missing_addrs = set(addr_lookup.keys()) - wrote_addrs
|
||||||
assert len(missing_addrs) == 0, f"Missing addrs from {src}: {missing_addrs}"
|
assert len(missing_addrs) == 0, f"Missing addrs from {src}: {missing_addrs}"
|
||||||
|
|
||||||
|
result[out] = ''.join(parts)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
@@ -1,65 +1,92 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import importlib
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import glob
|
from pathlib import Path
|
||||||
import subprocess
|
|
||||||
|
|
||||||
generator_path = os.path.dirname(os.path.realpath(__file__))
|
generator_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
opendbc_root = os.path.join(generator_path, '../')
|
|
||||||
include_pattern = re.compile(r'CM_ "IMPORT (.*?)";\n')
|
include_pattern = re.compile(r'CM_ "IMPORT (.*?)";\n')
|
||||||
generated_suffix = '_generated.dbc'
|
|
||||||
|
|
||||||
|
|
||||||
def read_dbc(src_dir: str, filename: str) -> str:
|
def _read_dbc(src_dir: str, filename: str, extra_files: dict[str, str] | None = None) -> str:
|
||||||
|
if extra_files and filename in extra_files:
|
||||||
|
return extra_files[filename]
|
||||||
with open(os.path.join(src_dir, filename), encoding='utf-8') as file_in:
|
with open(os.path.join(src_dir, filename), encoding='utf-8') as file_in:
|
||||||
return file_in.read()
|
return file_in.read()
|
||||||
|
|
||||||
|
|
||||||
def create_dbc(src_dir: str, filename: str, output_path: str):
|
def _create_dbc_content(src_dir: str, filename: str, extra_files: dict[str, str] | None = None) -> str:
|
||||||
dbc_file_in = read_dbc(src_dir, filename)
|
dbc_file_in = _read_dbc(src_dir, filename, extra_files)
|
||||||
|
|
||||||
includes = include_pattern.findall(dbc_file_in)
|
includes = include_pattern.findall(dbc_file_in)
|
||||||
|
|
||||||
output_filename = filename.replace('.dbc', generated_suffix)
|
parts = ['CM_ "AUTOGENERATED FILE, DO NOT EDIT";\n']
|
||||||
output_file_location = os.path.join(output_path, output_filename)
|
for include_filename in includes:
|
||||||
|
parts.append(f'\n\nCM_ "Imported file {include_filename} starts here";\n')
|
||||||
|
parts.append(_read_dbc(src_dir, include_filename, extra_files))
|
||||||
|
|
||||||
with open(output_file_location, 'w', encoding='utf-8') as dbc_file_out:
|
parts.append(f'\nCM_ "{filename} starts here";\n')
|
||||||
dbc_file_out.write('CM_ "AUTOGENERATED FILE, DO NOT EDIT";\n')
|
core_dbc = include_pattern.sub('', dbc_file_in)
|
||||||
|
parts.append(core_dbc)
|
||||||
|
|
||||||
for include_filename in includes:
|
return ''.join(parts)
|
||||||
include_file_header = f'\n\nCM_ "Imported file {include_filename} starts here";\n'
|
|
||||||
dbc_file_out.write(include_file_header)
|
|
||||||
|
|
||||||
include_file = read_dbc(src_dir, include_filename)
|
|
||||||
dbc_file_out.write(include_file)
|
|
||||||
|
|
||||||
dbc_file_out.write(f'\nCM_ "{filename} starts here";\n')
|
|
||||||
|
|
||||||
core_dbc = include_pattern.sub('', dbc_file_in)
|
|
||||||
dbc_file_out.write(core_dbc)
|
|
||||||
|
|
||||||
|
|
||||||
def create_all(output_path: str):
|
def _collect_script_outputs() -> dict[str, dict[str, str]]:
|
||||||
# clear out old DBCs
|
"""Import and call generate() from each sub-generator script.
|
||||||
for f in glob.glob(f"{output_path}/*{generated_suffix}"):
|
Returns {dir_name: {filename: content}}."""
|
||||||
os.remove(f)
|
outputs: dict[str, dict[str, str]] = {}
|
||||||
|
|
||||||
# run python generator scripts first
|
for py_file in sorted(Path(generator_path).rglob("*.py")):
|
||||||
for f in glob.glob(f"{generator_path}/*/*.py"):
|
if py_file.name.startswith("test_") or py_file.name == "generator.py":
|
||||||
subprocess.check_call(f)
|
continue
|
||||||
|
|
||||||
|
dir_name = py_file.parent.name
|
||||||
|
module_name = f"opendbc.dbc.generator.{dir_name}.{py_file.stem}"
|
||||||
|
mod = importlib.import_module(module_name)
|
||||||
|
if hasattr(mod, 'generate'):
|
||||||
|
outputs.setdefault(dir_name, {}).update(mod.generate())
|
||||||
|
|
||||||
|
return outputs
|
||||||
|
|
||||||
|
|
||||||
|
def generate_all() -> dict[str, str]:
|
||||||
|
"""Generate all DBC content in memory. Returns {name: content} where name has no .dbc extension."""
|
||||||
|
script_outputs = _collect_script_outputs()
|
||||||
|
|
||||||
|
result = {}
|
||||||
for src_dir, _, filenames in os.walk(generator_path):
|
for src_dir, _, filenames in os.walk(generator_path):
|
||||||
if src_dir == generator_path:
|
if src_dir == generator_path:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
#print(src_dir)
|
dir_name = os.path.basename(src_dir)
|
||||||
for filename in filenames:
|
extra = script_outputs.get(dir_name, {})
|
||||||
if filename.startswith('_') or not filename.endswith('.dbc'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
#print(filename)
|
# all non-_ .dbc files: on-disk templates + script-generated
|
||||||
create_dbc(src_dir, filename, output_path)
|
all_dbc_files = {f for f in filenames if f.endswith('.dbc') and not f.startswith('_')}
|
||||||
|
all_dbc_files |= {f for f in extra if not f.startswith('_')}
|
||||||
|
|
||||||
|
for filename in sorted(all_dbc_files):
|
||||||
|
output_name = filename.replace('.dbc', '_generated')
|
||||||
|
content = _create_dbc_content(src_dir, filename, extra)
|
||||||
|
result[output_name] = content
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def create_all(output_path: str):
|
||||||
|
"""Generate all DBC files and write them to output_path (for backward compatibility)."""
|
||||||
|
import glob
|
||||||
|
generated_suffix = '_generated.dbc'
|
||||||
|
|
||||||
|
# clear out old generated DBCs
|
||||||
|
for f in glob.glob(os.path.join(output_path, f"*{generated_suffix}")):
|
||||||
|
os.remove(f)
|
||||||
|
|
||||||
|
for name, content in generate_all().items():
|
||||||
|
with open(os.path.join(output_path, name + '.dbc'), 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
opendbc_root = os.path.join(generator_path, '../')
|
||||||
create_all(opendbc_root)
|
create_all(opendbc_root)
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import os
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
dbc_name = os.path.basename(__file__).replace(".py", ".dbc")
|
def generate():
|
||||||
hyundai_path = os.path.dirname(os.path.realpath(__file__))
|
parts = []
|
||||||
with open(os.path.join(hyundai_path, dbc_name), "w", encoding='utf-8') as f:
|
parts.append("""
|
||||||
f.write("""
|
|
||||||
VERSION ""
|
VERSION ""
|
||||||
|
|
||||||
|
|
||||||
@@ -45,8 +43,8 @@ BS_:
|
|||||||
BU_: XXX
|
BU_: XXX
|
||||||
""")
|
""")
|
||||||
|
|
||||||
for a in [0x100, 0x200]:
|
for a in [0x100, 0x200]:
|
||||||
f.write(f"""
|
parts.append(f"""
|
||||||
BO_ {a} RADAR_POINTS_METADATA_0x{a:x}: 64 RADAR
|
BO_ {a} RADAR_POINTS_METADATA_0x{a:x}: 64 RADAR
|
||||||
SG_ SIGNAL_1 : 0|32@1+ (1,0) [0|255] "" XXX
|
SG_ SIGNAL_1 : 0|32@1+ (1,0) [0|255] "" XXX
|
||||||
SG_ SIGNAL_2 : 32|32@1+ (1,0) [0|65535] "" XXX
|
SG_ SIGNAL_2 : 32|32@1+ (1,0) [0|65535] "" XXX
|
||||||
@@ -108,63 +106,65 @@ BO_ {a} RADAR_POINTS_METADATA_0x{a:x}: 64 RADAR
|
|||||||
SG_ SIGNAL_58 : 418|1@1+ (1,0) [0|3] "" XXX
|
SG_ SIGNAL_58 : 418|1@1+ (1,0) [0|3] "" XXX
|
||||||
""")
|
""")
|
||||||
|
|
||||||
# radar points are sent at 20 Hz in groups of 1 to 13 messages
|
# radar points are sent at 20 Hz in groups of 1 to 13 messages
|
||||||
# each message has 5 radar points for a total of 65 points max
|
# each message has 5 radar points for a total of 65 points max
|
||||||
# each radar point is 101 bits so the alignment is not consistent
|
# each radar point is 101 bits so the alignment is not consistent
|
||||||
RadarPointSignal = namedtuple("RadarPointSignal", ["name", "start", "length", "scale", "offset"])
|
RadarPointSignal = namedtuple("RadarPointSignal", ["name", "start", "length", "scale", "offset"])
|
||||||
radar_point_signals = (
|
radar_point_signals = (
|
||||||
RadarPointSignal("DISTANCE", 7, 14, 1/64, 0),
|
RadarPointSignal("DISTANCE", 7, 14, 1/64, 0),
|
||||||
RadarPointSignal("", 21, 2, 1, 0),
|
RadarPointSignal("", 21, 2, 1, 0),
|
||||||
RadarPointSignal("", 23, 8, 1/512, -127/512),
|
RadarPointSignal("", 23, 8, 1/512, -127/512),
|
||||||
RadarPointSignal("REL_VELOCITY", 31, 13, 1/32, -66),
|
RadarPointSignal("REL_VELOCITY", 31, 13, 1/32, -66),
|
||||||
RadarPointSignal("", 44, 2, 1, 0),
|
RadarPointSignal("", 44, 2, 1, 0),
|
||||||
RadarPointSignal("", 46, 2, 1, 0),
|
RadarPointSignal("", 46, 2, 1, 0),
|
||||||
RadarPointSignal("AZIMUTH", 48, 12, 1/512, -2047/512),
|
RadarPointSignal("AZIMUTH", 48, 12, 1/512, -2047/512),
|
||||||
RadarPointSignal("", 60, 2, 1, 0),
|
RadarPointSignal("", 60, 2, 1, 0),
|
||||||
RadarPointSignal("", 62, 1, 1, 0),
|
RadarPointSignal("", 62, 1, 1, 0),
|
||||||
RadarPointSignal("", 63, 7, 1, 0),
|
RadarPointSignal("", 63, 7, 1, 0),
|
||||||
RadarPointSignal("", 70, 1, 1, 0),
|
RadarPointSignal("", 70, 1, 1, 0),
|
||||||
RadarPointSignal("", 71, 6, 1, 0),
|
RadarPointSignal("", 71, 6, 1, 0),
|
||||||
RadarPointSignal("", 77, 2, 1, 0),
|
RadarPointSignal("", 77, 2, 1, 0),
|
||||||
RadarPointSignal("", 79, 8, 1/512, -127/512),
|
RadarPointSignal("", 79, 8, 1/512, -127/512),
|
||||||
RadarPointSignal("", 87, 1, 1, 0),
|
RadarPointSignal("", 87, 1, 1, 0),
|
||||||
RadarPointSignal("", 88, 2, 1, 0),
|
RadarPointSignal("", 88, 2, 1, 0),
|
||||||
RadarPointSignal("", 90, 3, 1, 0),
|
RadarPointSignal("", 90, 3, 1, 0),
|
||||||
# last 15 bits are controlled by LAYOUT_ID (seems to always zero, so below is layout 0)
|
# last 15 bits are controlled by LAYOUT_ID (seems to always zero, so below is layout 0)
|
||||||
RadarPointSignal("", 93, 6, 1, 0),
|
RadarPointSignal("", 93, 6, 1, 0),
|
||||||
RadarPointSignal("", 99, 8, 1, 0),
|
RadarPointSignal("", 99, 8, 1, 0),
|
||||||
RadarPointSignal("", 107, 1, 1, 0),
|
RadarPointSignal("", 107, 1, 1, 0),
|
||||||
)
|
)
|
||||||
radar_point_bit_count = sum([s.length for s in radar_point_signals])
|
radar_point_bit_count = sum([s.length for s in radar_point_signals])
|
||||||
|
|
||||||
for a in [0x101, 0x201]:
|
for a in [0x101, 0x201]:
|
||||||
f.write(f"""
|
parts.append(f"""
|
||||||
BO_ {a} RADAR_POINTS_0x{a:x}: 64 RADAR
|
BO_ {a} RADAR_POINTS_0x{a:x}: 64 RADAR
|
||||||
SG_ MESSAGE_ID : 0|5@1+ (1,0) [0|31] "" XXX
|
SG_ MESSAGE_ID : 0|5@1+ (1,0) [0|31] "" XXX
|
||||||
SG_ LAYOUT_ID : 5|2@1+ (1,0) [0|3] "" XXX
|
SG_ LAYOUT_ID : 5|2@1+ (1,0) [0|3] "" XXX
|
||||||
""")
|
""")
|
||||||
bit_idx = radar_point_signals[0].start
|
bit_idx = radar_point_signals[0].start
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
signal_idx = 1
|
signal_idx = 1
|
||||||
for sig in radar_point_signals:
|
for sig in radar_point_signals:
|
||||||
if sig.name:
|
if sig.name:
|
||||||
sig_name = f"POINT_{i+1}_{sig.name}"
|
sig_name = f"POINT_{i+1}_{sig.name}"
|
||||||
else:
|
else:
|
||||||
sig_name = f"POINT_{i+1}_SIGNAL_{signal_idx}"
|
sig_name = f"POINT_{i+1}_SIGNAL_{signal_idx}"
|
||||||
signal_idx += 1
|
signal_idx += 1
|
||||||
|
|
||||||
sig_start_idx = i * radar_point_bit_count + sig.start
|
sig_start_idx = i * radar_point_bit_count + sig.start
|
||||||
assert bit_idx == sig_start_idx, f"signal overlap or gap!!! {bit_idx} != {sig_start_idx}"
|
assert bit_idx == sig_start_idx, f"signal overlap or gap!!! {bit_idx} != {sig_start_idx}"
|
||||||
min_val = round(sig.offset, 10)
|
min_val = round(sig.offset, 10)
|
||||||
max_val = round((2**sig.length - 1) * sig.scale + sig.offset, 10)
|
max_val = round((2**sig.length - 1) * sig.scale + sig.offset, 10)
|
||||||
|
|
||||||
f.write(f" SG_ {sig_name} : {sig_start_idx}|{sig.length}@1+ ({sig.scale},{sig.offset}) [{min_val}|{max_val}] \"\" XXX\n")
|
parts.append(f" SG_ {sig_name} : {sig_start_idx}|{sig.length}@1+ ({sig.scale},{sig.offset}) [{min_val}|{max_val}] \"\" XXX\n")
|
||||||
bit_idx += sig.length
|
bit_idx += sig.length
|
||||||
|
|
||||||
# checksum is across a group of 0x100/200 and 0x101/201 messages (no checksums inside the other messages)
|
# checksum is across a group of 0x100/200 and 0x101/201 messages (no checksums inside the other messages)
|
||||||
# ccitt_crc16 = mkCrcFun(0x11021, initCrc=0xffff, xorOut=0x0000, rev=False)
|
# ccitt_crc16 = mkCrcFun(0x11021, initCrc=0xffff, xorOut=0x0000, rev=False)
|
||||||
for a in [0x104, 0x204]:
|
for a in [0x104, 0x204]:
|
||||||
f.write(f"""
|
parts.append(f"""
|
||||||
BO_ {a} RADAR_POINTS_CHECKSUM_0x{a:x}: 3 RADAR
|
BO_ {a} RADAR_POINTS_CHECKSUM_0x{a:x}: 3 RADAR
|
||||||
SG_ CRC16 : 0|16@1+ (1,0) [0|65535] "" XXX
|
SG_ CRC16 : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
return {"hyundai_kia_mando_corner_radar.dbc": "".join(parts)}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
dbc_name = os.path.basename(__file__).replace(".py", ".dbc")
|
def generate():
|
||||||
hyundai_path = os.path.dirname(os.path.realpath(__file__))
|
parts = []
|
||||||
with open(os.path.join(hyundai_path, dbc_name), "w", encoding='utf-8') as f:
|
parts.append("""
|
||||||
f.write("""
|
|
||||||
VERSION ""
|
VERSION ""
|
||||||
|
|
||||||
|
|
||||||
@@ -44,9 +42,9 @@ BS_:
|
|||||||
BU_: XXX
|
BU_: XXX
|
||||||
""")
|
""")
|
||||||
|
|
||||||
# note: 0x501/0x502 seem to be special in 0x5XX range
|
# note: 0x501/0x502 seem to be special in 0x5XX range
|
||||||
for a in range(0x500, 0x500 + 32):
|
for a in range(0x500, 0x500 + 32):
|
||||||
f.write(f"""
|
parts.append(f"""
|
||||||
BO_ {a} RADAR_TRACK_{a:x}: 8 RADAR
|
BO_ {a} RADAR_TRACK_{a:x}: 8 RADAR
|
||||||
SG_ UNKNOWN_1 : 7|8@0- (1,0) [-128|127] "" XXX
|
SG_ UNKNOWN_1 : 7|8@0- (1,0) [-128|127] "" XXX
|
||||||
SG_ AZIMUTH : 12|10@0- (0.2,0) [-102.4|102.2] "" XXX
|
SG_ AZIMUTH : 12|10@0- (0.2,0) [-102.4|102.2] "" XXX
|
||||||
@@ -59,3 +57,5 @@ BO_ {a} RADAR_TRACK_{a:x}: 8 RADAR
|
|||||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||||
SG_ STATE_2 : 55|2@0+ (1,0) [0|3] "" XXX
|
SG_ STATE_2 : 55|2@0+ (1,0) [0|3] "" XXX
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
return {"hyundai_kia_mando_front_radar.dbc": "".join(parts)}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
dbc_name = os.path.basename(__file__).replace(".py", ".dbc")
|
def generate():
|
||||||
rivian_path = os.path.dirname(os.path.realpath(__file__))
|
parts = []
|
||||||
with open(os.path.join(rivian_path, dbc_name), "w", encoding='utf-8') as f:
|
parts.append("""
|
||||||
f.write("""
|
|
||||||
VERSION ""
|
VERSION ""
|
||||||
|
|
||||||
|
|
||||||
@@ -44,8 +42,8 @@ BS_:
|
|||||||
BU_: XXX
|
BU_: XXX
|
||||||
""")
|
""")
|
||||||
|
|
||||||
for a in range(0x500, 0x500 + 32):
|
for a in range(0x500, 0x500 + 32):
|
||||||
f.write(f"""
|
parts.append(f"""
|
||||||
BO_ {a} RADAR_TRACK_{a:x}: 8 RADAR
|
BO_ {a} RADAR_TRACK_{a:x}: 8 RADAR
|
||||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||||
@@ -57,8 +55,10 @@ BO_ {a} RADAR_TRACK_{a:x}: 8 RADAR
|
|||||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||||
""")
|
""")
|
||||||
|
|
||||||
for a in range(0x500, 0x500 + 32):
|
for a in range(0x500, 0x500 + 32):
|
||||||
f.write(f"""
|
parts.append(f"""
|
||||||
VAL_ {a} STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
VAL_ {a} STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||||
VAL_ {a} MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
VAL_ {a} MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
return {"rivian_mando_front_radar.dbc": "".join(parts)}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import os
|
|
||||||
from opendbc.dbc.generator.tesla._radar_common import get_radar_point_definition, get_val_definition
|
from opendbc.dbc.generator.tesla._radar_common import get_radar_point_definition, get_val_definition
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
dbc_name = os.path.basename(__file__).replace(".py", ".dbc")
|
def generate():
|
||||||
tesla_path = os.path.dirname(os.path.realpath(__file__))
|
parts = []
|
||||||
with open(os.path.join(tesla_path, dbc_name), "w", encoding='utf-8') as f:
|
parts.append("""
|
||||||
f.write("""
|
|
||||||
VERSION ""
|
VERSION ""
|
||||||
|
|
||||||
NS_ :
|
NS_ :
|
||||||
@@ -133,15 +131,15 @@ BO_ 1281 TeslaRadarAlertMatrix: 8 Radar
|
|||||||
SG_ unused62 : 62|2@1+ (1,0) [0|3] "" Autopilot
|
SG_ unused62 : 62|2@1+ (1,0) [0|3] "" Autopilot
|
||||||
""")
|
""")
|
||||||
|
|
||||||
M_RANGE = range(0x310, 0x36D + 1, 3)
|
M_RANGE = range(0x310, 0x36D + 1, 3)
|
||||||
for i, base_id in enumerate(M_RANGE):
|
for i, base_id in enumerate(M_RANGE):
|
||||||
f.write(get_radar_point_definition(base_id, f"RadarPoint{i}"))
|
parts.append(get_radar_point_definition(base_id, f"RadarPoint{i}"))
|
||||||
|
|
||||||
L_RANGE = range(0x371, 0x37D + 1, 3)
|
L_RANGE = range(0x371, 0x37D + 1, 3)
|
||||||
for i, base_id in enumerate(L_RANGE):
|
for i, base_id in enumerate(L_RANGE):
|
||||||
f.write(get_radar_point_definition(base_id, f"ProcessedRadarPoint{i+1}"))
|
parts.append(get_radar_point_definition(base_id, f"ProcessedRadarPoint{i+1}"))
|
||||||
|
|
||||||
f.write("""
|
parts.append("""
|
||||||
BO_ 697 VIN_VIP_405HS: 8 Autopilot
|
BO_ 697 VIN_VIP_405HS: 8 Autopilot
|
||||||
SG_ VIN_MuxID M : 0|8@1+ (1,0) [0|0] "" Radar
|
SG_ VIN_MuxID M : 0|8@1+ (1,0) [0|0] "" Radar
|
||||||
SG_ VIN_Part1 m16 : 47|24@0+ (1,0) [0|16777215] "" Radar
|
SG_ VIN_Part1 m16 : 47|24@0+ (1,0) [0|16777215] "" Radar
|
||||||
@@ -278,5 +276,7 @@ BA_ "GenMsgCycleTime" BO_ 729 1000;
|
|||||||
|
|
||||||
VAL_ 681 Msg2A9_FourWheelDrive 3 "SNA" 2 "UNUSED" 1 "4WD" 0 "2WD" ;""")
|
VAL_ 681 Msg2A9_FourWheelDrive 3 "SNA" 2 "UNUSED" 1 "4WD" 0 "2WD" ;""")
|
||||||
|
|
||||||
for base_id in list(M_RANGE) + list(L_RANGE):
|
for base_id in list(M_RANGE) + list(L_RANGE):
|
||||||
f.write(get_val_definition(base_id))
|
parts.append(get_val_definition(base_id))
|
||||||
|
|
||||||
|
return {"tesla_radar_bosch.dbc": "".join(parts)}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import os
|
|
||||||
from opendbc.dbc.generator.tesla._radar_common import get_radar_point_definition, get_val_definition
|
from opendbc.dbc.generator.tesla._radar_common import get_radar_point_definition, get_val_definition
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
dbc_name = os.path.basename(__file__).replace(".py", ".dbc")
|
def generate():
|
||||||
tesla_path = os.path.dirname(os.path.realpath(__file__))
|
parts = []
|
||||||
with open(os.path.join(tesla_path, dbc_name), "w", encoding='utf-8') as f:
|
parts.append("""
|
||||||
f.write("""
|
|
||||||
VERSION ""
|
VERSION ""
|
||||||
|
|
||||||
NS_ :
|
NS_ :
|
||||||
@@ -66,12 +64,14 @@ BO_ 1601 UDS_radcRequest: 8 Diag
|
|||||||
SG_ UDS_radcRequestData : 7|64@0+ (1,0) [0|1.84467e+19] "" Radar
|
SG_ UDS_radcRequestData : 7|64@0+ (1,0) [0|1.84467e+19] "" Radar
|
||||||
""")
|
""")
|
||||||
|
|
||||||
POINT_RANGE = range(0x410, 0x45E + 1, 2)
|
POINT_RANGE = range(0x410, 0x45E + 1, 2)
|
||||||
for i, base_id in enumerate(POINT_RANGE):
|
for i, base_id in enumerate(POINT_RANGE):
|
||||||
f.write(get_radar_point_definition(base_id, f"RadarPoint{i}"))
|
parts.append(get_radar_point_definition(base_id, f"RadarPoint{i}"))
|
||||||
|
|
||||||
f.write("""
|
parts.append("""
|
||||||
VAL_ 1025 lowPowerMode 1 "COMMANDED_LOW_POWER" 0 "DEFAULT_LOW_POWER" 2 "NORMAL_POWER" 3 "SNA";""")
|
VAL_ 1025 lowPowerMode 1 "COMMANDED_LOW_POWER" 0 "DEFAULT_LOW_POWER" 2 "NORMAL_POWER" 3 "SNA";""")
|
||||||
|
|
||||||
for base_id in list(POINT_RANGE):
|
for base_id in list(POINT_RANGE):
|
||||||
f.write(get_val_definition(base_id))
|
parts.append(get_val_definition(base_id))
|
||||||
|
|
||||||
|
return {"tesla_radar_continental.dbc": "".join(parts)}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
system = platform.system()
|
|
||||||
|
|
||||||
env = Environment(
|
|
||||||
CFLAGS=[
|
|
||||||
'-Wall',
|
|
||||||
"-Wextra",
|
|
||||||
'-Werror',
|
|
||||||
'-nostdlib',
|
|
||||||
'-fno-builtin',
|
|
||||||
'-std=gnu11',
|
|
||||||
'-Wfatal-errors',
|
|
||||||
'-Wno-pointer-to-int-cast',
|
|
||||||
'-g',
|
|
||||||
'-O0',
|
|
||||||
'-fno-omit-frame-pointer',
|
|
||||||
'-DALLOW_DEBUG',
|
|
||||||
],
|
|
||||||
LINKFLAGS=[
|
|
||||||
'-fsanitize=undefined',
|
|
||||||
'-fno-sanitize-recover=undefined',
|
|
||||||
],
|
|
||||||
CPPPATH=["#"],
|
|
||||||
tools=["default", "compilation_db"],
|
|
||||||
)
|
|
||||||
|
|
||||||
# add coverage if available
|
|
||||||
# Use TryCompile (not TryLink) because -nostdlib in CFLAGS breaks the link probe.
|
|
||||||
conf = Configure(env, log_file=os.devnull)
|
|
||||||
prev = env['CFLAGS'][:]
|
|
||||||
env.Append(CFLAGS=['-fprofile-arcs', '-ftest-coverage'])
|
|
||||||
has_coverage = conf.TryCompile('int x;\n', '.c')
|
|
||||||
env['CFLAGS'] = prev
|
|
||||||
if has_coverage:
|
|
||||||
env.Append(CFLAGS=['-fprofile-arcs', '-ftest-coverage'])
|
|
||||||
env.Append(LINKFLAGS=['-fprofile-arcs', '-ftest-coverage'])
|
|
||||||
env = conf.Finish()
|
|
||||||
|
|
||||||
safety = env.SharedObject("safety.os", "safety.c")
|
|
||||||
libsafety = env.SharedLibrary("libsafety.so", [safety])
|
|
||||||
|
|
||||||
# GCC-style note file is generated by compiler, allow scons to clean it up
|
|
||||||
env.SideEffect("safety.gcno", safety)
|
|
||||||
@@ -1,10 +1,40 @@
|
|||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from cffi import FFI
|
from cffi import FFI
|
||||||
|
|
||||||
from opendbc.safety import LEN_TO_DLC
|
from opendbc.safety import LEN_TO_DLC
|
||||||
|
|
||||||
libsafety_dir = os.path.dirname(os.path.abspath(__file__))
|
libsafety_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def _build_libsafety() -> str:
|
||||||
|
"""Compile libsafety.so to a temp file and return its path."""
|
||||||
|
root = str(Path(libsafety_dir).parents[3])
|
||||||
|
safety_c = os.path.join(libsafety_dir, "safety.c")
|
||||||
|
safety_os = os.path.join(libsafety_dir, "safety.os")
|
||||||
|
|
||||||
|
cflags = [
|
||||||
|
'-Wall', '-Wextra', '-Werror', '-nostdlib', '-fno-builtin',
|
||||||
|
'-std=gnu11', '-Wfatal-errors', '-Wno-pointer-to-int-cast',
|
||||||
|
'-g', '-O0', '-fno-omit-frame-pointer', '-DALLOW_DEBUG',
|
||||||
|
'-fprofile-arcs', '-ftest-coverage',
|
||||||
|
]
|
||||||
|
ldflags = [
|
||||||
|
'-fsanitize=undefined', '-fno-sanitize-recover=undefined',
|
||||||
|
'-fprofile-arcs', '-ftest-coverage',
|
||||||
|
]
|
||||||
|
|
||||||
|
fd, libsafety_so = tempfile.mkstemp(suffix='.so')
|
||||||
|
os.close(fd)
|
||||||
|
|
||||||
|
subprocess.check_call(['cc', '-fPIC', *cflags, '-I', root, '-c', safety_c, '-o', safety_os])
|
||||||
|
subprocess.check_call(['cc', '-shared', safety_os, '-o', libsafety_so, *ldflags])
|
||||||
|
return libsafety_so
|
||||||
|
|
||||||
|
|
||||||
ffi = FFI()
|
ffi = FFI()
|
||||||
|
|
||||||
ffi.cdef("""
|
ffi.cdef("""
|
||||||
@@ -83,12 +113,18 @@ int mutation_get_active_mutant(void);
|
|||||||
|
|
||||||
class LibSafety:
|
class LibSafety:
|
||||||
pass
|
pass
|
||||||
libsafety: LibSafety = ffi.dlopen(os.path.join(libsafety_dir, "libsafety.so"))
|
libsafety: LibSafety
|
||||||
|
|
||||||
def load(path):
|
def load(path):
|
||||||
global libsafety
|
global libsafety
|
||||||
libsafety = ffi.dlopen(str(path))
|
libsafety = ffi.dlopen(str(path))
|
||||||
|
|
||||||
|
def __getattr__(name):
|
||||||
|
if name == "libsafety":
|
||||||
|
load(_build_libsafety())
|
||||||
|
return libsafety
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
def make_CANPacket(addr: int, bus: int, dat):
|
def make_CANPacket(addr: int, bus: int, dat):
|
||||||
ret = ffi.new('CANPacket_t *')
|
ret = ffi.new('CANPacket_t *')
|
||||||
ret[0].extended = 1 if addr >= 0x800 else 0
|
ret[0].extended = 1 if addr >= 0x800 else 0
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ cd $DIR
|
|||||||
|
|
||||||
source ../../../setup.sh
|
source ../../../setup.sh
|
||||||
|
|
||||||
# reset coverage data and generate gcc note file
|
# reset coverage data
|
||||||
rm -f ./libsafety/*.gcda
|
rm -f ./libsafety/*.gcda
|
||||||
scons -j$(nproc) -D
|
|
||||||
|
|
||||||
# run safety tests and generate coverage data
|
# run safety tests and generate coverage data
|
||||||
python -m unittest discover -s . -p 'test_*.py' -t ../../../
|
python -m unittest discover -s . -p 'test_*.py' -t ../../../
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ requires-python = ">=3.11,<3.13" # pycapnp doesn't work with 3.13
|
|||||||
urls = { "homepage" = "https://github.com/commaai/opendbc" }
|
urls = { "homepage" = "https://github.com/commaai/opendbc" }
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"scons",
|
|
||||||
"numpy",
|
"numpy",
|
||||||
"tqdm",
|
"tqdm",
|
||||||
"pycapnp==2.1.0",
|
"pycapnp==2.1.0",
|
||||||
@@ -90,9 +89,6 @@ ignore = [
|
|||||||
]
|
]
|
||||||
flake8-implicit-str-concat.allow-multiline=false
|
flake8-implicit-str-concat.allow-multiline=false
|
||||||
|
|
||||||
[tool.ruff.lint.per-file-ignores]
|
|
||||||
"site_scons/*" = ["ALL"]
|
|
||||||
|
|
||||||
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
||||||
"numpy.mean".msg = "Sum and divide. np.mean is slow"
|
"numpy.mean".msg = "Sum and divide. np.mean is slow"
|
||||||
|
|
||||||
|
|||||||
5
test.sh
5
test.sh
@@ -9,13 +9,10 @@ source ./setup.sh
|
|||||||
# *** uv lockfile check ***
|
# *** uv lockfile check ***
|
||||||
uv lock --check
|
uv lock --check
|
||||||
|
|
||||||
# *** build ***
|
|
||||||
scons -j8
|
|
||||||
|
|
||||||
# *** lint + test ***
|
# *** lint + test ***
|
||||||
lefthook run test
|
lefthook run test
|
||||||
|
|
||||||
# *** all done ***
|
# *** all done ***
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
printf "\n${GREEN}All good!${NC} Finished build, lint, and test in ${SECONDS}s\n"
|
printf "\n${GREEN}All good!${NC} Finished lint and test in ${SECONDS}s\n"
|
||||||
|
|||||||
11
uv.lock
generated
11
uv.lock
generated
@@ -384,7 +384,6 @@ dependencies = [
|
|||||||
{ name = "numpy" },
|
{ name = "numpy" },
|
||||||
{ name = "pycapnp" },
|
{ name = "pycapnp" },
|
||||||
{ name = "pycryptodome" },
|
{ name = "pycryptodome" },
|
||||||
{ name = "scons" },
|
|
||||||
{ name = "tqdm" },
|
{ name = "tqdm" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -428,7 +427,6 @@ requires-dist = [
|
|||||||
{ name = "pycapnp", specifier = "==2.1.0" },
|
{ name = "pycapnp", specifier = "==2.1.0" },
|
||||||
{ name = "pycryptodome" },
|
{ name = "pycryptodome" },
|
||||||
{ name = "ruff", marker = "extra == 'testing'" },
|
{ name = "ruff", marker = "extra == 'testing'" },
|
||||||
{ name = "scons" },
|
|
||||||
{ name = "tqdm" },
|
{ name = "tqdm" },
|
||||||
{ name = "tree-sitter", marker = "extra == 'testing'" },
|
{ name = "tree-sitter", marker = "extra == 'testing'" },
|
||||||
{ name = "tree-sitter-c", marker = "extra == 'testing'" },
|
{ name = "tree-sitter-c", marker = "extra == 'testing'" },
|
||||||
@@ -547,15 +545,6 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/6d/78/097c0798b1dab9f8affe73da9642bb4500e098cb27fd8dc9724816ac747b/ruff-0.15.2-py3-none-win_arm64.whl", hash = "sha256:cabddc5822acdc8f7b5527b36ceac55cc51eec7b1946e60181de8fe83ca8876e", size = 10941649, upload-time = "2026-02-19T22:32:18.108Z" },
|
{ url = "https://files.pythonhosted.org/packages/6d/78/097c0798b1dab9f8affe73da9642bb4500e098cb27fd8dc9724816ac747b/ruff-0.15.2-py3-none-win_arm64.whl", hash = "sha256:cabddc5822acdc8f7b5527b36ceac55cc51eec7b1946e60181de8fe83ca8876e", size = 10941649, upload-time = "2026-02-19T22:32:18.108Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scons"
|
|
||||||
version = "4.10.1"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/c9/2f430bb39e4eccba32ce8008df4a3206df651276422204e177a09e12b30b/scons-4.10.1.tar.gz", hash = "sha256:99c0e94a42a2c1182fa6859b0be697953db07ba936ecc9817ae0d218ced20b15", size = 3258403, upload-time = "2025-11-16T22:43:39.258Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/ce/bf/931fb9fbb87234c32b8b1b1c15fba23472a10777c12043336675633809a7/scons-4.10.1-py3-none-any.whl", hash = "sha256:bd9d1c52f908d874eba92a8c0c0a8dcf2ed9f3b88ab956d0fce1da479c4e7126", size = 4136069, upload-time = "2025-11-16T22:43:35.933Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sortedcontainers"
|
name = "sortedcontainers"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user