Stop reporting the flight controller's serial as the drone's
getSerialNumber() is a BaseComponent method, so every component answers for
itself — and the bridge reads it off the flight controller. A Mavic Pro reports
08RDE1J00103H1 (what DJI Go labels "Flight Controller SN") where the airframe
sticker, and the registration, say 08QDE3H012032E. We were publishing the former
as the drone's serial, onto records that exist to satisfy BEK 1649 §5.
Same trap as 002e484, where a component's own firmware stood in for the
aircraft's, but with no correct source to switch to: MSDK v4 exposes no
aircraft-level serial at all — BaseProduct offers only the model and the
firmware package version — so the registered serial can only be typed by hand.
So split the two rather than pick one:
serial the airframe's, hand-entered, and the only one that
reaches the logbook and the CSV export
flight_controller_serial what the aircraft reports; auto-filled on connect,
and what POST /api/drones/auto now upserts on
Keying auto-add on the flight controller's serial keeps the fleet recognising a
connected drone without typing — it is stable per airframe — while leaving the
compliance record's serial to the pilot. A flight controller swapped in a repair
now costs a duplicate fleet entry to merge, where before it would have quietly
rewritten what the logbook claimed the drone was.
Note droneInput.payload() is a whole-record write, so any UI editing a drone must
round-trip flightControllerSerial; blanking it forks the drone into a duplicate
on its next connect. Drones.vue carries it through the edit form for that reason.
The migration copies existing serials into flight_controller_serial rather than
moving them: every current value came from auto-add and is therefore a flight
controller's, but a pilot may since have corrected one by hand and this cannot
tell them apart. Copying keeps auto-add matching the airframes it matched before.
Applied to the remote PocketBase, where drones held no records, so the backfill
was a no-op there.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
33595c99e8
commit
183c83c177
14 files changed
+297
-139
No files matched your search
+29
-13
@@ -56,11 +56,24 @@ class DjiSdkBridge(
|
||||
private var eventSink: EventChannel.EventSink? = null
|
||||
|
||||
/**
|
||||
* Aircraft identity, resolved asynchronously after connect (see [fetchIdentity])
|
||||
* and cached so [connectionMap] can answer `getProductInfo` without re-fetching.
|
||||
* Volatile: written from the SDK's callback threads, read from the main thread.
|
||||
* The *flight controller's* serial number — not the aircraft's.
|
||||
*
|
||||
* `getSerialNumber` is a `BaseComponent` method, so every component answers for
|
||||
* itself, and this one is read off the flight controller: on a Mavic Pro it
|
||||
* returns 08RDE1J00103H1 where the airframe sticker says 08QDE3H012032E (the
|
||||
* same number DJI Go shows as "Flight Controller SN"). MSDK v4 exposes no
|
||||
* aircraft-level serial at all — `BaseProduct` offers only the model and the
|
||||
* firmware package version — so the airframe serial the pilot registers with
|
||||
* cannot be read over the SDK and is typed by hand in the Web App instead.
|
||||
*
|
||||
* Still worth publishing: it is stable per airframe (short of swapping the
|
||||
* flight controller), which is what the fleet's auto-add keys on.
|
||||
*
|
||||
* Resolved asynchronously after connect (see [fetchIdentity]) and cached so
|
||||
* [connectionMap] can answer `getProductInfo` without re-fetching. Volatile:
|
||||
* written from the SDK's callback threads, read from the main thread.
|
||||
*/
|
||||
@Volatile private var serialNumber: String? = null
|
||||
@Volatile private var flightControllerSerial: String? = null
|
||||
|
||||
@Volatile private var firmwareVersion: String? = null
|
||||
|
||||
@@ -215,7 +228,7 @@ class DjiSdkBridge(
|
||||
"model" to model,
|
||||
"firmware" to (product?.firmwarePackageVersion ?: firmwareVersion),
|
||||
"controllerFirmware" to controllerFirmwareVersion,
|
||||
"serial" to serialNumber,
|
||||
"flightControllerSerial" to flightControllerSerial,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -243,7 +256,7 @@ class DjiSdkBridge(
|
||||
* worst case is dropping a just-resolved value that the retry chain re-fetches.
|
||||
*/
|
||||
private fun resetIdentityCache() {
|
||||
serialNumber = null
|
||||
flightControllerSerial = null
|
||||
firmwareVersion = null
|
||||
controllerFirmwareVersion = null
|
||||
}
|
||||
@@ -252,14 +265,15 @@ class DjiSdkBridge(
|
||||
private fun clearIdentity() {
|
||||
mainHandler.post {
|
||||
identityGeneration++
|
||||
serialNumber = null
|
||||
flightControllerSerial = null
|
||||
firmwareVersion = null
|
||||
controllerFirmwareVersion = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the aircraft's serial number and firmware version.
|
||||
* Resolves the flight controller's serial number and the aircraft's firmware
|
||||
* version. (The airframe serial is not among them — see [flightControllerSerial].)
|
||||
*
|
||||
* Neither is readable the instant a product connects — `getFirmwarePackageVersion`
|
||||
* returns null and the flight controller's callbacks fail until the SDK has
|
||||
@@ -284,13 +298,15 @@ class DjiSdkBridge(
|
||||
|
||||
val controller = (product as? Aircraft)?.flightController
|
||||
if (controller != null) {
|
||||
if (serialNumber == null) {
|
||||
if (flightControllerSerial == null) {
|
||||
// Answers for the flight controller itself, not the airframe — see
|
||||
// [flightControllerSerial] for why there is no aircraft-level source.
|
||||
controller.getSerialNumber(object : CommonCallbacks.CompletionCallbackWith<String> {
|
||||
override fun onSuccess(value: String?) {
|
||||
if (value.isNullOrBlank()) return
|
||||
mainHandler.post {
|
||||
if (generation != identityGeneration || serialNumber == value) return@post
|
||||
serialNumber = value
|
||||
if (generation != identityGeneration || flightControllerSerial == value) return@post
|
||||
flightControllerSerial = value
|
||||
emitIdentity()
|
||||
}
|
||||
}
|
||||
@@ -325,7 +341,7 @@ class DjiSdkBridge(
|
||||
)
|
||||
}
|
||||
|
||||
val pending = serialNumber == null || firmwareVersion == null || controllerFirmwareVersion == null
|
||||
val pending = flightControllerSerial == null || firmwareVersion == null || controllerFirmwareVersion == null
|
||||
if (pending && attempt + 1 < IDENTITY_MAX_ATTEMPTS) {
|
||||
mainHandler.postDelayed({ fetchIdentity(generation, attempt + 1) }, IDENTITY_RETRY_MS)
|
||||
}
|
||||
@@ -342,7 +358,7 @@ class DjiSdkBridge(
|
||||
emit(
|
||||
mapOf(
|
||||
"type" to "identity",
|
||||
"serial" to serialNumber,
|
||||
"flightControllerSerial" to flightControllerSerial,
|
||||
"firmware" to firmwareVersion,
|
||||
"controllerFirmware" to controllerFirmwareVersion,
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user