diff --git a/openpilot/cereal/log.capnp b/openpilot/cereal/log.capnp index 0221f00e1..332e4d332 100644 --- a/openpilot/cereal/log.capnp +++ b/openpilot/cereal/log.capnp @@ -2593,7 +2593,6 @@ struct Event { clocks @35 :Clocks; deviceState @6 :DeviceState; chestnutState @152 :ChestnutState; - chestnutGpuState @153 :ChestnutState; logMessage @18 :Text; errorLogMessage @85 :Text; diff --git a/openpilot/cereal/services.py b/openpilot/cereal/services.py index 88bc47ad2..ed718506f 100755 --- a/openpilot/cereal/services.py +++ b/openpilot/cereal/services.py @@ -26,7 +26,6 @@ _services: dict[str, tuple] = { "temperatureSensor": (True, 2., 200), "deviceState": (True, 2., 1), "chestnutState": (True, 10., 10), - "chestnutGpuState": (False, 10.), "touch": (True, 20., 1), "can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment "controlsState": (True, 100., 10, QueueSize.MEDIUM), diff --git a/openpilot/system/hardware/chestnut/monitoring.py b/openpilot/system/hardware/chestnut/monitoring.py deleted file mode 100644 index 829db9961..000000000 --- a/openpilot/system/hardware/chestnut/monitoring.py +++ /dev/null @@ -1,124 +0,0 @@ -import struct -from contextlib import suppress - -import usb1 - -import openpilot.cereal.messaging as messaging -from openpilot.cereal.services import SERVICE_LIST -from openpilot.common.hardware.usb import CHESTNUT_USB_IDS - - -USB_TIMEOUT_MS = 100 -PCIE_LTSSM_ADDRESS = 0xB450 - - -class ChestnutUsb: - def __init__(self): - self.context: usb1.USBContext | None = None - self.handle = None - - def close(self) -> None: - handle, context = self.handle, self.context - self.handle = None - self.context = None - with suppress(Exception): - if handle is not None: - handle.close() - with suppress(Exception): - if context is not None: - context.close() - - def connect(self) -> bool: - if self.handle is not None: - return True - - context = usb1.USBContext() - for vendor_id, product_id in CHESTNUT_USB_IDS: - handle = context.openByVendorIDAndProductID(vendor_id, product_id, skip_on_error=True) - if handle is not None: - self.context = context - self.handle = handle - return True - context.close() - return False - - def _read(self, request: int, value: int, length: int) -> bytes: - if self.handle is None: - raise usb1.USBErrorNoDevice - raw = bytes(self.handle.controlRead(0xC0, request, value, 0, length, timeout=USB_TIMEOUT_MS)) - if len(raw) != length: - raise ValueError(f"short chestnut USB response: {len(raw)}/{length}") - return raw - - def read_ina(self) -> tuple[int, int, bool]: - return struct.unpack(' int: - return self._read(0xE4, PCIE_LTSSM_ADDRESS, 1)[0] - - -class ChestnutMonitoring: - def __init__(self, usb: ChestnutUsb | None = None): - self.usb = usb or ChestnutUsb() - self.gpu_state = None - self.seen = False - self.enabled = False - self.usb_failed = False - - def set_enabled(self, enabled: bool) -> None: - if self.enabled == enabled: - return - self.enabled = enabled - self.usb.close() - self.usb_failed = False - - def retry(self) -> None: - self.usb_failed = False - - def model_alive(self, sm: messaging.SubMaster, now: float) -> bool: - modeld = next((p for p in sm['managerState'].processes if p.name == 'modeld'), None) - if modeld is not None and modeld.shouldBeRunning and not modeld.running: - return False - recv_time = sm.recv_time['chestnutGpuState'] - return recv_time > 0. and now - recv_time < 10. / SERVICE_LIST['chestnutGpuState'].frequency - - def update_gpu_state(self, sm: messaging.SubMaster, now: float) -> None: - if sm.updated['chestnutGpuState']: - self.gpu_state = sm['chestnutGpuState'] if sm.valid['chestnutGpuState'] else None - elif not self.model_alive(sm, now): - self.gpu_state = None - - def update(self, sm: messaging.SubMaster, now: float, model_loading: bool = False): - self.update_gpu_state(sm, now) - return self.build_message(model_loading) - - def build_message(self, model_loading: bool = False): - if not self.enabled: - return None - - msg = messaging.new_message('chestnutState') - if self.gpu_state is not None: - msg.chestnutState = self.gpu_state - state = msg.chestnutState - - if self.usb_failed: - return msg if self.seen else None - - try: - if not self.usb.connect(): - self.usb_failed = True - return msg if self.seen else None - - self.seen = True - voltage, current, fault = self.usb.read_ina() - pcie_ltssm = self.usb.read_pcie_ltssm() - state.supplyVoltage = voltage - state.supplyCurrent = current - state.supplyFault = fault - state.pcieLtssm = pcie_ltssm - msg.valid = True - except Exception as e: - if not model_loading or not isinstance(e, usb1.USBErrorTimeout): - self.usb.close() - self.usb_failed = True - return msg diff --git a/openpilot/system/hardware/chestnut/status.py b/openpilot/system/hardware/chestnut/status.py index 34456b2c1..c3321a397 100644 --- a/openpilot/system/hardware/chestnut/status.py +++ b/openpilot/system/hardware/chestnut/status.py @@ -21,13 +21,14 @@ class ChestnutStatus: self.power_lost = False self.power_restored = False self.link_failures = 0 + self.model_loading_seen = False self.model_attempted = False self.overheated = False self.usb_seen = False self.usb_failed = False def update(self, offroad: bool, branch: str, usb_state: list[dict], firmware_failed: bool, - model_active: bool | None, state, usb_failed: bool, set_alert) -> None: + model_loading: bool, model_active: bool | None, state, set_alert) -> None: detected = [d for d in usb_state if is_chestnut_usb_id(d["vendorId"], d["productId"], include_bootloader=True)] devices = [d for d in detected if is_chestnut_usb_id(d["vendorId"], d["productId"])] firmware_ok = len(devices) == 1 and devices[0]["product"] == CHESTNUT_USB_PRODUCT @@ -39,15 +40,16 @@ class ChestnutStatus: self.power_lost = False self.power_restored = False self.link_failures = 0 + self.model_loading_seen = False self.model_attempted = False self.usb_seen = firmware_ok self.usb_failed = False - self.model_attempted |= model_active is not None + self.model_loading_seen |= model_loading + self.model_attempted |= self.model_loading_seen and not model_loading and model_active is not None - if not offroad: - self.usb_seen |= firmware_ok - self.usb_failed = not offroad and self.usb_seen and (not firmware_ok or usb_failed) + if not offroad and self.usb_seen and not firmware_ok: + self.usb_failed = True if not offroad and state is not None: powered = state.supplyVoltage >= CHESTNUT_POWERED_VOLTAGE @@ -78,28 +80,17 @@ class ChestnutStatus: release = branch in CHESTNUT_RELEASE_BRANCHES missing = self.usb_failed or (offroad and release and time.monotonic() - self.started > 10. and len(detected) != 1) slow_usb = offroad and len(devices) == 1 and devices[0]["speedMbps"] < 5000 - update_failed = offroad and firmware_failed - compiled = firmware_ok and chestnut_compiled() - uncompiled = offroad and firmware_ok and not compiled - + set_alert("Offroad_ChestnutBranch", not release and len(devices) == 1) + set_alert("Offroad_ChestnutNotDetected", missing) + set_alert("Offroad_ChestnutOverheated", self.overheated, f"{state.tempC:.0f} °C" if state is not None else None) + set_alert("Offroad_ChestnutUsbSlow", slow_usb, f"{devices[0]['speedMbps']} Mbps" if slow_usb else None) if self.power_lost: pcie_alert = ("Chestnut power restored. 12V is stable again, cycle ignition." if self.power_restored else "Chestnut power disconnected. Check 12V connection, then cycle ignition." if self.power_unavailable else "Chestnut power lost. Possibly caused by an engine-crank voltage drop. Check 12V connection, then cycle ignition.") else: pcie_alert = "Chestnut GPU unavailable. PCIe link is not up. Check the GPU is securely seated." - - alerts = ( - ("Offroad_ChestnutNotDetected", missing, None), - ("Offroad_ChestnutUpdateFailed", update_failed, None), - ("Offroad_ChestnutUncompiled", uncompiled, None), - ("Offroad_ChestnutPcieUnavailable", self.pcie_failed, pcie_alert), - ("Offroad_ChestnutOverheated", self.overheated, f"{state.tempC:.0f} °C" if state is not None else None), - ("Offroad_ChestnutUsbSlow", slow_usb, f"{devices[0]['speedMbps']} Mbps" if slow_usb else None), - ) - active_alert = next((name for name, active, _ in alerts if active), None) - - set_alert("Offroad_ChestnutBranch", not release and len(devices) == 1 and not missing) - for name, _, extra_text in alerts: - set_alert(name, name == active_alert, extra_text) + set_alert("Offroad_ChestnutPcieUnavailable", self.pcie_failed, pcie_alert) + set_alert("Offroad_ChestnutUncompiled", offroad and firmware_ok and not chestnut_compiled()) + set_alert("Offroad_ChestnutUpdateFailed", offroad and firmware_failed) self.offroad = offroad