Files
sunnypilot/system/manager/manager.py
DevTekVE ee5c1c4507 Modular Assistive Driving System (MADS) (#446)
* allow re-regage

* bump opendbc

* bump panda

* apply pause/resume fix for hyundai (should do this in a separate PR)

* bump opendbc

* fix

* rename

* Fix?

* make sure to disengage for allow always cars

* fix

* combine

* more fix

* not needed

* check if engagement is from openpilot's state machine

* Rename

* fix panda safety

* fix

* no fake lfa button for @devtekve ;)

* fix non drive gear re-engage

* fix settings

* combine

* add replace method

* use replace

* remoev already checks if it exists

* fix

* group

* add todo

* reserve events

* cleaner

* hyundai: only allow for cars with lfa button

* sunnyParams

* make sure it's car only

* Move car-specific changes to opendbc

* no need

* bump opendbc

* more fixes

* no more available

* more!

* final?

* always emit user disable

* no longer needed

* move unit test

* add sunnypilot to unit tests

* bump opendbc

* use new cereal

* bump opendbc

* static analysis

* no unittest

* no need available

* UI border update

* show MADS updates

* Add TODO

* no longer needed

* fix changed events

* fix cluster enabled

* don't add pre enable if not long

* should use enabled

* enabled <-> active

* better format

* bump opendbc

* static analysis

* static analysis

* Rename test as collector was dying

* Show our overriding

* Revert "show MADS updates"

This reverts commit daf0ad62

Revert "fix changed events"

This reverts commit 31d8c97f

* ignoring reserved events

* adjusting creation delays

* back to stock

removing allow_cancel

* should be enabled

* revert

* silent lkas disable

* no need

* user disable tests

* just warning

* MUST REMOVE test process replay

* fix no entry

* fixme

* bump opendbc

* need this check

* cleanup

* allow entering paused state if no entry from disabled

* brake hold should apply to all

* in lists

* update unit test

* simpler

* unused

* same thing

* fix

* only mads in enabled state and long in disabled state

* unify silent enable

* do this for dlob

* bump submodules

* fix

* bump submodules

* bump opendbc

* less frequent

* more events

* fix

* allow no entry to paused for non-drive gears

* fix

* use cereal

* Revert "allow no entry to paused for non-drive gears"

This reverts commit 6d64a4dd9c.

* allow in all

* Revert "allow in all"

This reverts commit 6375f14891.

* should not be all!

* rename for clarity

* silent park brake

* flipped

* bump submodules

* Bump to latest mads-new panda

* bump panda

* more nissan

* bump panda

* bump msgq

* bump panda

* bump submodules

* bump opendbc

* bump opendbc

* improving the state

* Revert "PlayStation® model (#34133)"

This reverts commit 5160bee543.

* should be none

* bump panda

* bump opendbc

* Apply suggestions from code review

* bump panda

* bump ref panda

* add todo-sp

* bump panda ref

* bump more panda

* changing refs

* nuke nuke nuke

* use sunny's newer states

* bump with new panda

* bump panda

* Parse more flags from alt exp, more tests, hyundai main cruise allowed

* Parse more flags from alt exp, more tests, hyundai main cruise allowed

* missed

* mutation for controls allowed rising edge

* ford mutation

* license

* remove

* unused

* bump submodules

* use always allowed mads button alt exp

* fix

* whitelist jason's lastname to codespell

* test_processes: update ref logs to 82c0278

* bump submodules

* bump submodules

* bump submodules

* bump panda

* add controls mismatch lateral event

* Simplify lateral disengagement logic for MADS configuration

Reversed the conditional to align the logic with the `disengage_lateral_on_brake` parameter. This ensures that lateral disengagement behavior is more intuitive and matches the expected configuration. Improves code readability and reduces potential misconfigurations.

* remove unified engagement mode in panda

* controls allow should be allowed at all times

* squash! treat MADS button as user entry

* heartbeat for mads

* heartbeat mismatch exit control

* remove always allow mads button from alt

* move to safety_mads

* remove main cruise allowed from alt

* bump panda

* heartbeat engaged mads mismatch mutation test

* bump panda

* use mads the third panda

* ignore pre enable with mads

* only force exit if actually actuating

* use brake signal instead of pedal events when dlob is active

* fix tests

* fix panda tests

* bump panda

* new events to retain long blocks

* format

* uem: do not engage mads if long is engaged

* bump submodules

* fix not allowed engaged bug

* block uem from engaging

* flipped

* use different heartbeat check if dlob

* hard code to skip heartbeat check

* remove toyota lta status for lkas, causes weird behaviors

* block tesla

* bump panda

* bump to merged panda

* bump opendbc

* bump opendbc

* bump opendbc

* bump opendbc

* Apply suggestions from code review

* code ignore spells

* needs to be in carstate

* Bump opendbc

* Update MADS toggle descriptions for clarity.

Added notes to clarify behavior of the "MadsMainCruiseAllowed" setting, particularly its impact on vehicles without LFA/LKAS buttons. This ensures users are informed about potential implications when disabling this feature.

* Updating translations + Adding spanish

* Disengage Lateral on Brake -> Pause Lateral on Brake

* test_processes: update ref logs to dd41005

* Apply suggestions from code review

* fix mads button not allowed

* bump submodules

* bump submodule

* test_processes: update ref logs to 0a0b998

* has multiple lists

* Revert "has multiple lists"

This reverts commit a37c1d26fe.

* base

* Reapply "has multiple lists"

This reverts commit d1cd8dcc81.

* migrate mads toggles to sp panel

* this is why it keeps crashing

* house keeping

* more housekeeping

* more housekeeping

* don't show description by default (yet)

* reset to main panel when clicked away

* more

* some more with interactions

* don't stretch cause it looks weird with descriptions

* simpler to handle offroad transition

* some are toggleable while onroad

* remove unused event

* slight cleanup

* default to true for HKG main cruise toggle

* append to list after

* add Customize MADS to UI preview

* simpler

* move to sp list

* how tf was this removed

* update mads settings button on show event

* test_processes: update ref logs to efa9c32

---------

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-01-04 20:48:02 -05:00

239 lines
7.4 KiB
Python
Executable File

#!/usr/bin/env python3
import datetime
import os
import signal
import sys
import traceback
from cereal import log
import cereal.messaging as messaging
import openpilot.system.sentry as sentry
from openpilot.common.params import Params, ParamKeyType
from openpilot.common.text_window import TextWindow
from openpilot.system.hardware import HARDWARE
from openpilot.system.manager.helpers import unblock_stdout, write_onroad_params, save_bootlog
from openpilot.system.manager.process import ensure_running
from openpilot.system.manager.process_config import managed_processes
from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_ID
from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.system.version import get_build_metadata, terms_version, training_version
from openpilot.system.hardware.hw import Paths
def manager_init() -> None:
save_bootlog()
build_metadata = get_build_metadata()
params = Params()
params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START)
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
if build_metadata.release_channel:
params.clear_all(ParamKeyType.DEVELOPMENT_ONLY)
default_params: list[tuple[str, str | bytes]] = [
("CompletedTrainingVersion", "0"),
("DisengageOnAccelerator", "0"),
("GsmMetered", "1"),
("HasAcceptedTerms", "0"),
("LanguageSetting", "main_en"),
("OpenpilotEnabledToggle", "1"),
("LongitudinalPersonality", str(log.LongitudinalPersonality.standard)),
]
sunnypilot_default_params: list[tuple[str, str | bytes]] = [
("Mads", "1"),
("MadsMainCruiseAllowed", "1"),
("MadsPauseLateralOnBrake", "0"),
("MadsUnifiedEngagementMode", "1"),
("ModelManager_LastSyncTime", "0"),
("ModelManager_ModelsCache", "")
]
if params.get_bool("RecordFrontLock"):
params.put_bool("RecordFront", True)
# set unset params
for k, v in (default_params + sunnypilot_default_params):
if params.get(k) is None:
params.put(k, v)
# Create folders needed for msgq
try:
os.mkdir(Paths.shm_path())
except FileExistsError:
pass
except PermissionError:
print(f"WARNING: failed to make {Paths.shm_path()}")
# set version params
params.put("Version", build_metadata.openpilot.version)
params.put("TermsVersion", terms_version)
params.put("TrainingVersion", training_version)
params.put("GitCommit", build_metadata.openpilot.git_commit)
params.put("GitCommitDate", build_metadata.openpilot.git_commit_date)
params.put("GitBranch", build_metadata.channel)
params.put("GitRemote", build_metadata.openpilot.git_origin)
params.put_bool("IsTestedBranch", build_metadata.tested_channel)
params.put_bool("IsReleaseBranch", build_metadata.release_channel)
# set dongle id
reg_res = register(show_spinner=True)
if reg_res:
dongle_id = reg_res
else:
serial = params.get("HardwareSerial")
raise Exception(f"Registration failed for device {serial}")
os.environ['DONGLE_ID'] = dongle_id # Needed for swaglog
os.environ['GIT_ORIGIN'] = build_metadata.openpilot.git_normalized_origin # Needed for swaglog
os.environ['GIT_BRANCH'] = build_metadata.channel # Needed for swaglog
os.environ['GIT_COMMIT'] = build_metadata.openpilot.git_commit # Needed for swaglog
if not build_metadata.openpilot.is_dirty:
os.environ['CLEAN'] = '1'
# init logging
sentry.init(sentry.SentryProject.SELFDRIVE)
cloudlog.bind_global(dongle_id=dongle_id,
version=build_metadata.openpilot.version,
origin=build_metadata.openpilot.git_normalized_origin,
branch=build_metadata.channel,
commit=build_metadata.openpilot.git_commit,
dirty=build_metadata.openpilot.is_dirty,
device=HARDWARE.get_device_type())
# preimport all processes
for p in managed_processes.values():
p.prepare()
def manager_cleanup() -> None:
# send signals to kill all procs
for p in managed_processes.values():
p.stop(block=False)
# ensure all are killed
for p in managed_processes.values():
p.stop(block=True)
cloudlog.info("everything is dead")
def manager_thread() -> None:
cloudlog.bind(daemon="manager")
cloudlog.info("manager start")
cloudlog.info({"environ": os.environ})
params = Params()
ignore: list[str] = []
if params.get("DongleId", encoding='utf8') in (None, UNREGISTERED_DONGLE_ID):
ignore += ["manage_athenad", "uploader"]
if os.getenv("NOBOARD") is not None:
ignore.append("pandad")
ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0]
sm = messaging.SubMaster(['deviceState', 'carParams'], poll='deviceState')
pm = messaging.PubMaster(['managerState'])
write_onroad_params(False, params)
ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore)
started_prev = False
while True:
sm.update(1000)
started = sm['deviceState'].started
if started and not started_prev:
params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION)
elif not started and started_prev:
params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION)
# update onroad params, which drives pandad's safety setter thread
if started != started_prev:
write_onroad_params(started, params)
started_prev = started
ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore)
running = ' '.join("{}{}\u001b[0m".format("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
for p in managed_processes.values() if p.proc)
print(running)
cloudlog.debug(running)
# send managerState
msg = messaging.new_message('managerState', valid=True)
msg.managerState.processes = [p.get_process_state_msg() for p in managed_processes.values()]
pm.send('managerState', msg)
# Exit main loop when uninstall/shutdown/reboot is needed
shutdown = False
for param in ("DoUninstall", "DoShutdown", "DoReboot"):
if params.get_bool(param):
shutdown = True
params.put("LastManagerExitReason", f"{param} {datetime.datetime.now()}")
cloudlog.warning(f"Shutting down manager - {param} set")
if shutdown:
break
def main() -> None:
manager_init()
if os.getenv("PREPAREONLY") is not None:
return
# SystemExit on sigterm
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit(1))
try:
manager_thread()
except Exception:
traceback.print_exc()
sentry.capture_exception()
finally:
manager_cleanup()
params = Params()
if params.get_bool("DoUninstall"):
cloudlog.warning("uninstalling")
HARDWARE.uninstall()
elif params.get_bool("DoReboot"):
cloudlog.warning("reboot")
HARDWARE.reboot()
elif params.get_bool("DoShutdown"):
cloudlog.warning("shutdown")
HARDWARE.shutdown()
if __name__ == "__main__":
unblock_stdout()
try:
main()
except KeyboardInterrupt:
print("got CTRL-C, exiting")
except Exception:
add_file_handler(cloudlog)
cloudlog.exception("Manager failed to start")
try:
managed_processes['ui'].stop()
except Exception:
pass
# Show last 3 lines of traceback
error = traceback.format_exc(-3)
error = "Manager failed to start\n\n" + error
with TextWindow(error) as t:
t.wait_for_exit()
raise
# manual exit because we are forked
sys.exit(0)