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:
tajniak81
2026-07-16 18:36:14 +02:00
co-authored by Claude Opus 4.8
parent 33595c99e8
commit 183c83c177
14 changed files with 297 additions and 139 deletions
@@ -0,0 +1,72 @@
/// <reference path="../pb_data/types.d.ts" />
// Splits the drone's serial in two, because the app had been conflating them.
//
// `getSerialNumber` in MSDK v4 is a `BaseComponent` method: every component
// answers for itself, and the Fly App reads it off the *flight controller*. So
// what a connected aircraft reports — and what auto-add has been storing in
// `serial` — is the flight controller's number, not the one on the airframe's
// sticker that the drone is registered under. On a Mavic Pro the aircraft says
// 08RDE1J00103H1 where the sticker reads 08QDE3H012032E. There is no fix on the
// SDK side: MSDK v4 exposes no aircraft-level serial at all, so the registered
// serial can only ever be typed in by the pilot. (Same trap as the firmware
// versions in 1720300900 — a component reporting its own value, taken for the
// aircraft's — but with no correct source to switch to.)
//
// After this:
// - serial the airframe sticker's number — hand-entered,
// and the one that belongs on a compliance record
// - flight_controller_serial what the aircraft reports; auto-filled, and the
// key POST /api/drones/auto upserts on
//
// Existing values are *copied* into `flight_controller_serial`, not moved: every
// non-empty `serial` today 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 the two apart. Copying keeps auto-add matching the same airframes
// it matched before (a cleared key would fork every drone into a duplicate on
// its next connect) and loses nothing; the Drones tab shows both numbers, so a
// `serial` still holding a flight controller's is visible and correctable.
//
// Apply by copying into your PocketBase deployment's `pb_migrations/` directory
// and restarting. Written for PocketBase v0.22+/v0.23. Idempotent: the field is
// added only if absent and the backfill only writes records whose
// `flight_controller_serial` is still empty, so re-running is a no-op.
//
// Depends on 1720300900_add_drone_identity.js (idx_drones_serial).
migrate(
(app) => {
const drones = app.findCollectionByNameOrId('drones')
if (!drones.fields.find((f) => f.name === 'flight_controller_serial')) {
drones.fields.add(new Field({ name: 'flight_controller_serial', type: 'text', max: 120 }))
}
// One fleet entry per airframe, enforced on both numbers: auto-add keys on
// the flight controller's, so a duplicate there would fork a drone's history
// across two records — the reason idx_drones_serial existed in the first
// place. The airframe serial stays unique too, now on its own terms.
const idx =
'CREATE UNIQUE INDEX `idx_drones_fc_serial` ON `drones` (`flight_controller_serial`)' +
" WHERE `flight_controller_serial` != ''"
if (!drones.indexes.find((i) => i.includes('idx_drones_fc_serial'))) drones.indexes.push(idx)
app.save(drones)
// Backfill: today's `serial` values are what auto-add stored, i.e. flight
// controllers'. Copy them across so connected drones keep matching their
// existing fleet entry.
for (const rec of app.findAllRecords('drones')) {
const serial = (rec.getString('serial') || '').trim()
if (!serial || (rec.getString('flight_controller_serial') || '').trim()) continue
rec.set('flight_controller_serial', serial)
app.save(rec)
}
},
(app) => {
const drones = app.findCollectionByNameOrId('drones')
const f = drones.fields.find((x) => x.name === 'flight_controller_serial')
if (f) drones.fields.removeById(f.id)
drones.indexes = drones.indexes.filter((i) => !i.includes('idx_drones_fc_serial'))
app.save(drones)
},
)