document the source chooser
Both files, per this project's rule: the probe entries and the two invariants in CLAUDE.md, the feature and why the list is filtered in README.
This commit is contained in:
1 parent
54e840e590
commit
b8d0ce8625
2 files changed
+63
No files matched your search
@@ -17,6 +17,9 @@ Options: `--server`, `--port`, `--multicast`, `--interface`, `--buffer-mb` (per
|
||||
buffer, default 200), `--max-window`, `--holdoff`, `--recv-buffer`, `--plots` (the row
|
||||
count to *start* with, 1..`RowSet::kMaxRows`; the layout editor owns it afterwards).
|
||||
|
||||
Run with no `--server` and udpscope asks which source to open: the sessions it has stored, plus a
|
||||
form for a new host. Every probe below passes `--server`, so none of them sees it.
|
||||
|
||||
`deps/plotter` is built from source with only the GTK4 adapter enabled
|
||||
(`PLOT_BUILD_GTK4`); its tests and examples are forced off. `deps/udpsclient` is a
|
||||
vendored C99 source pair compiled into a static lib.
|
||||
@@ -445,6 +448,22 @@ Two lines of the self-check output are assertions in disguise:
|
||||
substitutes a missing-image placeholder, the button still works, and every other line of the
|
||||
dump still passes. Changing an icon name is a one-line change in `build_toolbar()` and this
|
||||
is the only check that it names something that exists.
|
||||
- `[sources] N known: HOST:PORT plots=P signals=S [multicast=G] [interface=I] | ...` (stderr,
|
||||
`UDPSCOPE_PROBE_SOURCES=1`) — the startup chooser's discovery, printed and exited **before
|
||||
`gtk_init()`**, so this one needs no display and no `xvfb-run`. **Only sources with a named
|
||||
signal may appear**: a development machine accumulates one session file per probe run on a
|
||||
throwaway port (140 of them when this was written), and the filter in `known_sources()` is
|
||||
what keeps the list the sources a user configured. Order is newest first by file mtime.
|
||||
- `[sources] picked HOST:PORT multicast=G interface=I` (stderr, `UDPSCOPE_PROBE_PICK_MS`) —
|
||||
shows the chooser, then activates row 0 the way a click does and continues into the real
|
||||
application, so a full run with **no `--server`** must reach the scope and emit every other
|
||||
dump line unchanged. `multicast=`/`interface=` coming back non-empty from a stored row is the
|
||||
assertion that a multicast session reconnects to its group, and it needs **no multicast
|
||||
networking**: write the session file by hand and read the line. With
|
||||
`UDPSCOPE_PROBE_NEWHOST=host:port` it drives the form instead — `[sources] form open,
|
||||
connect_sensitive=1` then a `picked` line, with no SIGSEGV, which is as much a crash test of a
|
||||
dialog built and destroyed as it is a state check. An empty list must print `picked nothing`
|
||||
rather than dereferencing a null row.
|
||||
- **The session layout round trip** (`~/.config/udpscope/<host>_<port>.toml`) — run once
|
||||
with a drop and a window change, then again with no interaction: the second run's dump
|
||||
must read `layout loaded from ...` and `layout: rows=R window_s=W row0=[...]` with the
|
||||
@@ -1234,4 +1253,24 @@ far the largest file.
|
||||
`gtk_icon_theme_has_icon()` is checked for every toolbar button and reported by the dump (see
|
||||
`toolbar icons:` under Verification): a resource path that was never added does not raise an
|
||||
error, it draws `image-missing`.
|
||||
- **The chooser runs before `ScopeModel` exists, and `--server` is what decides.** `Config` carries
|
||||
`server_specified`, set only by the `--server` branch, and `main()` asks only when it is false —
|
||||
so declining costs no buffer and no socket, and `ScopeModel`, `Acquisition`, `RowSet` and
|
||||
`run_ui()` need no notion of "no source yet". It is `--server` alone and not "no arguments":
|
||||
`--multicast` still needs a server to fetch CONFIG from. **Every documented probe passes
|
||||
`--server`**, which is what makes the feature invisible to the regression net — a probe added
|
||||
without it would reach the chooser and hang. `gtk_init()` plus a nested `GMainLoop`, and then
|
||||
`run_ui()`'s `GtkApplication` afterwards in the same process, is a verified combination
|
||||
(`gtk_init()` is idempotent; running your own loop is the documented GTK4 replacement for the
|
||||
removed `gtk_dialog_run()`).
|
||||
- **A session file's name cannot say how its source was reached, so the file says it.**
|
||||
`<host>_<port>.toml` carries neither the multicast group nor the interface, so a chooser row
|
||||
could not reconnect to a multicast source at all. `LayoutState::multicast`/`interface_addr` are
|
||||
written in the **top block, before the first `[[table]]`** — a bare key after a table header is
|
||||
that table's key in TOML, which is exactly how `enabled` was parsed into the last
|
||||
`[[calibration]]` record and lost without a word. A file from an older build has neither key and
|
||||
loads as unicast on the default route. The chooser's list filters to sessions that **name at
|
||||
least one signal**, because a probe run leaves a file per throwaway port and all of them hold
|
||||
`signals = ["", "", "", ""]`; `known_sources()` splits the filename at the **last** `_`, since
|
||||
`sanitize()` turns anything unsafe in a host into one.
|
||||
- Every source file carries an SPDX MIT header; match it in new files.
|
||||
@@ -458,6 +458,30 @@ a math channel's name and that name only exists as a signal once its graph is in
|
||||
reopened on the same source comes back as the one that was closed — including a signal that was
|
||||
enabled only to feed a math channel.
|
||||
|
||||
**Launched with no `--server`, udpscope asks which source to open.** It used to connect to
|
||||
`127.0.0.1:8000` because that is the default in `Config` — fine for one streamer, useless for
|
||||
several, and there was no way to see which sources the app already knew about. The chooser lists
|
||||
one row per stored session: the server and port it was named after, how many plots and signals it
|
||||
held, and its multicast group when it had one, newest first. `New host…` is a four-field form —
|
||||
server, port, multicast group, interface — and nothing more: `--buffer-mb` and the rest are tuning
|
||||
rather than "which source", and a form that asks for them is a form you have to read before using
|
||||
the app. Cancel exits before anything is allocated, because declining to choose a source is not a
|
||||
failure.
|
||||
|
||||
It runs **before the model exists**, which is the whole reason it is cheap: no buffer, no socket
|
||||
and no window until a source is known, and `ScopeModel`, `Acquisition` and `run_ui()` are
|
||||
untouched by the feature. Every headless probe passes `--server`, so none of them reaches it.
|
||||
|
||||
Two things the list had to get right. A session file is named `<host>_<port>.toml` and that name
|
||||
cannot say whether the source was multicast or which interface it joined on, so the file now
|
||||
records both — in the top block, before the first table, because a bare key written after a
|
||||
`[[table]]` header belongs to that table in TOML, which is how the `enabled` key was silently lost
|
||||
once already. And the list is **filtered to sessions that name a signal**: a development machine
|
||||
accumulates one file per probe run on a throwaway port — 140 of them here, every one with
|
||||
`signals = ["", "", "", ""]` — so an unfiltered chooser opens as a wall of ports nobody chose. A
|
||||
row's `×` forgets a source by deleting its session file, with no confirmation: the gesture is the
|
||||
confirmation, and what it destroys is one layout the app rewrites on the next clean exit.
|
||||
|
||||
**Math channels.** A math channel is derived from the others by a graph of operations and is then
|
||||
an *ordinary channel*: the model synthesises a signal entry for it, so an enabled one is acquired,
|
||||
plotted, measured, exported and listed in the signal panel exactly like a streamed signal — and
|
||||
|
||||
Reference in new issue
Block a user