Skip to content

feat: DM32 stock CPS CSV import and export #67

Description

@pskillen

Problem

OpenGD77 is the only supported CPS interchange format today (src/lib/import/opengd77/, src/lib/export/opengd77/). Operators using a Baofeng DM-32UV with stock CPS export a different CSV set — channels.csv, zones.csv, scanlist.csv, digital-contacts.csv, rx-group-list.csv — with different column names, value encodings, and radio-specific concepts (dual-mode channels, separate scan lists, Anlaog channel type spelling, RX Group List = ALL meta, 16-channel scan cap).

There is no path to import a DM32 export into our internal models, edit/visualise it in the app, or export back to DM32 CPS.

Intended outcome

Add DM32 stock CPS CSV as a second import/export adapter pair, mirroring the OpenGD77 pattern: vendor specifics at the boundary; feature code sees only internal Codeplug models.

Import

  • Register dm32 adapter in src/lib/import/registry.ts.
  • Classify files by filename and/or headers (handle channels.csv.csv double-extension quirk).
  • Parse and map into internal models:
DM32 file Internal target
channels.csv Channel[]
zones.csv Zone[] (pipe-separated Channel MemberssourceMemberNames)
digital-contacts.csv Contact[] + TalkGroup[] (split by contact type)
rx-group-list.csv RxGroupList[]
scanlist.csv TBD — see model gaps below
  • Name-based FK resolution at import boundary (channel names, scan list names, contact names) — same pattern as OpenGD77.
  • Map DM32 wire values → internal model (mode, power, timeslot, tones, bandwidth, etc.) via explicit mapping tables in adapter code + docs.
  • Unmapped DM32-only columns → Channel.vendorExtras (keyed by canonical header name).
  • Document skipped/lossy fields and classification rules in docs/features/import/dm32.md.

Export

  • Register dm32 adapter in src/lib/export/registry.ts.
  • Serialise internal models → DM32 CSV files (per-file download + ZIP bundle option, mirroring OpenGD77 export page).
  • Map internal values → DM32 wire format (Anlaog not Analog, Slot 1 not 1, High/Low power, 12.5KHz bandwidth, etc.).
  • Assign No. row indices at export time (per refactor: drop channel number from internal model (assign at export) #53 — not stored in internal model).
  • Respect DM32 capacity limits (scan list ≤16 members, rx group ≤32 contacts) — surface warnings when export would truncate or fail FK checks.
  • Handle scan-list import order on export: DM32 CPS often requires channels without scan refs first, then zones, then scan lists, then carrier channels — document export file set and ordering; may ship multi-file ZIP with README or ordered import instructions.

UI

  • Import: extend ImportDropzone / importFiles to recognise DM32 files alongside OpenGD77 (adapter selection by detected format; error if mixed vendors in one drop).
  • Export: add DM32 section on /export with per-file downloads + ZIP (replace or supplement placeholder formats list).

DM32 ↔ internal model mapping notes

Key differences from OpenGD77 (design during implementation):

Concept DM32 Internal model today
Channel types Anlaog, Digital, Fixed Analog, Fixed Digital analogue / digital / other — needs #45 mode enum + #46 multi-mode
Scan lists Separate scanlist.csv; channel Scan List FK Not modelled — zones only; OpenGD77 zone = scan
RX group ALL CPS meta value on monitor rows Maps to promiscuous RX semantics / TG list behaviour — design choice
Power High / Low String (P2, Master from OpenGD77) — #52 rationalisation
Timeslot Slot 1 / Slot 2 String 1 / 2
Location Typically absent on DM32 channels location / useLocation optional — leave null on import
Zones Pipe-separated members Same ParsedZone pattern as OpenGD77

Model gaps (likely sub-tasks)

  • Scan lists: DM32 requires a ScanList entity (or channel-level scanListName + scan list definitions). May need internal model extension before full round-trip. v1 option: import scan list name onto channel + stash scan list body in project-level vendorExtras or defer scan list export until model exists.
  • Dual-mode channels: Fixed Analog / Fixed Digital may map to feat: support for multi-mode channels #46 multi-mode logical channel or two export rows — decide during design.

Reference material

Operator repo format knowledge (not in this repo — use when implementing):

  • dm32-codeplug-csv skill — column order, FK rules, capacity limits, channels.csv.csv quirk
  • dm32-codeplug-rules skill — zone vs scan-list design, DMR expansion patterns
  • Sample exports under dm32/baofeng-cps-export/ and dm32/generated-csvs/

Use gitignored sample-exports/ locally for round-trip tests; never commit operator codeplugs.

Affected

  • src/lib/import/dm32/ — adapter, parse, columns, tests
  • src/lib/export/dm32/ — serialise, download, round-trip tests
  • src/lib/import/registry.ts, src/lib/export/registry.ts
  • src/lib/import/index.ts — format detection / adapter routing
  • src/routes/Export.tsx — DM32 export UI
  • ImportDropzone hint text + classification
  • docs/features/import/dm32.md, docs/features/export/ — mapping tables
  • Possibly src/models/codeplug.ts — scan list support

Notes / dependencies

Suggested phasing

  1. Core ETL: channels, zones, contacts, rx group lists — import + export round-trip tests with fixture CSVs.
  2. Scan lists: model extension + scanlist.csv import/export.
  3. Polish: ZIP bundle, export warnings, UI copy, progress/outstanding docs.

Out of scope

  • Stock CPS binary .data files or USB radio programming.
  • qDMR YAML (feat: import/export qDMR YAML codeplugs #37) — separate adapter.
  • Full DM32 layout automation (m×n repeater expansion, scan-list policy) — that's operator layout tooling in dmr-programming; this ticket is faithful CSV ↔ internal model conversion.
  • Guaranteed loss-free round-trip for every DM32 column — document lossy fields; vendorExtras for the rest.

Manual verify

  1. Import DM32 generated-csvs/ folder → channels, zones, contacts visible in app.
  2. Edit a channel → export DM32 channels.csv → re-import in stock CPS (or diff against source).
  3. Mixed OpenGD77 + DM32 drop → clear error, no partial corrupt state.
  4. Export with >16 scan members → warning surfaced.

Workflow note

Large multi-commit feature: branch from origin/main, atomic conventional commits per file type or layer (detect → parse channels → … → export → UI → docs). Use docs/features/import/dm32-progress.md + outstanding log. PR linking Closes #.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestradio-format-supportsupport for 3rd party radio formats - import/export, etc

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions