mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
112 lines
5.2 KiB
Python
112 lines
5.2 KiB
Python
"""Actual exported native status + actual production C++ negotiate/write/HB/read order."""
|
|
import ctypes
|
|
import os
|
|
import subprocess
|
|
|
|
|
|
class PandadOrder:
|
|
def __init__(self, safety, library_path, executable):
|
|
self.safety = safety
|
|
self.lib = ctypes.CDLL(str(library_path))
|
|
self.process = subprocess.Popen([str(executable)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
|
self.heartbeat = False
|
|
self.compatible = False
|
|
self.axis_session = ''
|
|
|
|
def status(self):
|
|
# Board USB0xd5 initializer reads request first, permission second.
|
|
request = int(self.safety.aol_get_request_mask())
|
|
permission = int(self.safety.aol_get_permission_mask())
|
|
mode = ctypes.c_uint16.in_dll(self.lib, 'current_safety_mode').value
|
|
word = ctypes.c_uint16.in_dll(self.lib, 'current_safety_param').value
|
|
capability = int(ctypes.c_void_p.in_dll(self.lib, 'aol_policy').value is not None)
|
|
return request, permission, mode, word, capability
|
|
|
|
def exchange(self, line):
|
|
assert self.process.poll() is None
|
|
self.process.stdin.write(line + '\n')
|
|
self.process.stdin.flush()
|
|
result = self.process.stdout.readline().strip().split()
|
|
assert result, self.process.poll()
|
|
return result
|
|
|
|
def step(self, driven, published, now):
|
|
before = self.status()
|
|
axis_event = driven['aolAxisState']
|
|
axis = axis_event.aolAxisState
|
|
intent_event = published['aolIntentWire']
|
|
fields = [now, int(axis_event.valid), int(axis.qualified), axis.sessionId, axis.observedMonoTime, axis.validUntilMonoTime,
|
|
axis_event.logMonoTime, int(axis.desiredLateral), int(axis.desiredLongitudinal), int(intent_event.valid and published['carState'].valid),
|
|
axis.sourceCarStateMonoTime, intent_event.logMonoTime, bytes(intent_event.aolIntentWire).hex(), str(axis.intentProducerSessionId) or '-']
|
|
capable, request, retain = map(int, self.exchange('PREP ' + ' '.join(map(str, (*before, *fields)))))
|
|
if capable:
|
|
self.safety.aol_set_host_request(request)
|
|
enabled = int(driven['selfdriveState'].selfdriveState.enabled)
|
|
self.heartbeat = bool(int(self.exchange('HB ' + ' '.join(map(str, (*before, enabled, capable))))[0]))
|
|
self.safety.set_aol_test_heartbeat(self.heartbeat)
|
|
after = self.status()
|
|
compatible, session = self.exchange('DONE ' + ' '.join(map(str, (*after, capable))))
|
|
self.compatible = bool(int(compatible))
|
|
self.axis_session = '' if session == '-' else session
|
|
return None, {'before': before, 'plan': (capable, request, retain), 'heartbeat': self.heartbeat,
|
|
'after': after, 'session': session}
|
|
|
|
def close(self):
|
|
self.process.stdin.close()
|
|
try:
|
|
assert self.process.wait(timeout=10) == 0
|
|
finally:
|
|
self.process.stdout.close()
|
|
|
|
|
|
import hashlib
|
|
from pathlib import Path
|
|
import re
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture(scope='module')
|
|
def hkg_pandad_order(tmp_path_factory):
|
|
try:
|
|
import capnproto
|
|
include_dir, library_dir = capnproto.INCLUDE_DIR, capnproto.LIB_DIR
|
|
except ModuleNotFoundError:
|
|
prefix = Path(os.environ['CAPNP_PREFIX'])
|
|
assert (prefix / 'include/capnp/message.h').is_file()
|
|
include_dir, library_dir = str(prefix / 'include'), str(prefix / 'lib')
|
|
|
|
root = Path(__file__).resolve().parents[4]
|
|
directory = tmp_path_factory.mktemp('hkg-pandad-order')
|
|
original = root / 'openpilot/starpilot/aol/tests/hyundai_pandad_order.cc'
|
|
pandad = root / 'openpilot/selfdrive/pandad/pandad.cc'
|
|
matched = re.search(r' if \(aol_write_ok\) \{.*?\n \}', pandad.read_text(), re.S)
|
|
assert matched is not None
|
|
source = directory / 'peer.cc'
|
|
source.write_text(original.read_text().replace('// PRODUCTION_HEARTBEAT_BLOCK', matched[0]))
|
|
generated = [root / ('openpilot/cereal/gen/cpp/' + name + '.capnp.c++') for name in ('log', 'car', 'custom', 'deprecated')]
|
|
paths = [original, pandad, *generated, *list((root / 'openpilot/cereal/gen/cpp').glob('*.capnp.h'))]
|
|
roots = [root, root / 'openpilot', root / 'opendbc_repo', root / 'openpilot/cereal/gen/cpp']
|
|
closure = {}
|
|
queue = [original, *paths[2:]]
|
|
while queue:
|
|
path = queue.pop()
|
|
if path in closure:
|
|
continue
|
|
closure[path] = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
for quoted in re.findall(r'^\s*#include\s+"([^"\n]+)"', path.read_text(), re.M):
|
|
included = next((r / quoted for r in [path.parent, *roots] if (r / quoted).is_file()), None)
|
|
assert included is not None, (path, quoted)
|
|
queue.append(included)
|
|
closure[pandad] = hashlib.sha256(pandad.read_bytes()).hexdigest()
|
|
executable = directory / 'peer'
|
|
subprocess.run(['c++', '-std=c++17', '-O1', '-Wall', '-Wextra', '-Werror',
|
|
*[flag for r in roots for flag in ('-I', str(r))], '-I', include_dir,
|
|
str(source), *map(str, generated), '-L', library_dir, '-lcapnp', '-lkj', '-o', str(executable)],
|
|
check=True, timeout=40)
|
|
assert all(hashlib.sha256(path.read_bytes()).hexdigest() == value for path, value in closure.items())
|
|
print({'productionHeartbeatSourceClosure': {str(p.relative_to(root)): h for p, h in closure.items()},
|
|
'cppArtifactSha256': hashlib.sha256(executable.read_bytes()).hexdigest()})
|
|
yield lambda native, library: PandadOrder(native, library, executable)
|
|
assert all(hashlib.sha256(path.read_bytes()).hexdigest() == value for path, value in closure.items())
|