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 <noreply@anthropic.com>
This commit is contained in:
tajniak81andClaude Opus 5.5 committed 2026-10-22 14:56:49 +02:00
1 parent e3917e6f25
commit 727850bb0e
3 files changed
+51 -2

No files matched your search

@@ -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))
}
}
@@ -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<Int>("updateFrequency") ?: 10,
call.argument<Int>("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
@@ -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.
}
}
}