park mode: pause self-driving processes, keep car ECU heartbeat
When the car is in park (ignition still on), pause everything that
isn't strictly needed and keep controlsd in a minimal-cycle keepalive
mode so the car's steering ECU keeps seeing the LFA/LKAS CAN-FD
messages and stays in tester mode (no steering fault on shift to drive).
controlsd:
- Detects park gear, writes ParkMode to /dev/shm/params.
- park_mode_tick(): publishes a do-nothing CarControl through
self.card (CarController.update unconditionally appends the
steering messages every cycle, which is the actual heartbeat).
Still runs clearpilot_state_control so the LFA/debug button +
ScreenDisplayMode keep working in park.
- After park→drive, stay in keepalive-tick mode until SubMaster
reports all services healthy (an 8s hard cap as safety net).
Avoids the burst of commIssue alerts that would otherwise fire
while modeld/plannerd/paramsd/torqued/dmonitoringd/calibrationd/
frogpilot_process spin back up from cold.
manager / process_config:
- New gating helpers _park_mode(), only_onroad_active,
driverview_active, always_run_unless_parked.
- Re-gated to only_onroad_active: modeld, sensord, soundd, locationd,
calibrationd, torqued, paramsd, plannerd, radard, speed_logicd.
- Re-gated to driverview_active: dmonitoringmodeld, dmonitoringd.
- frogpilot_process → always_run_unless_parked (preserves offroad
behavior, only pauses when ignition+parked).
- controlsd stays plain only_onroad — it's the writer + heartbeat.
- ParkMode registered in params.cc, defaulted to "0" in manager_init.
thermald + fan_controller (broken-tree rule, ported):
- thermald subscribes to carState; passes standstill, is_parked,
cruise_engaged into fan_controller.update.
- New fan range rules:
parked → 0-100% (no floor, full cooling)
cruise on → 30-100%
standstill → 10-100%
moving → 30-100%
ignition off → 0-30% (existing)
dashcamd: already park-aware via gear-driven trip lifecycle — no
change needed.
Verified: build clean. Launched on bench: park mode kicks in on
startup (gearShifter=unknown initially → eventually park),
modeld/plannerd/paramsd/torqued/calibrationd/frogpilot_process
correctly disappear from the manager process list, gpsd/dashcamd/
ui/controlsd/pandad stay alive, ParkMode=1 in /dev/shm/params.
This commit is contained in:
@@ -261,6 +261,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
|||||||
{"DashcamState", PERSISTENT},
|
{"DashcamState", PERSISTENT},
|
||||||
{"IsDaylight", PERSISTENT},
|
{"IsDaylight", PERSISTENT},
|
||||||
{"ModelFps", PERSISTENT},
|
{"ModelFps", PERSISTENT},
|
||||||
|
{"ParkMode", PERSISTENT},
|
||||||
{"ModelStandby", PERSISTENT},
|
{"ModelStandby", PERSISTENT},
|
||||||
{"ModelStandbyTs", PERSISTENT},
|
{"ModelStandbyTs", PERSISTENT},
|
||||||
{"ScreenRecorderDebug", PERSISTENT},
|
{"ScreenRecorderDebug", PERSISTENT},
|
||||||
|
|||||||
Binary file not shown.
@@ -78,8 +78,19 @@ class Controls:
|
|||||||
|
|
||||||
self.params_memory.put_bool("CPTLkasButtonAction", False)
|
self.params_memory.put_bool("CPTLkasButtonAction", False)
|
||||||
self.params_memory.put_int("ScreenDisplayMode", 0)
|
self.params_memory.put_int("ScreenDisplayMode", 0)
|
||||||
|
self.params_memory.put_bool("ParkMode", False)
|
||||||
# CLEARPILOT: edge tracking for park→drive auto-wake of screen
|
# CLEARPILOT: edge tracking for park→drive auto-wake of screen
|
||||||
self.was_driving_gear = False
|
self.was_driving_gear = False
|
||||||
|
# CLEARPILOT: park-mode bookkeeping. While ParkMode is true, controlsd
|
||||||
|
# only does a minimal tick (carControl heartbeat through card so the
|
||||||
|
# hyundai CAN-FD steering messages keep the car ECU's tester mode alive)
|
||||||
|
# and writes ParkMode so manager pauses modeld/locationd/etc. After
|
||||||
|
# exiting park, stay in keepalive-tick mode until SubMaster reports
|
||||||
|
# everything healthy again, with an 8-second hard cap so we never get
|
||||||
|
# stuck if some service is permanently broken.
|
||||||
|
self.park_mode = False
|
||||||
|
self.park_exit_frame = -1
|
||||||
|
self.PARK_GRACE_MAX_FRAMES = int(8.0 / DT_CTRL)
|
||||||
|
|
||||||
self.radarless_model = self.params.get("Model", encoding='utf-8') in RADARLESS_MODELS
|
self.radarless_model = self.params.get("Model", encoding='utf-8') in RADARLESS_MODELS
|
||||||
|
|
||||||
@@ -237,6 +248,16 @@ class Controls:
|
|||||||
CS = self.data_sample()
|
CS = self.data_sample()
|
||||||
cloudlog.timestamp("Data sampled")
|
cloudlog.timestamp("Data sampled")
|
||||||
|
|
||||||
|
# CLEARPILOT: park mode — short-circuit to a minimal tick (just publish
|
||||||
|
# the carControl heartbeat to keep the car's ECU in tester mode). Also
|
||||||
|
# covers the post-park grace window so commIssue events don't flash
|
||||||
|
# while modeld / locationd / paramsd / etc. spin back up.
|
||||||
|
self._update_park_mode(CS)
|
||||||
|
if self.park_mode or self._in_park_exit_grace():
|
||||||
|
self._park_mode_tick(CS)
|
||||||
|
self.CS_prev = CS
|
||||||
|
return
|
||||||
|
|
||||||
self.update_events(CS)
|
self.update_events(CS)
|
||||||
self.update_frogpilot_events(CS)
|
self.update_frogpilot_events(CS)
|
||||||
self.update_clearpilot_events(CS)
|
self.update_clearpilot_events(CS)
|
||||||
@@ -1238,6 +1259,38 @@ class Controls:
|
|||||||
if (len(CS.buttonEvents) > 0):
|
if (len(CS.buttonEvents) > 0):
|
||||||
print (CS.buttonEvents)
|
print (CS.buttonEvents)
|
||||||
|
|
||||||
|
def _update_park_mode(self, CS):
|
||||||
|
# CLEARPILOT: track the gear-park flag and write ParkMode for manager.
|
||||||
|
# On park→drive transition, capture the frame so the grace window starts.
|
||||||
|
in_park = CS.gearShifter == GearShifter.park
|
||||||
|
if in_park != self.park_mode:
|
||||||
|
self.park_mode = in_park
|
||||||
|
self.params_memory.put_bool("ParkMode", in_park)
|
||||||
|
if not in_park:
|
||||||
|
self.park_exit_frame = self.sm.frame
|
||||||
|
|
||||||
|
def _in_park_exit_grace(self):
|
||||||
|
# CLEARPILOT: after exiting park, keep doing the minimal tick until the
|
||||||
|
# restarting modeld/locationd/etc. are publishing again, so they don't
|
||||||
|
# trip a burst of commIssue alerts. Exit grace as soon as SubMaster
|
||||||
|
# reports everything healthy; otherwise hold for up to 8 seconds as a
|
||||||
|
# safety cap (avoids getting stuck if a service is permanently broken).
|
||||||
|
if self.park_exit_frame < 0:
|
||||||
|
return False
|
||||||
|
if (self.sm.frame - self.park_exit_frame) >= self.PARK_GRACE_MAX_FRAMES:
|
||||||
|
return False
|
||||||
|
return not (self.sm.all_checks() and not self.card.can_rcv_timeout)
|
||||||
|
|
||||||
|
def _park_mode_tick(self, CS):
|
||||||
|
# CLEARPILOT: minimal-cycle keepalive. Publishes a do-nothing carControl
|
||||||
|
# via card → CarController.update → sendcan, which sends the LFA/LKAS
|
||||||
|
# CAN-FD messages every cycle. Those send unconditionally regardless of
|
||||||
|
# CC.enabled / latActive, so the car's steering ECU keeps seeing the
|
||||||
|
# heartbeat and stays in tester mode.
|
||||||
|
CC = car.CarControl.new_message()
|
||||||
|
self.clearpilot_state_control(CC, CS)
|
||||||
|
self.card.controls_update(CC, self.frogpilot_variables)
|
||||||
|
|
||||||
def clearpilot_state_control(self, CC, CS):
|
def clearpilot_state_control(self, CC, CS):
|
||||||
# CLEARPILOT: pure UI plumbing — does not modify CC/actuators. Maintains
|
# CLEARPILOT: pure UI plumbing — does not modify CC/actuators. Maintains
|
||||||
# ScreenDisplayMode (5-state machine driven by the LFA/debug button + gear
|
# ScreenDisplayMode (5-state machine driven by the LFA/debug button + gear
|
||||||
|
|||||||
@@ -321,6 +321,7 @@ def manager_init(frogpilot_functions) -> None:
|
|||||||
("ModelFps", "20"),
|
("ModelFps", "20"),
|
||||||
("ModelStandby", "0"),
|
("ModelStandby", "0"),
|
||||||
("ModelStandbyTs", "0"),
|
("ModelStandbyTs", "0"),
|
||||||
|
("ParkMode", "0"),
|
||||||
("ShutdownTouchReset", "0"),
|
("ShutdownTouchReset", "0"),
|
||||||
("TelemetryEnabled", "0"),
|
("TelemetryEnabled", "0"),
|
||||||
("VpnEnabled", "1"),
|
("VpnEnabled", "1"),
|
||||||
|
|||||||
@@ -39,6 +39,25 @@ def always_run(started, params, CP: car.CarParams) -> bool:
|
|||||||
def only_onroad(started: bool, params, CP: car.CarParams) -> bool:
|
def only_onroad(started: bool, params, CP: car.CarParams) -> bool:
|
||||||
return started
|
return started
|
||||||
|
|
||||||
|
# CLEARPILOT: park-mode gating. controlsd writes ParkMode to /dev/shm/params
|
||||||
|
# when the car is parked (ignition still on). Manager's ensure_running loop
|
||||||
|
# reads these gates every tick and stops/starts processes naturally as the
|
||||||
|
# flag flips. controlsd itself stays on plain `started` (it's the one
|
||||||
|
# writing the flag and emitting the steering keepalive to the car's ECU).
|
||||||
|
def _park_mode() -> bool:
|
||||||
|
return Params("/dev/shm/params").get_bool("ParkMode")
|
||||||
|
|
||||||
|
def only_onroad_active(started: bool, params, CP: car.CarParams) -> bool:
|
||||||
|
return started and not _park_mode()
|
||||||
|
|
||||||
|
def driverview_active(started: bool, params, CP: car.CarParams) -> bool:
|
||||||
|
return driverview(started, params, CP) and not _park_mode()
|
||||||
|
|
||||||
|
def always_run_unless_parked(started, params, CP: car.CarParams) -> bool:
|
||||||
|
# Same as always_run, but pauses while ignition is on and the car is parked.
|
||||||
|
# Preserves offroad behavior; only changes the started+parked case.
|
||||||
|
return not (started and _park_mode())
|
||||||
|
|
||||||
def only_offroad(started, params, CP: car.CarParams) -> bool:
|
def only_offroad(started, params, CP: car.CarParams) -> bool:
|
||||||
return not started
|
return not started
|
||||||
|
|
||||||
@@ -63,26 +82,26 @@ procs = [
|
|||||||
PythonProcess("micd", "system.micd", iscar),
|
PythonProcess("micd", "system.micd", iscar),
|
||||||
PythonProcess("timed", "system.timed", always_run, enabled=not PC),
|
PythonProcess("timed", "system.timed", always_run, enabled=not PC),
|
||||||
|
|
||||||
PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(not PC or WEBCAM)),
|
PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview_active, enabled=(not PC or WEBCAM)),
|
||||||
# CLEARPILOT: disabled segment + camera logging — no rlog/qlog or .hevc
|
# CLEARPILOT: disabled segment + camera logging — no rlog/qlog or .hevc
|
||||||
# files written to /data/media/0/realdata. We don't use comma's upload/
|
# files written to /data/media/0/realdata. We don't use comma's upload/
|
||||||
# replay pipeline. Keep deleter running for any leftover cleanup.
|
# replay pipeline. Keep deleter running for any leftover cleanup.
|
||||||
# NativeProcess("encoderd", "system/loggerd", ["./encoderd"], allow_logging),
|
# NativeProcess("encoderd", "system/loggerd", ["./encoderd"], allow_logging),
|
||||||
# NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar),
|
# NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar),
|
||||||
# NativeProcess("loggerd", "system/loggerd", ["./loggerd"], allow_logging),
|
# NativeProcess("loggerd", "system/loggerd", ["./loggerd"], allow_logging),
|
||||||
NativeProcess("modeld", "selfdrive/modeld", ["./modeld"], only_onroad),
|
NativeProcess("modeld", "selfdrive/modeld", ["./modeld"], only_onroad_active),
|
||||||
#NativeProcess("mapsd", "selfdrive/navd", ["./mapsd"], only_onroad),
|
#NativeProcess("mapsd", "selfdrive/navd", ["./mapsd"], only_onroad),
|
||||||
#PythonProcess("navmodeld", "selfdrive.modeld.navmodeld", only_onroad),
|
#PythonProcess("navmodeld", "selfdrive.modeld.navmodeld", only_onroad),
|
||||||
NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC),
|
NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad_active, enabled=not PC),
|
||||||
NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None), always_watchdog=only_offroad),
|
NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None), always_watchdog=only_offroad),
|
||||||
PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad),
|
PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad_active),
|
||||||
NativeProcess("locationd", "selfdrive/locationd", ["./locationd"], only_onroad),
|
NativeProcess("locationd", "selfdrive/locationd", ["./locationd"], only_onroad_active),
|
||||||
NativeProcess("boardd", "selfdrive/boardd", ["./boardd"], always_run, enabled=False),
|
NativeProcess("boardd", "selfdrive/boardd", ["./boardd"], always_run, enabled=False),
|
||||||
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad),
|
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad_active),
|
||||||
PythonProcess("torqued", "selfdrive.locationd.torqued", only_onroad),
|
PythonProcess("torqued", "selfdrive.locationd.torqued", only_onroad_active),
|
||||||
PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad),
|
PythonProcess("controlsd", "selfdrive.controls.controlsd", only_onroad),
|
||||||
PythonProcess("deleter", "system.loggerd.deleter", always_run),
|
PythonProcess("deleter", "system.loggerd.deleter", always_run),
|
||||||
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
|
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview_active, enabled=(not PC or WEBCAM)),
|
||||||
# PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI), # Fixme
|
# PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI), # Fixme
|
||||||
# CLEARPILOT: replacement for qcomgpsd (whose diag interface is broken on this device).
|
# CLEARPILOT: replacement for qcomgpsd (whose diag interface is broken on this device).
|
||||||
# Uses Quectel modem AT commands via mmcli. Self-driving does NOT consume this; locationd
|
# Uses Quectel modem AT commands via mmcli. Self-driving does NOT consume this; locationd
|
||||||
@@ -90,18 +109,18 @@ procs = [
|
|||||||
PythonProcess("gpsd", "system.clearpilot.gpsd", qcomgps, enabled=TICI),
|
PythonProcess("gpsd", "system.clearpilot.gpsd", qcomgps, enabled=TICI),
|
||||||
# CLEARPILOT: speed/cruise overlay daemon. Reads gpsLocation + carState, writes display
|
# CLEARPILOT: speed/cruise overlay daemon. Reads gpsLocation + carState, writes display
|
||||||
# params for the onroad UI; asserts ClearpilotPlayDing on speed-limit warning transitions.
|
# params for the onroad UI; asserts ClearpilotPlayDing on speed-limit warning transitions.
|
||||||
PythonProcess("speed_logicd", "selfdrive.clearpilot.speed_logicd", only_onroad),
|
PythonProcess("speed_logicd", "selfdrive.clearpilot.speed_logicd", only_onroad_active),
|
||||||
# CLEARPILOT: dashcam — VisionIPC frames → OMX H.264 → 3-min MP4 segments + SRT
|
# CLEARPILOT: dashcam — VisionIPC frames → OMX H.264 → 3-min MP4 segments + SRT
|
||||||
# GPS subtitles in /data/media/0/videos/. Manages its own trip lifecycle.
|
# GPS subtitles in /data/media/0/videos/. Manages its own trip lifecycle.
|
||||||
NativeProcess("dashcamd", "selfdrive/clearpilot", ["./dashcamd"], always_run),
|
NativeProcess("dashcamd", "selfdrive/clearpilot", ["./dashcamd"], always_run),
|
||||||
# PythonProcess("ugpsd", "system.ugpsd", only_onroad, enabled=TICI),
|
# PythonProcess("ugpsd", "system.ugpsd", only_onroad, enabled=TICI),
|
||||||
#PythonProcess("navd", "selfdrive.navd.navd", only_onroad),
|
#PythonProcess("navd", "selfdrive.navd.navd", only_onroad),
|
||||||
PythonProcess("pandad", "selfdrive.boardd.pandad", always_run),
|
PythonProcess("pandad", "selfdrive.boardd.pandad", always_run),
|
||||||
PythonProcess("paramsd", "selfdrive.locationd.paramsd", only_onroad),
|
PythonProcess("paramsd", "selfdrive.locationd.paramsd", only_onroad_active),
|
||||||
NativeProcess("ubloxd", "system/ubloxd", ["./ubloxd"], ublox, enabled=TICI),
|
NativeProcess("ubloxd", "system/ubloxd", ["./ubloxd"], ublox, enabled=TICI),
|
||||||
PythonProcess("pigeond", "system.ubloxd.pigeond", ublox, enabled=TICI),
|
PythonProcess("pigeond", "system.ubloxd.pigeond", ublox, enabled=TICI),
|
||||||
PythonProcess("plannerd", "selfdrive.controls.plannerd", only_onroad),
|
PythonProcess("plannerd", "selfdrive.controls.plannerd", only_onroad_active),
|
||||||
PythonProcess("radard", "selfdrive.controls.radard", only_onroad),
|
PythonProcess("radard", "selfdrive.controls.radard", only_onroad_active),
|
||||||
PythonProcess("thermald", "selfdrive.thermald.thermald", always_run),
|
PythonProcess("thermald", "selfdrive.thermald.thermald", always_run),
|
||||||
PythonProcess("tombstoned", "selfdrive.tombstoned", always_run, enabled=not PC),
|
PythonProcess("tombstoned", "selfdrive.tombstoned", always_run, enabled=not PC),
|
||||||
# PythonProcess("updated", "selfdrive.updated.updated", always_run, enabled=not PC),
|
# PythonProcess("updated", "selfdrive.updated.updated", always_run, enabled=not PC),
|
||||||
@@ -115,7 +134,7 @@ procs = [
|
|||||||
|
|
||||||
# FrogPilot processes
|
# FrogPilot processes
|
||||||
PythonProcess("fleet_manager", "selfdrive.frogpilot.fleetmanager.fleet_manager", always_run),
|
PythonProcess("fleet_manager", "selfdrive.frogpilot.fleetmanager.fleet_manager", always_run),
|
||||||
PythonProcess("frogpilot_process", "selfdrive.frogpilot.frogpilot_process", always_run),
|
PythonProcess("frogpilot_process", "selfdrive.frogpilot.frogpilot_process", always_run_unless_parked),
|
||||||
]
|
]
|
||||||
|
|
||||||
managed_processes = {p.name: p for p in procs}
|
managed_processes = {p.name: p for p in procs}
|
||||||
@@ -8,7 +8,8 @@ from openpilot.selfdrive.controls.lib.pid import PIDController
|
|||||||
|
|
||||||
class BaseFanController(ABC):
|
class BaseFanController(ABC):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def update(self, cur_temp: float, ignition: bool) -> int:
|
def update(self, cur_temp: float, ignition: bool, standstill: bool = False,
|
||||||
|
is_parked: bool = True, cruise_engaged: bool = False) -> int:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -20,9 +21,30 @@ class TiciFanController(BaseFanController):
|
|||||||
self.last_ignition = False
|
self.last_ignition = False
|
||||||
self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_TRML))
|
self.controller = PIDController(k_p=0, k_i=4e-3, k_f=1, rate=(1 / DT_TRML))
|
||||||
|
|
||||||
def update(self, cur_temp: float, ignition: bool) -> int:
|
def update(self, cur_temp: float, ignition: bool, standstill: bool = False,
|
||||||
self.controller.neg_limit = -(100 if ignition else 30)
|
is_parked: bool = True, cruise_engaged: bool = False) -> int:
|
||||||
self.controller.pos_limit = -(30 if ignition else 0)
|
# CLEARPILOT fan range rules. neg_limit is the most-negative the PID can
|
||||||
|
# output (= max fan %); pos_limit is the inverse of the floor (= -min fan %).
|
||||||
|
# parked → 0-100% (full range, no floor)
|
||||||
|
# in drive + cruise engaged → 30-100%
|
||||||
|
# in drive + cruise off + standstill → 10-100%
|
||||||
|
# in drive + cruise off + moving → 30-100%
|
||||||
|
# ignition off → 0-30% (existing behavior)
|
||||||
|
if not ignition:
|
||||||
|
self.controller.neg_limit = -30
|
||||||
|
self.controller.pos_limit = 0
|
||||||
|
elif is_parked:
|
||||||
|
self.controller.neg_limit = -100
|
||||||
|
self.controller.pos_limit = 0
|
||||||
|
elif cruise_engaged:
|
||||||
|
self.controller.neg_limit = -100
|
||||||
|
self.controller.pos_limit = -30
|
||||||
|
elif standstill:
|
||||||
|
self.controller.neg_limit = -100
|
||||||
|
self.controller.pos_limit = -10
|
||||||
|
else:
|
||||||
|
self.controller.neg_limit = -100
|
||||||
|
self.controller.pos_limit = -30
|
||||||
|
|
||||||
if ignition != self.last_ignition:
|
if ignition != self.last_ignition:
|
||||||
self.controller.reset()
|
self.controller.reset()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from pathlib import Path
|
|||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
import cereal.messaging as messaging
|
import cereal.messaging as messaging
|
||||||
from cereal import log
|
from cereal import car, log
|
||||||
from cereal.services import SERVICE_LIST
|
from cereal.services import SERVICE_LIST
|
||||||
from openpilot.common.dict_helpers import strip_deprecated_keys
|
from openpilot.common.dict_helpers import strip_deprecated_keys
|
||||||
from openpilot.common.filter_simple import FirstOrderFilter
|
from openpilot.common.filter_simple import FirstOrderFilter
|
||||||
@@ -164,7 +164,8 @@ def hw_state_thread(end_event, hw_queue):
|
|||||||
|
|
||||||
def thermald_thread(end_event, hw_queue) -> None:
|
def thermald_thread(end_event, hw_queue) -> None:
|
||||||
pm = messaging.PubMaster(['deviceState', 'frogpilotDeviceState'])
|
pm = messaging.PubMaster(['deviceState', 'frogpilotDeviceState'])
|
||||||
sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates"], poll="pandaStates")
|
# CLEARPILOT: subscribe to carState for the fan-range gear/cruise gating
|
||||||
|
sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates", "carState"], poll="pandaStates")
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
@@ -290,7 +291,21 @@ def thermald_thread(end_event, hw_queue) -> None:
|
|||||||
msg.deviceState.maxTempC = all_comp_temp
|
msg.deviceState.maxTempC = all_comp_temp
|
||||||
|
|
||||||
if fan_controller is not None:
|
if fan_controller is not None:
|
||||||
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"])
|
# CLEARPILOT: feed gear + cruise state to widen the fan range when parked
|
||||||
|
# (0-100%, no floor) so the device can fully cool the moment it can't be
|
||||||
|
# heard over road noise. carState may not be alive when ignition is off
|
||||||
|
# or before pandad/card hand it through — default safe to "parked".
|
||||||
|
if sm.seen['carState']:
|
||||||
|
cs = sm['carState']
|
||||||
|
standstill = cs.standstill
|
||||||
|
is_parked = cs.gearShifter == car.CarState.GearShifter.park
|
||||||
|
else:
|
||||||
|
standstill = False
|
||||||
|
is_parked = True
|
||||||
|
cruise_engaged = sm['controlsState'].enabled if sm.seen['controlsState'] else False
|
||||||
|
msg.deviceState.fanSpeedPercentDesired = fan_controller.update(
|
||||||
|
all_comp_temp, onroad_conditions["ignition"], standstill,
|
||||||
|
is_parked=is_parked, cruise_engaged=cruise_engaged)
|
||||||
|
|
||||||
is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (time.monotonic() - off_ts > 60 * 5))
|
is_offroad_for_5_min = (started_ts is None) and ((not started_seen) or (off_ts is None) or (time.monotonic() - off_ts > 60 * 5))
|
||||||
if is_offroad_for_5_min and offroad_comp_temp > OFFROAD_DANGER_TEMP:
|
if is_offroad_for_5_min and offroad_comp_temp > OFFROAD_DANGER_TEMP:
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user