You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 Members → sourceMemberNames)
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.
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):
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.
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,Anlaogchannel type spelling,RX Group List = ALLmeta, 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
Codeplugmodels.Import
dm32adapter insrc/lib/import/registry.ts.channels.csv.csvdouble-extension quirk).channels.csvChannel[]zones.csvZone[](pipe-separatedChannel Members→sourceMemberNames)digital-contacts.csvContact[]+TalkGroup[](split by contact type)rx-group-list.csvRxGroupList[]scanlist.csvChannel.vendorExtras(keyed by canonical header name).docs/features/import/dm32.md.Export
dm32adapter insrc/lib/export/registry.ts.AnlaognotAnalog,Slot 1not1,High/Lowpower,12.5KHzbandwidth, etc.).No.row indices at export time (per refactor: drop channel number from internal model (assign at export) #53 — not stored in internal model).UI
ImportDropzone/importFilesto recognise DM32 files alongside OpenGD77 (adapter selection by detected format; error if mixed vendors in one drop)./exportwith per-file downloads + ZIP (replace or supplement placeholder formats list).DM32 ↔ internal model mapping notes
Key differences from OpenGD77 (design during implementation):
Anlaog,Digital,Fixed Analog,Fixed Digitalanalogue/digital/other— needs #45 mode enum + #46 multi-modescanlist.csv; channelScan ListFKALLHigh/LowP2,Masterfrom OpenGD77) — #52 rationalisationSlot 1/Slot 21/2location/useLocationoptional — leave null on importParsedZonepattern as OpenGD77Model gaps (likely sub-tasks)
ScanListentity (or channel-levelscanListName+ 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-levelvendorExtrasor defer scan list export until model exists.Fixed Analog/Fixed Digitalmay 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-csvskill — column order, FK rules, capacity limits,channels.csv.csvquirkdm32-codeplug-rulesskill — zone vs scan-list design, DMR expansion patternsdm32/baofeng-cps-export/anddm32/generated-csvs/Use gitignored
sample-exports/locally for round-trip tests; never commit operator codeplugs.Affected
src/lib/import/dm32/— adapter, parse, columns, testssrc/lib/export/dm32/— serialise, download, round-trip testssrc/lib/import/registry.ts,src/lib/export/registry.tssrc/lib/import/index.ts— format detection / adapter routingsrc/routes/Export.tsx— DM32 export UIImportDropzonehint text + classificationdocs/features/import/dm32.md,docs/features/export/— mapping tablessrc/models/codeplug.ts— scan list supportNotes / dependencies
opengd77.mdand existing adapters as template.Suggested phasing
scanlist.csvimport/export.Out of scope
.datafiles or USB radio programming.dmr-programming; this ticket is faithful CSV ↔ internal model conversion.vendorExtrasfor the rest.Manual verify
generated-csvs/folder → channels, zones, contacts visible in app.channels.csv→ re-import in stock CPS (or diff against source).Workflow note
Large multi-commit feature: branch from
origin/main, atomic conventional commits per file type or layer (detect → parse channels → … → export → UI → docs). Usedocs/features/import/dm32-progress.md+ outstanding log. PR linkingCloses #.