From 727850bb0e10b1cd95e2e616c7ef706ed9aa8237 Mon Sep 17 00:00:00 2001 From: tajniak81 <13187254+tajniak81@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:56:49 +0200 Subject: [PATCH] Log simulator starts, stops and diagnostics to a file on the tablet The Mavic Pro simulator closes by itself every 3-11 minutes and logcat on the dev tablet rolls over before it can be read. SimLog appends the simulator's on/off changes (with the flight state at that moment), the app's own start and stop commands, and every diagnostics change to files/sim_log.txt. Co-Authored-By: Claude Opus 5.5 --- .../pilotvault/flyapp/DiagnosticsBridge.kt | 1 + .../flyapp/FlightControllerBridge.kt | 20 ++++++++++-- .../kotlin/com/pilotvault/flyapp/SimLog.kt | 32 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/SimLog.kt diff --git a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/DiagnosticsBridge.kt b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/DiagnosticsBridge.kt index 19cd714..91e9901 100644 --- a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/DiagnosticsBridge.kt +++ b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/DiagnosticsBridge.kt @@ -33,6 +33,7 @@ class DiagnosticsBridge(private val ctx: BridgeCtx) : SubBridge { ctx.mainHandler.post { if (items == last) return@post last = items + SimLog.write(ctx.appContext, "diagnostics: " + items.joinToString(" | ") { "${it["code"]} ${it["reason"]}" }) ctx.emit(mapOf("type" to "diagnostics", "items" to items)) } } diff --git a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/FlightControllerBridge.kt b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/FlightControllerBridge.kt index 36d861e..ea79dc5 100644 --- a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/FlightControllerBridge.kt +++ b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/FlightControllerBridge.kt @@ -25,6 +25,9 @@ import kotlin.math.sqrt */ class FlightControllerBridge(private val ctx: BridgeCtx) : SubBridge { + /** The simulator state last seen, so [SimLog] records only its changes. */ + @Volatile private var lastSimulatorActive: Boolean? = null + private fun fc(): FlightController? = ctx.aircraft()?.flightController override fun handle(call: MethodCall, result: MethodChannel.Result): Boolean { @@ -58,10 +61,12 @@ class FlightControllerBridge(private val ctx: BridgeCtx) : SubBridge { call.argument("updateFrequency") ?: 10, call.argument("satelliteCount") ?: 12, ) + SimLog.write(ctx.appContext, "app: startSimulator") sim.start(data, ctx.completion(result)) } } "stopSimulator" -> guard(controller, result) { + SimLog.write(ctx.appContext, "app: stopSimulator") val sim = it.simulator if (sim == null) ctx.fail(result, "Simulator unavailable on this aircraft") else sim.stop(ctx.completion(result)) @@ -135,8 +140,19 @@ class FlightControllerBridge(private val ctx: BridgeCtx) : SubBridge { if (product !is Aircraft) return val controller = product.flightController ?: return controller.setStateCallback { state -> - val values = telemetry(state) + compass(controller) + - ("simulatorActive" to (try { controller.simulator?.isSimulatorActive } catch (_: Throwable) { null })) + val sim = try { controller.simulator?.isSimulatorActive } catch (_: Throwable) { null } + if (sim != null && sim != lastSimulatorActive) { + if (lastSimulatorActive != null || sim) { + SimLog.write( + ctx.appContext, + "simulator ${if (sim) "ON" else "OFF"}: mode=${state.flightModeString} flying=${state.isFlying} " + + "motors=${state.areMotorsOn()} alt=${state.aircraftLocation?.altitude} sats=${state.satelliteCount} " + + "flightTime=${state.flightTimeInSeconds}s goHome=${state.goHomeExecutionState}", + ) + } + lastSimulatorActive = sim + } + val values = telemetry(state) + compass(controller) + ("simulatorActive" to sim) ctx.mainHandler.post { ctx.hub.putFast(values) } } // Magnetic interference per compass (P6b's interference meter): the diff --git a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/SimLog.kt b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/SimLog.kt new file mode 100644 index 0000000..6df4639 --- /dev/null +++ b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/SimLog.kt @@ -0,0 +1,32 @@ +package com.pilotvault.flyapp + +import android.content.Context +import android.util.Log +import java.io.File +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +/** + * A small file log for the simulator closing by itself (Mavic Pro, every + * 3–11 min, cause unknown): simulator starts/stops with the flight state at + * that moment, and every diagnostics change. logcat on the dev tablet rolls + * over within minutes, so this lives in the app's files dir (`sim_log.txt`, + * capped at [MAX_BYTES]) and is read with `run-as … cat files/sim_log.txt`. + */ +object SimLog { + private const val FILE = "sim_log.txt" + private const val MAX_BYTES = 512 * 1024 + private val time = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.ROOT) + + fun write(context: Context, line: String) { + Log.w("PVSim", line) + try { + val f = File(context.filesDir, FILE) + if (f.length() > MAX_BYTES) f.delete() + synchronized(this) { f.appendText("${time.format(Date())} $line\n") } + } catch (_: Throwable) { + // Logging must never break a flight. + } + } +}