mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-04 05:13:46 +08:00
Drive native replay turn arrows and clarify independent simulation resets
This commit is contained in:
@@ -4,8 +4,17 @@ from pathlib import Path
|
||||
from openpilot.selfdrive.ui.ui_state import UIState, device
|
||||
from preparing_awake import PreparationWake
|
||||
from replay_display_hold import ReplayDisplayHold
|
||||
from replay_ui_controls import ReplayUIControls, ReplayStateView, isolated_replay
|
||||
from replay_ui_controls import ReplayUIControls, ReplayStateView, isolated_replay, apply_turn_intent
|
||||
replay_controls=ReplayUIControls(os.environ['ROADSCORE_STATUS_FILE'],enabled=isolated_replay(os.environ))
|
||||
if replay_controls.enabled:
|
||||
from openpilot.selfdrive.ui.mici.onroad.hud_renderer import TurnIntent
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
original_turn_intent=TurnIntent._update_state
|
||||
def replay_turn_intent(self):
|
||||
sm=ui_state.sm
|
||||
if isinstance(sm,ReplayStateView) and apply_turn_intent(self,sm.signal_mode):return
|
||||
return original_turn_intent(self)
|
||||
TurnIntent._update_state=replay_turn_intent
|
||||
display_hold=ReplayDisplayHold(os.environ.get("ROADSCORE_AUDIO_DRAIN_FILE"), enabled=os.environ.get("OPENPILOT_PREFIX")=="roadscore_replay")
|
||||
original_state=UIState._update_state
|
||||
def replay_state(self,*args,**kwargs):
|
||||
|
||||
@@ -41,7 +41,8 @@ def gesture_view(state):
|
||||
if signal in ('left', 'right', 'off'):
|
||||
shaker = state.get('signal_shaker') or {}
|
||||
description = 'Shaker' if shaker.get('rendered_active') else 'Replay signal' if signal != 'off' else 'Recorded signals muted'
|
||||
return f'Simulated {signal} / {description}', 'active', 'simulated_signal'
|
||||
title = f'Simulated {signal}' + (' shaker' if shaker.get('rendered_active') else '')
|
||||
return f'{title} / {description}', 'active', 'simulated_signal'
|
||||
engagement = state.get('engagement_presentation') or {}
|
||||
if engagement.get('enabled') and not engagement.get('simulated') and engagement.get('input_fresh') and engagement.get('rendered_state') == 'transition':
|
||||
return ('Engaged / Opening music' if engagement.get('active') else 'Disengaged / Contained music'), 'active', 'engagement'
|
||||
|
||||
@@ -15,6 +15,26 @@ def isolated_replay(environ):
|
||||
and environ.get('OPENPILOT_PREFIX') == 'roadscore_replay')
|
||||
|
||||
|
||||
def apply_turn_intent(widget, signal_mode):
|
||||
"""Use the existing native arrow textures/animation without inventing events."""
|
||||
if signal_mode not in ('left', 'right', 'off'):
|
||||
return False
|
||||
direction = -1 if signal_mode == 'left' else 1 if signal_mode == 'right' else 0
|
||||
if direction:
|
||||
if not widget._pre or widget._turn_intent_direction != direction:
|
||||
widget._turn_intent_rotation_filter.x = -direction * widget.FADE_IN_ANGLE
|
||||
widget._pre = True
|
||||
widget._turn_intent_direction = direction
|
||||
widget._turn_intent_alpha_filter.update(1)
|
||||
widget._turn_intent_rotation_filter.update(0)
|
||||
else:
|
||||
widget._pre = False
|
||||
widget._turn_intent_direction = 0
|
||||
widget._turn_intent_alpha_filter.update(0)
|
||||
widget._turn_intent_rotation_filter.update(0)
|
||||
return True
|
||||
|
||||
|
||||
class ReplayUIControls:
|
||||
def __init__(self, status_path, *, enabled=False, clock=time.monotonic):
|
||||
self.path = Path(status_path)
|
||||
|
||||
@@ -6,7 +6,7 @@ from types import SimpleNamespace
|
||||
|
||||
from cereal import log, car, custom
|
||||
from cue_timing import REFERENCE
|
||||
from replay_ui_controls import ReplayUIControls, ReplayStateView, isolated_replay
|
||||
from replay_ui_controls import ReplayUIControls, ReplayStateView, isolated_replay, apply_turn_intent
|
||||
|
||||
|
||||
class Subscriber:
|
||||
@@ -104,5 +104,17 @@ class Tests(unittest.TestCase):
|
||||
state=delay.apply(snapshot,dict(sequence=1,callback_wall=99.5,dac_wall=99.8,cues=cues))
|
||||
self.path.write_text(json.dumps(state));self.view.update()
|
||||
self.assertEqual((self.view.mode,self.view.signal_mode),('disengaged','right'))
|
||||
def test_native_turn_arrow_direction_and_recorded_fallback(self):
|
||||
class Filter:
|
||||
x=0
|
||||
def update(self,target):self.target=target
|
||||
widget=SimpleNamespace(_pre=False,_turn_intent_direction=0,FADE_IN_ANGLE=30,
|
||||
_turn_intent_alpha_filter=Filter(),_turn_intent_rotation_filter=Filter())
|
||||
self.assertTrue(apply_turn_intent(widget,'left'))
|
||||
self.assertEqual(widget._turn_intent_direction,-1);self.assertEqual(widget._turn_intent_alpha_filter.target,1)
|
||||
self.assertTrue(apply_turn_intent(widget,'right'));self.assertEqual(widget._turn_intent_direction,1)
|
||||
self.assertTrue(apply_turn_intent(widget,'off'));self.assertEqual(widget._turn_intent_direction,0)
|
||||
self.assertEqual(widget._turn_intent_alpha_filter.target,0)
|
||||
self.assertFalse(apply_turn_intent(widget,'recorded'))
|
||||
|
||||
if __name__=='__main__':unittest.main()
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
<!-- Snackbar for messages -->
|
||||
<div id="snackbar_wrapper"></div>
|
||||
<script type="module" src="/assets/mobile/js/app.js?v=replay-controls-2"></script>
|
||||
<script type="module" src="/assets/mobile/js/app.js?v=replay-controls-3"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RoadScore } from "./views/RoadScore.js?v=replay-controls-2"
|
||||
import { RoadScore } from "./views/RoadScore.js?v=replay-controls-3"
|
||||
import { createApp, h } from "vue"
|
||||
import { AppShell } from "./components/AppShell.js?v=roadscore-calibration-2"
|
||||
import { Home } from "./views/Home.js"
|
||||
|
||||
@@ -137,11 +137,11 @@ export const RoadScore = {
|
||||
</div></GalaxySection>
|
||||
<GalaxySection title="Replay simulation" icon="bi-play-circle" :collapsible="false"><div style="padding:16px">
|
||||
<p class="gx-row__desc">Replay simulation changes displayed engagement, turn signals and music. It does not engage or control the vehicle.</p>
|
||||
<p><strong>{{status.demo?.available ? (status.demo.mode === 'engaged' ? 'Simulated engage' : status.demo.mode === 'disengaged' ? 'Simulated disengage' : 'Using recorded state') : 'Replay unavailable'}}</strong></p>
|
||||
<p><strong>{{status.demo?.available ? (status.demo.mode === 'engaged' ? 'Simulated engage' : status.demo.mode === 'disengaged' ? 'Simulated disengage' : 'Using recorded engagement') : 'Replay unavailable'}}</strong></p>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
||||
<button class="gx-btn" :disabled="busy || !status.demo?.available" @click="action('demo_engagement', {session_id:status.demo.session_id, mode:'engaged'})">Simulate engage</button>
|
||||
<button class="gx-btn" :disabled="busy || !status.demo?.available" @click="action('demo_engagement', {session_id:status.demo.session_id, mode:'disengaged'})">Simulate disengage</button>
|
||||
<button class="gx-btn" :disabled="busy || !status.demo?.available" @click="action('demo_engagement', {session_id:status.demo.session_id, mode:'recorded'})">Use recorded state</button>
|
||||
<button class="gx-btn" :disabled="busy || !status.demo?.available" @click="action('demo_engagement', {session_id:status.demo.session_id, mode:'recorded'})">Recorded engagement</button>
|
||||
</div>
|
||||
<p><strong>{{status.demo?.signal_mode === 'recorded' ? 'Using recorded signals' : 'Simulated signals: ' + (status.demo?.signal_mode || 'recorded')}}</strong></p>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
||||
|
||||
Reference in New Issue
Block a user