diff --git a/cereal/log.capnp b/cereal/log.capnp index b6ccf2f55f..70508f36e2 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -488,6 +488,7 @@ struct DeviceState @0xa4d8b5af2aa492eb { pmicTempC @39 :List(Float32); intakeTempC @46 :Float32; exhaustTempC @47 :Float32; + caseTempC @48 :Float32; maxTempC @44 :Float32; # max of other temps, used to control fan thermalZones @38 :List(ThermalZone); thermalStatus @14 :ThermalStatus; diff --git a/system/hardware/base.py b/system/hardware/base.py index 0f8b7d8680..c7c765f20a 100644 --- a/system/hardware/base.py +++ b/system/hardware/base.py @@ -1,11 +1,54 @@ +import os from abc import abstractmethod, ABC -from collections import namedtuple +from dataclasses import dataclass, fields from cereal import log -ThermalConfig = namedtuple('ThermalConfig', ['cpu', 'gpu', 'mem', 'pmic', 'intake', 'exhaust']) NetworkType = log.DeviceState.NetworkType +@dataclass +class ThermalZone: + # a zone from /sys/class/thermal/thermal_zone* + name: str # a.k.a type + scale: float = 1000. # scale to get degrees in C + zone_number = -1 + + def read(self) -> float: + if self.zone_number < 0: + for n in os.listdir("/sys/devices/virtual/thermal"): + if not n.startswith("thermal_zone"): + continue + with open(os.path.join("/sys/devices/virtual/thermal", n, "type")) as f: + if f.read().strip() == self.name: + self.zone_number = int(n.removeprefix("thermal_zone")) + break + + try: + with open(f"/sys/devices/virtual/thermal/thermal_zone{self.zone_number}/temp") as f: + return int(f.read()) / self.scale + except FileNotFoundError: + return 0 + +@dataclass +class ThermalConfig: + cpu: list[ThermalZone] | None = None + gpu: list[ThermalZone] | None = None + pmic: list[ThermalZone] | None = None + memory: ThermalZone | None = None + intake: ThermalZone | None = None + exhaust: ThermalZone | None = None + case: ThermalZone | None = None + + def get_msg(self): + ret = {} + for f in fields(ThermalConfig): + v = getattr(self, f.name) + if v is not None: + if isinstance(v, list): + ret[f.name + "TempC"] = [x.read() for x in v] + else: + ret[f.name + "TempC"] = v.read() + return ret class HardwareBase(ABC): @staticmethod @@ -84,9 +127,8 @@ class HardwareBase(ABC): def shutdown(self): pass - @abstractmethod def get_thermal_config(self): - pass + return ThermalConfig() @abstractmethod def set_screen_brightness(self, percentage): diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index 15b144ec4e..e9b645691c 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -51,41 +51,6 @@ OFFROAD_DANGER_TEMP = 75 prev_offroad_states: dict[str, tuple[bool, str | None]] = {} -tz_by_type: dict[str, int] | None = None -def populate_tz_by_type(): - global tz_by_type - tz_by_type = {} - for n in os.listdir("/sys/devices/virtual/thermal"): - if not n.startswith("thermal_zone"): - continue - with open(os.path.join("/sys/devices/virtual/thermal", n, "type")) as f: - tz_by_type[f.read().strip()] = int(n.removeprefix("thermal_zone")) - -def read_tz(x): - if x is None: - return 0 - - if isinstance(x, str): - if tz_by_type is None: - populate_tz_by_type() - x = tz_by_type[x] - - try: - with open(f"/sys/devices/virtual/thermal/thermal_zone{x}/temp") as f: - return int(f.read()) - except FileNotFoundError: - return 0 - - -def read_thermal(thermal_config): - dat = messaging.new_message('deviceState', valid=True) - dat.deviceState.cpuTempC = [read_tz(z) / thermal_config.cpu[1] for z in thermal_config.cpu[0]] - dat.deviceState.gpuTempC = [read_tz(z) / thermal_config.gpu[1] for z in thermal_config.gpu[0]] - dat.deviceState.memoryTempC = read_tz(thermal_config.mem[0]) / thermal_config.mem[1] - dat.deviceState.pmicTempC = [read_tz(z) / thermal_config.pmic[1] for z in thermal_config.pmic[0]] - dat.deviceState.intakeTempC = read_tz(thermal_config.intake[0]) / thermal_config.intake[1] - dat.deviceState.exhaustTempC = read_tz(thermal_config.exhaust[0]) / thermal_config.exhaust[1] - return dat def set_offroad_alert_if_changed(offroad_alert: str, show_alert: bool, extra_text: str | None=None): @@ -234,7 +199,8 @@ def hardware_thread(end_event, hw_queue) -> None: if (sm.frame % round(SERVICE_LIST['pandaStates'].frequency * DT_HW) != 0) and not ign_edge: continue - msg = read_thermal(thermal_config) + msg = messaging.new_message('deviceState', valid=True) + msg.deviceState = thermal_config.get_msg() msg.deviceState.deviceType = HARDWARE.get_device_type() try: diff --git a/system/hardware/pc/hardware.py b/system/hardware/pc/hardware.py index a2386b92d2..017a449c90 100644 --- a/system/hardware/pc/hardware.py +++ b/system/hardware/pc/hardware.py @@ -1,7 +1,7 @@ import random from cereal import log -from openpilot.system.hardware.base import HardwareBase, ThermalConfig +from openpilot.system.hardware.base import HardwareBase NetworkType = log.DeviceState.NetworkType NetworkStrength = log.DeviceState.NetworkStrength @@ -53,9 +53,6 @@ class Pc(HardwareBase): def shutdown(self): print("SHUTDOWN!") - def get_thermal_config(self): - return ThermalConfig(cpu=((None,), 1), gpu=((None,), 1), mem=(None, 1), pmic=((None,), 1), intake=(None, 1), exhaust=(None, 1)) - def set_screen_brightness(self, percentage): pass diff --git a/system/hardware/tici/hardware.py b/system/hardware/tici/hardware.py index 8950329841..b69de4dd96 100644 --- a/system/hardware/tici/hardware.py +++ b/system/hardware/tici/hardware.py @@ -10,7 +10,7 @@ from pathlib import Path from cereal import log from openpilot.common.gpio import gpio_set, gpio_init, get_irqs_for_action -from openpilot.system.hardware.base import HardwareBase, ThermalConfig +from openpilot.system.hardware.base import HardwareBase, ThermalConfig, ThermalZone from openpilot.system.hardware.tici import iwlist from openpilot.system.hardware.tici.pins import GPIO from openpilot.system.hardware.tici.amplifier import Amplifier @@ -323,17 +323,19 @@ class Tici(HardwareBase): os.system("sudo poweroff") def get_thermal_config(self): - intake, exhaust = (None, 1), (None, 1) + intake, exhaust, case = None, None, None if self.get_device_type() == "mici": - intake = ("intake", 1000) - exhaust = ("exhaust", 1000) - return ThermalConfig(cpu=([f"cpu{i}-silver-usr" for i in range(4)] + - [f"cpu{i}-gold-usr" for i in range(4)], 1000), - gpu=(("gpu0-usr", "gpu1-usr"), 1000), - mem=("ddr-usr", 1000), - pmic=(("pm8998_tz", "pm8005_tz"), 1000), + case = ThermalZone("case") + intake = ThermalZone("intake") + exhaust = ThermalZone("exhaust") + return ThermalConfig(cpu=[ThermalZone(f"cpu{i}-silver-usr") for i in range(4)] + + [ThermalZone(f"cpu{i}-gold-usr") for i in range(4)], + gpu=[ThermalZone("gpu0-usr"), ThermalZone("gpu1-usr")], + memory=ThermalZone("ddr-usr"), + pmic=[ThermalZone("pm8998_tz"), ThermalZone("pm8005_tz")], intake=intake, - exhaust=exhaust) + exhaust=exhaust, + case=case) def set_screen_brightness(self, percentage): try: