diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index c6d77e3..51feccf 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -98,7 +98,7 @@ class Controls: self.park_mode = False self.park_exit_frame = -1 self.startup_complete_frame = -1 - self.PARK_GRACE_MAX_FRAMES = int(8.0 / DT_CTRL) + self.PARK_GRACE_MAX_FRAMES = int(15.0 / DT_CTRL) self.PARK_STARTUP_DELAY_FRAMES = int(10.0 / DT_CTRL) self.radarless_model = self.params.get("Model", encoding='utf-8') in RADARLESS_MODELS @@ -174,11 +174,20 @@ class Controls: self.state = State.disabled self.enabled = False self.active = False - # CLEARPILOT: track engagement edge for the post-engage controlsdLagging - # suppression window (the loop briefly lags during state transition into - # enabled, which would otherwise pop "Controls Process Lagging: Reboot - # Your Device" right as the user lets the wheel go). + # CLEARPILOT: track engagement edge for the post-engage suppression + # window. Two edges: + # - last_engage_attempt_frame: rises with CS.cruiseState.enabled + # (covers blocked NO_ENTRY attempts — alerts fire BEFORE engage) + # - last_engaged_frame: rises with self.enabled + # (covers state-transition lag once engagement succeeds) + # The grace suppresses controlsdLagging + commIssue for 3 s after either + # edge — long enough for the loop to settle past the engage transition + # and for any briefly-stale services (longitudinalPlan, frogpilotPlan) + # to catch up. Without this, the user gets a flash of scary "Reboot Your + # Device" and "Communication Issue" alerts right as their hands are + # still on the wheel after pressing SET. self.last_engaged_frame = -1 + self.last_engage_attempt_frame = -1 self.POST_ENGAGE_LAG_GRACE_FRAMES = int(3.0 / DT_CTRL) self.soft_disable_timer = 0 self.mismatch_counter = 0 @@ -349,6 +358,12 @@ class Controls: self.events.clear() + # CLEARPILOT: capture rising edge of cruise.enabled so we can suppress + # noisy startup alerts (controlsdLagging, commIssue) for the first 3 + # seconds after the user presses SET. self.CS_prev is last cycle's CS. + if CS.cruiseState.enabled and not self.CS_prev.cruiseState.enabled: + self.last_engage_attempt_frame = self.sm.frame + # Add joystick event, static on cars, dynamic on nonCars if self.joystick_mode: self.events.add(EventName.joystickDebug) @@ -491,14 +506,20 @@ class Controls: self.events.add(EventName.cameraMalfunction) elif not self.sm.all_freq_ok(self.camera_packets): self.events.add(EventName.cameraFrameRate) + # CLEARPILOT: 3-second grace after either edge of "engagement" — covers + # both the cruise SET press (which fires before state_transition runs, + # so it can trigger NO_ENTRY alerts that block engagement) and the + # post-engage state transition lag. + in_engage_grace = ( + (self.last_engaged_frame >= 0 + and (self.sm.frame - self.last_engaged_frame) < self.POST_ENGAGE_LAG_GRACE_FRAMES) + or + (self.last_engage_attempt_frame >= 0 + and (self.sm.frame - self.last_engage_attempt_frame) < self.POST_ENGAGE_LAG_GRACE_FRAMES) + ) + if not REPLAY and self.rk.lagging: - # CLEARPILOT: suppress controlsdLagging for the first 3 seconds after - # engagement — the loop briefly lags during the state transition into - # enabled, which otherwise pops a scary "Reboot Your Device" alert - # just as the user is letting their hands off the wheel. - in_post_engage_lag_grace = (self.last_engaged_frame >= 0 - and (self.sm.frame - self.last_engaged_frame) < self.POST_ENGAGE_LAG_GRACE_FRAMES) - if not in_post_engage_lag_grace: + if not in_engage_grace: self.events.add(EventName.controlsdLagging) if not self.radarless_model: if len(self.sm['radarState'].radarErrors) or (not self.rk.lagging and not self.sm.all_checks(['radarState'])): @@ -514,12 +535,16 @@ class Controls: has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE)) no_system_errors = (not has_disable_events) or (len(self.events) == num_events) if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors: - if not self.sm.all_alive(): - self.events.add(EventName.commIssue) - elif not self.sm.all_freq_ok(): - self.events.add(EventName.commIssueAvgFreq) - else: # invalid or can_rcv_timeout. - self.events.add(EventName.commIssue) + # CLEARPILOT: suppress the commIssue NO_ENTRY alert for the first 3 s + # after engage attempt — services like longitudinalPlan / frogpilotPlan + # often need a beat to validate after a cold spawn. + if not in_engage_grace: + if not self.sm.all_alive(): + self.events.add(EventName.commIssue) + elif not self.sm.all_freq_ok(): + self.events.add(EventName.commIssueAvgFreq) + else: # invalid or can_rcv_timeout. + self.events.add(EventName.commIssue) logs = { 'invalid': [s for s, valid in self.sm.valid.items() if not valid],