fix(live-atc): stop ATC from repeating/looping and hanging on silent frequencies

- Neutralize the local engine's autonomous auto-advance (evaluateAutoTransitions/
  evaluateSimpleAutoFlow) — the Python backend now drives state exclusively via
  moveToSilent; the old walker could self-answer pilot states and race the
  backend, producing loops.
- Dedupe applyBackendDecision's ATC log entry + TTS: moveToSilent no longer logs
  say_tpl for backend-driven auto-advanced states (suppressSay), and a
  lastAppliedSay guard drops a repeated decision from overlapping sources
  (transmit reply, telemetry tick, silence timeout).
- Cap consecutive silence-timeout re-fires on the same state at 2 instead of
  re-arming forever.
- Pause telemetry forwarding while a pilot transmission is in flight, and guard
  against overlapping telemetry POSTs.
- Add request timeouts to TTS (20s) and backend transmit/telemetry/timeout
  (30s)/createSession (60s) calls so a hung request can no longer freeze the
  whole session.

Verified live against the Python backend: a full clearance→taxi chain now logs
each ATC line exactly once instead of 2-3x.
This commit is contained in:
itsrubberduck
2026-07-16 23:47:52 +02:00
parent 3c284716bb
commit 3fe431ea47
6 changed files with 139 additions and 22 deletions

View File

@@ -169,7 +169,7 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import useCommunicationsEngine from "../../shared/utils/communicationsEngine";
import { useAuthStore } from '~/stores/auth'
@@ -478,6 +478,10 @@ const applyBackendDecision = (
const handleSimControlResult = (
result: import('../../shared/utils/simControl').SimControlCommandResult,
) => session.handleSimControlResult(result)
// Named distinctly from the `transmitInFlight` destructured from `session` below
// (used for AI-traffic gating) — same underlying ref, just needed earlier here
// for useSimBridgeSync, which is constructed before `session` exists.
const bridgeSyncTransmitInFlight = computed(() => session.transmitInFlight.value)
const speech = useRadioSpeech(engine, freq, speechInterrupt, {
setLastTransmission,
@@ -576,6 +580,7 @@ const {
startRecording,
stopRecording,
onCommandResult: handleSimControlResult,
transmitInFlight: bridgeSyncTransmitInFlight,
})
const session = useLiveAtcSession(engine, {