feat: live expected communication from backend, radio pronunciation toggle, and frequency validation

- Expected Communication card now driven entirely by backend responses:
  controller speech from controller_say_rendered, expected pilot phrase
  from expected_pilot_template — both update after every state transition
  instead of relying on the static COMMUNICATION_STEPS array
- Initial expected pilot phrase seeded on session creation via the new
  expected_pilot_template field in CreateSessionResponse, so the card
  shows the correct text before any transmission
- Radio pronunciation toggle (mdi-radio / mdi-text) on the card applies
  normalizeRadioPhrase() (ICAO alphabet, wun/too/tree, callsign expansion)
  to both ATC speech and expected pilot phrase
- Frequency validation at transmit time: if the pilot's active frequency
  does not match the state's expected frequency (resolved from
  frequency_name → flight-plan variable) a canned ATC reply is played
  and the backend is not called
- Flight-plan variables (callsign, squawk, destination, ATIS, SID, stand,
  initial altitude) now passed to createSession() as variable_overrides
  so backend sessions use real flight-plan data from the first state
- Backend session variables synced into local vars after each response
  so frontend and backend stay consistent
- Removed hardcoded frequency chip from Expected Communication card

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
leubeem
2026-05-20 17:51:44 +02:00
parent c9f51f757f
commit 62721ced4c
2 changed files with 123 additions and 23 deletions

View File

@@ -4,6 +4,7 @@ export interface RadioSessionResponse {
current_state: string
variables: Record<string, any>
flags: Record<string, boolean>
expected_pilot_template: string | null
}
export interface RadioTransmitResponse {
@@ -27,10 +28,13 @@ export function useRadioBackend() {
return (config.public.radioBackendUrl as string) || 'http://127.0.0.1:8000'
}
async function createSession(flowSlug: string): Promise<RadioSessionResponse> {
async function createSession(
flowSlug: string,
variables?: Record<string, any>,
): Promise<RadioSessionResponse> {
return await $fetch<RadioSessionResponse>(`${baseUrl()}/api/radio/session`, {
method: 'POST',
body: { flow_slug: flowSlug },
body: { flow_slug: flowSlug, variables: variables ?? null },
})
}