/// // 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) }, )