mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 17:13:54 +08:00
Jetlink offroad alert and optional owner
- jetlinkd is ancillary: a dead owner sends the drive back to the small model and no longer blocks engaging - a crashed owner now restarts, as porting.md asks - Offroad_JetlinkUnavailable says why a Jetlink the user turned on cannot run (jetlink's reason()) - JetlinkBigModel, JetlinkCatalog and JetlinkRuntime are no longer used params_keys.h changed: libparams_c.so, libcommon.a and the prebuilt manifest need regenerating with the prebuilt build. Until then the tracked params library does not know Offroad_JetlinkUnavailable, so hardwared skips that alert (logged once) and it stays inert.
This commit is contained in:
@@ -257,6 +257,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"Offroad_ConnectivityNeeded", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_ConnectivityNeededPrompt", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_ExcessiveActuation", {PERSISTENT, JSON}},
|
||||
{"Offroad_JetlinkUnavailable", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_NoFirmware", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
{"Offroad_Pairing", {CLEAR_ON_MANAGER_START, JSON}},
|
||||
{"Offroad_Recalibration", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
|
||||
@@ -347,11 +348,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"ChestnutLoading", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"JetlinkMode", {PERSISTENT, INT, "0"}},
|
||||
{"JetlinkChargePhone", {PERSISTENT, BOOL, "0"}},
|
||||
{"JetlinkBigModel", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"JetlinkCatalog", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"JetlinkSpec", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"JetlinkPointers", {PERSISTENT | DONT_LOG, JSON}},
|
||||
{"JetlinkProgress", {CLEAR_ON_MANAGER_START | DONT_LOG, JSON}},
|
||||
{"JetlinkRuntime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | DONT_LOG, JSON}},
|
||||
{"Version", {PERSISTENT, STRING}},
|
||||
};
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
"text": "Chestnut USB link is slow. Check the USB cable. The current speed is %1.",
|
||||
"severity": 0
|
||||
},
|
||||
"Offroad_JetlinkUnavailable": {
|
||||
"text": "Jetlink unavailable: %1",
|
||||
"severity": 0
|
||||
},
|
||||
"Offroad_UnregisteredHardware": {
|
||||
"text": "Failed to register with comma.ai backend. It will not connect or upload to comma.ai servers, and receives no support from comma.ai. If this is a device purchased at comma.ai/shop, open a ticket at https://comma.ai/support.",
|
||||
"severity": 1
|
||||
|
||||
@@ -25,7 +25,7 @@ def process_health(processes):
|
||||
return drive
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name', ['android_autod', 'galaxy_hotspot'])
|
||||
@pytest.mark.parametrize('name', ['android_autod', 'galaxy_hotspot', 'jetlinkd'])
|
||||
@pytest.mark.parametrize('running,requested', [(False, False), (False, True), (True, True)])
|
||||
def test_optional_lifecycle_does_not_block_engagement(name, running, requested):
|
||||
drive = process_health([(name, running, requested)])
|
||||
|
||||
@@ -17,7 +17,7 @@ from openpilot.cereal import log
|
||||
from openpilot.cereal.services import SERVICE_LIST
|
||||
from openpilot.common.utils import strip_deprecated_keys
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.params import Params, UnknownKeyName
|
||||
from openpilot.common.realtime import DT_HW
|
||||
from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
|
||||
from openpilot.common.hardware import HARDWARE, COMMA_HARDWARE, PC
|
||||
@@ -33,6 +33,7 @@ from openpilot.system.hardware.chestnut.monitoring import chestnut_state_thread
|
||||
from openpilot.system.hardware.chestnut.status import ChestnutStatus
|
||||
from openpilot.common.version import terms_version, training_version
|
||||
from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID
|
||||
from openpilot.starpilot.models import jetlink_adapter
|
||||
|
||||
ThermalStatus = log.DeviceState.ThermalStatus
|
||||
NetworkType = log.DeviceState.NetworkType
|
||||
@@ -123,6 +124,23 @@ def set_offroad_alert_if_changed(offroad_alert: str, show_alert: bool, extra_tex
|
||||
prev_offroad_states[offroad_alert] = (show_alert, extra_text)
|
||||
set_offroad_alert(offroad_alert, show_alert, extra_text)
|
||||
|
||||
# A prebuilt params library not yet regenerated lacks Offroad_JetlinkUnavailable: the alert is skipped rather than
|
||||
# taking hardwared down. The guard can go once the library knows the key.
|
||||
jetlink_alert_usable = True
|
||||
|
||||
|
||||
def update_jetlink_alert() -> None:
|
||||
"""Why the Jetlink the user turned on cannot run. Reads files only; with the link off, jetlink binds nothing."""
|
||||
global jetlink_alert_usable
|
||||
if not jetlink_alert_usable:
|
||||
return
|
||||
reason = jetlink_adapter.hooks().reason()
|
||||
try:
|
||||
set_offroad_alert_if_changed("Offroad_JetlinkUnavailable", reason is not None, extra_text=reason)
|
||||
except UnknownKeyName:
|
||||
jetlink_alert_usable = False
|
||||
cloudlog.warning("Offroad_JetlinkUnavailable is not in this params library: Jetlink's offroad alert is off")
|
||||
|
||||
def touch_thread(end_event):
|
||||
count = 0
|
||||
|
||||
@@ -394,6 +412,7 @@ def hardware_thread(end_event, hw_queue) -> None:
|
||||
extra_text = f"{offroad_comp_temp:.1f}C"
|
||||
show_alert = (not onroad_conditions["device_temp_good"] or not startup_conditions["device_temp_engageable"]) and onroad_conditions["ignition"]
|
||||
set_offroad_alert_if_changed("Offroad_TemperatureTooHigh", show_alert, extra_text=extra_text)
|
||||
update_jetlink_alert()
|
||||
|
||||
if show_alert:
|
||||
msg.deviceState.fanSpeedPercentDesired = 100
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
"""hardwared's Jetlink offroad alert says why the link cannot run, and never takes hardwared down."""
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from openpilot.common.params import UnknownKeyName
|
||||
from openpilot.system.hardware import hardwared
|
||||
|
||||
|
||||
class TestJetlinkAlert(unittest.TestCase):
|
||||
def setUp(self):
|
||||
hardwared.jetlink_alert_usable = True
|
||||
self.addCleanup(setattr, hardwared, 'jetlink_alert_usable', True)
|
||||
self.reason = Mock(return_value='no usable gadget')
|
||||
self.link = SimpleNamespace(hooks=lambda: SimpleNamespace(reason=self.reason))
|
||||
self.set_alert = Mock()
|
||||
for name, value in (('jetlink_adapter', self.link), ('set_offroad_alert_if_changed', self.set_alert), ('cloudlog', Mock())):
|
||||
patcher = patch.object(hardwared, name, value)
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_the_alert_follows_jetlinks_reason(self):
|
||||
hardwared.update_jetlink_alert()
|
||||
self.set_alert.assert_called_with('Offroad_JetlinkUnavailable', True, extra_text='no usable gadget')
|
||||
self.reason.return_value = None
|
||||
hardwared.update_jetlink_alert()
|
||||
self.set_alert.assert_called_with('Offroad_JetlinkUnavailable', False, extra_text=None)
|
||||
|
||||
def test_with_the_link_off_jetlink_is_not_bound(self):
|
||||
from jetlink.openpilot.hooks import Hooks
|
||||
from openpilot.starpilot.models.jetlink_adapter import API
|
||||
factory = Mock()
|
||||
self.link.hooks = lambda: Hooks(API, factory, link_on=lambda: False)
|
||||
hardwared.update_jetlink_alert()
|
||||
factory.assert_not_called()
|
||||
self.set_alert.assert_called_once_with('Offroad_JetlinkUnavailable', False, extra_text=None)
|
||||
|
||||
def test_a_params_library_without_the_key_turns_the_alert_off_once(self):
|
||||
self.set_alert.side_effect = UnknownKeyName(b'Offroad_JetlinkUnavailable')
|
||||
hardwared.update_jetlink_alert()
|
||||
hardwared.update_jetlink_alert()
|
||||
self.set_alert.assert_called_once()
|
||||
hardwared.cloudlog.warning.assert_called_once()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -137,7 +137,8 @@ procs = [
|
||||
enabled=platform.system() == "Linux", restart_on_exit=True),
|
||||
PythonProcess("galaxy_hotspot", "openpilot.starpilot.galaxy.hotspot_daemon", galaxy_local,
|
||||
enabled=COMMA_HARDWARE and not os.environ.get('OPENPILOT_PREFIX'), restart_on_exit=True),
|
||||
JetlinkProcess("jetlinkd", "openpilot.starpilot.models.jetlink_adapter", jetlink_enabled, enabled=COMMA_HARDWARE),
|
||||
JetlinkProcess("jetlinkd", "openpilot.starpilot.models.jetlink_adapter", jetlink_enabled, enabled=COMMA_HARDWARE,
|
||||
restart_on_exit=True),
|
||||
PythonProcess("android_autod", "openpilot.starpilot.system.android_auto.daemon", android_auto_enabled, enabled=COMMA_HARDWARE,
|
||||
restart_on_exit=True), # boot can race bluetoothd (NoSuchUnit)
|
||||
# Boot can race the audio device; selfdrived blocks engagement while soundd is down.
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
# Projection and local phone access are optional even when requested.
|
||||
# Their lifecycles remain visible in managerState and their own status.
|
||||
ANCILLARY_PROCESSES = frozenset({'android_autod', 'galaxy_hotspot'})
|
||||
# A dead Jetlink owner sends the drive back to the small model.
|
||||
ANCILLARY_PROCESSES = frozenset({'android_autod', 'galaxy_hotspot', 'jetlinkd'})
|
||||
|
||||
|
||||
def driving_process_failures(processes) -> set[str]:
|
||||
|
||||
@@ -102,6 +102,21 @@ class TestProcessRecovery(unittest.TestCase):
|
||||
self.assertEqual(p.restart_failures, 0)
|
||||
self.assertEqual(p.restart_at, 0)
|
||||
|
||||
def test_a_crashed_jetlink_owner_restarts(self):
|
||||
from openpilot.system.manager.process_config import managed_processes
|
||||
configured = managed_processes["jetlinkd"]
|
||||
self.assertTrue(configured.restart_on_exit)
|
||||
p = type(configured)("jetlinkd", "unused", lambda started, params, cp: True, restart_on_exit=configured.restart_on_exit)
|
||||
self.step(p)
|
||||
old = p.proc
|
||||
old.exitcode = 1
|
||||
self.step(p)
|
||||
self.assertIs(p.proc, old)
|
||||
self.now += 1.01
|
||||
self.step(p)
|
||||
self.assertIsNot(p.proc, old)
|
||||
self.assertTrue(old.joined)
|
||||
|
||||
def test_boot_racing_processes_restart(self):
|
||||
from openpilot.system.manager.process_config import procs
|
||||
restarting = {p.name for p in procs if getattr(p, "restart_on_exit", False)}
|
||||
|
||||
Reference in New Issue
Block a user