The Codeplug Editor
The browser tool for editing a codeplug, hosted on the site (website.md). For users it is the recommended way to edit a codeplug (Programming the radio); this page is for people who change it.
site/templates/codeplug.html and site/static/js/editor/ make the page (codeplug/index.html).
site/static/js/codeplug/ holds what doesn't depend on the page, so the Tools page, the flasher and the tests can
use it too. Both are plain ES modules that also run in Node. The user's view is in
Programming the radio.
| Module | Does |
|---|---|
codeplug/image.js |
The codeplug as one flat image, file offset = the STM32 wire address (open-firmware-cps.md 7.1), as tools/cps_client.py and backups store it. Keeps every byte the editor doesn't decode |
codeplug/fields.js |
Field encodings: BCD frequencies, IDs and tones, 0xFF-padded names, 24-bit locations |
codeplug/records.js |
Each record decoded into a plain object and encoded back. An encoder writes only the fields it owns, so vendor bytes survive an edit |
codeplug/model.js |
The editing model. References are the records themselves, so a delete or renumber can't leave one pointing at the wrong record. encode() rewrites only changed records: an unedited codeplug round-trips byte for byte. validate() returns the Problems list |
codeplug/iddb.js |
The DMR ID database, format N (7.5): area 1, then area 2 up to the radio's end |
codeplug/csv.js |
Tables as CSV (RFC 4180): fixed English headers, references by name, import matched by number then name |
codeplug/yaml.js |
The YAML subset qdmr writes: block and flow maps and lists, quoted values, !default tags, comments. Anchors are refused |
codeplug/qdmr.js |
qdmr YAML to the model and back (qdmr-yaml.md), with notes on what each side can't hold |
codeplug/detect.js, codeplug/stock_tyt.js |
Telling the open-firmware layout from a vendor codeplug, and converting that to the editing model (codeplug-stock-tyt.md) |
codeplug/transport.js |
How the page reaches a radio (below) |
codeplug/fakecps.js |
A simulated RadioFoundry radio: VERSION, CPS reads, sector writes and C 5, writes refused outside the codeplug as the radio refuses them |
codeplug/sources/ |
Contacts sources: radioid.js (RadioID.net's user.csv and API), regions.js (generated region names), output.js (users to the ID database or contacts) |
editor/app.js |
The page: tabs, the file menu, radio read and write, the draft |
editor/grid.js, views.js |
The table (in-place edit, sort, filter, selection, keyboard) and each record kind's columns, form and membership editor |
editor/undo.js |
Snapshots of every table's order and every record's fields |
editor/files.js |
What an opened file holds (codeplug image, backup bundle, codeplug.bin), and the draft in IndexedDB |
editor/extensions.js, parts.js |
Optional parts register their Save items, table actions, text file openers and DMR ID Database views. parts.js lists them, so a part is added without touching app.js |
editor/csvpart.js, qdmrpart.js, contactspart.js |
The CSV, qdmr YAML and RadioID parts |
Add #sim to the page's address to read and write a simulated radio (fakecps.js).
Transports
A transport has identify(radios), read({iddb, progress, signal}), write(image, current, {progress, signal}) and
close(). Addresses are image addresses; the transport adds the radio's base on the wire. cps-serial is the only one
today: the open-firmware CPS (cps-protocol.md 2) over the radio's USB serial port or the
programming cable (Web Serial). It identifies the radio by the board name the debug link reports, matched against
codeplug.names in data/radios.json. The registry (tools/boards.py Board.codeplug()) fills that entry from the
board's flash map: the base (the CPS address offset, nonzero on some radios), the end of the room for the codeplug and ID database, and whether
the radio has a TFTP staging area. A read takes the codeplug's regions and the ID database. A write compares with the
radio and sends only the changed bytes inside the codeplug's regions, never whole sectors, since a sector can hold the
radio's own settings (0x6000). It verifies, then ends with C 5 so the radio reloads the codeplug without a restart.
A browser can't reach the radio's UDP port or its http server (see USB network mode above), so for a radio in USB
network mode the editor saves codeplug.bin for tftp put instead. A faster transport (the loader's CRC-compared
sector writes) can register beside cps-serial later.
RadioID.net
RadioID.net's API sends no CORS headers. radioid.js tries a request directly, then through a public CORS proxy
(PROXIES), until RadioID allows the site's origin. The user's own API key (optional) is sent only on direct requests
(X-API-Token), never through a proxy, and is kept in the browser's localStorage with the user's choices. Pages
come 200 users at a time, one request after another. Larger selections use a user.csv the user downloads. No user
data is in the repository or on the site: regions.js holds only country and state names
(site/rfoundrysite/radioid_regions.py).
Tests
site/tests/codeplug.test.mjs checks the decoder and encoder against the radio's own codeplug code: fixtures.py
builds tests/host/cpsSim, stores a populated codeplug, and records what the radio reads back (--detail). The JS
decode must match it, and the encode must store what the radio then reads. csv.test.mjs covers the CSV round trips
and import problems; sources.test.mjs covers the RadioID parser, the API client against a fake fetch (the key
never sent through the proxy), templates and outputs. qdmr.test.mjs covers the YAML parser, the mapping, round
trips and the converters; when dmrconf (qdmr's command line tool) is installed it also encodes an exported file.
Nothing reaches the network.