Feature/source plugins #1
No files matched your search
@@ -94,7 +94,7 @@ switches trigger mode on, waits for an event, then leaves trigger mode the way t
|
||||
"Go live" button does, closes the window, and prints on stdout the status line in
|
||||
both states, the frame and per-channel sample counts, each plot's painted-frame
|
||||
count, which channel each trace slot holds, and the allocated size and reveal
|
||||
state of the dock and the events pane.
|
||||
state of the dock, and the events page's controls (present and nested in the page).
|
||||
|
||||
Every size it prints is read from a value the frame tick captured while the window was alive, not
|
||||
from the widget: the dump runs from the destroy handler, where the widget pointer is already freed
|
||||
@@ -146,8 +146,8 @@ panel laid over the plot's right edge, opaque, with its own title, a *Close* but
|
||||
page. The Trigger badge opens the trigger form; the Source badge (when shown) opens the source kind's own panels
|
||||
(the E-1608's *Analog out* and *Digital I/O*, behind a segmented switch at the top of the panel). It
|
||||
closes with a second click on its badge, with `Esc`, with *Close*, or with a click on the plot; it never
|
||||
resizes the plot. The right-hand **dock** (the sidebar, **280 px**, fixed) holds the **readouts**: a
|
||||
compact, read-only copy of the cursors table (a header row `A B delta`, then one line per cursor
|
||||
resizes the plot. The right-hand **dock** (the sidebar, **280 px**, fixed) holds the **readouts** and, under
|
||||
them, the **Events** block (below): the readouts are a compact, read-only copy of the cursors table (a header row `A B delta`, then one line per cursor
|
||||
line) and of the measurements (one line each: the signal's colour chip, the type, the value). It is
|
||||
shown from the start and insensitive until the stream has signals. Each of the two blocks has an
|
||||
**Edit** button, which opens the full panel as a slide-over page -- `cursors` or `measure` -- over the
|
||||
@@ -159,8 +159,12 @@ cells are a fixed number of characters wide and ellipsized, so the dock's width
|
||||
depend on what a value reads: the **who** cell of a cursor line is 5 characters (the full text is its
|
||||
tooltip), a value cell 9 (its tooltip is the full text). The dock and the pages show **one string**:
|
||||
`update_cursors_pane()` and `update_measures()` format each value once and hand that variable to the
|
||||
page's label and to the dock's, so the two cannot disagree. The remaining part of the redesign is
|
||||
listed under "Review findings not (yet) implemented".
|
||||
page's label and to the dock's, so the two cannot disagree. The **Events** block is a heading with a
|
||||
**Manage** button over a short list of the cached events, newest first -- one compact row each, never a
|
||||
picture -- and one click on a row replays that event (see "Captured events"). The full list, with the
|
||||
pictures and the comparison actions, is the `events` slide-over page that **Manage** opens. There is no
|
||||
events pane beside the plot and no Events button in the header: events never take any of the plot's width.
|
||||
The rest of the redesign is listed under "Review findings not (yet) implemented".
|
||||
|
||||
Threading: one producer thread owns the data source (the UDPStreamer client is the built-in
|
||||
one) and calls `poll()`; the GTK main thread owns every widget and the GL contexts.
|
||||
@@ -838,20 +842,36 @@ time to look at is still there:
|
||||
20261009-123250-0001.bmp # a picture of it, drawn from the samples
|
||||
```
|
||||
|
||||
The **left pane** is opened on demand from the toolbar's event-history button, is collapsed by
|
||||
default, and its **divider can be dragged**: it starts at 240 pixels and remembers the width you last
|
||||
chose, and while it is closed it takes no room at all. Both the button and the pane exist while **recording is on**, or while an event is on
|
||||
screen: with the cache disabled and nothing being examined there is nothing to show, and a visible
|
||||
empty column beside the plot is what *off* must not look like. Recording is switched in the **trigger
|
||||
panel**, with the trigger's other options rather than in the pane whose contents it decides, and it is
|
||||
**off by default** -- writing every latch to disk is the user's decision about their disk.
|
||||
Events are read in two places. The **dock's Events block** lists them compactly -- one row per event, newest
|
||||
first, at most **24** with a dim "... N more, see Manage" row after them: a marker (a filled circle on the
|
||||
event on screen, a hollow one on each member of the comparison or accumulation on screen), the time and
|
||||
duration, and the event's sequence number (`#7`). **One click on a row replays it.** The block's **Manage**
|
||||
button opens the **`events` page**, a slide-over page over the plot with the full list (below), the *Live*
|
||||
button and the two group actions. The page replaces the left pane that used to sit beside the plot behind a
|
||||
header button and a draggable divider; both are gone, and so is the toolbar's Events button -- opening the
|
||||
list no longer resizes the plot at all.
|
||||
|
||||
The block and the page exist all the time and are **insensitive, not hidden**, when there is nothing to show:
|
||||
they are available while **recording is on**, while an event is on screen (a replay or a group), or while
|
||||
the cache already holds events. **Deviation from the redesign spec,** which has the block insensitive
|
||||
"outside trigger mode": a replay or a group *leaves* trigger mode (a file has no edge to wait for), so a block
|
||||
that went grey outside trigger mode would grey itself under the very click that started the replay. Trigger
|
||||
mode is therefore not one of the conditions. Recording is switched in the **trigger panel**, with the
|
||||
trigger's other options rather than in the list whose contents it decides, and it is **off by default** --
|
||||
writing every latch to disk is the user's decision about their disk. (The session file keeps nothing about
|
||||
this list: there is no remembered width, because there is no pane to size.)
|
||||
|
||||
The dock follows every latch; the page's list is refreshed per latch only while it is open, and rebuilt when
|
||||
**Manage** opens it. **The dock list has no thumbnails** -- its rows are fixed-width, ellipsized cells, so the
|
||||
dock's size does not depend on what an event is called -- the page has them, and its rows are replayed with a
|
||||
**double** click, because on the page a single click builds the selection that *Compare* and *Accumulate* read.
|
||||
|
||||
Each row carries **its own picture** beside the numbers -- time, duration, signal count, rate -- 138
|
||||
pixels wide whatever the pane's width, and as tall as that event's own proportions make it: the
|
||||
pixels wide whatever the page's width, and as tall as that event's own proportions make it: the
|
||||
preview is one 44-pixel strip per signal, so a one-signal event is a short row and a six-signal one a
|
||||
tall one (capped, so that a row stays a row). **Hovering it shows the whole preview at full size**,
|
||||
through a tooltip -- the small picture says *which* event this is, the zoom says what is in it.
|
||||
Clicking a row **replays** it: the same loader `--open` uses, on the event's file. The click **leaves
|
||||
Double-clicking a row on the page (or clicking one in the dock) **replays** it: the same loader `--open` uses, on the event's file. The click **leaves
|
||||
the list alone** -- your scroll position and the pair you had selected for a comparison both survive
|
||||
it, because marking which event is on screen rewrites the rows' titles in place instead of rebuilding
|
||||
the list. **The layout does
|
||||
@@ -862,7 +882,7 @@ rearrange the user's work under a click. What changes is the data, and the trigg
|
||||
file has no edge to wait for. Cursors, measurements, math channels and another export all work on a
|
||||
stored event. **Live** takes you back to the stream.
|
||||
|
||||
Two buttons under the pane's header read the **selection**:
|
||||
Two buttons under the page's header read the **selection**:
|
||||
|
||||
- *Compare* puts the two selected events in **two plots side by side**, each with **the layout you
|
||||
already have** -- the same rows, the same signals in the same slots, the same colours -- one showing
|
||||
@@ -880,8 +900,8 @@ model whose channel count no longer matches the stream), the trigger is left, an
|
||||
|
||||
A replay leaves trigger mode, because a file has no edge to wait for -- an armed trigger keeps a
|
||||
*frozen capture* on screen (`display_mode()` stays `Held` while the model holds a snapshot), and a
|
||||
held display draws that snapshot rather than the file that was just loaded. The events pane stays
|
||||
open through it: it is available while a stored event is on screen.
|
||||
held display draws that snapshot rather than the file that was just loaded. The dock's Events block and
|
||||
the `events` page stay available through it: they are available while a stored event is on screen.
|
||||
|
||||
**Going back to *Live* puts the live view back as it was.** Opening a file changes two things that
|
||||
are not the user's choice -- the time window becomes the event's own span, because that is what
|
||||
@@ -912,7 +932,7 @@ Three things worth knowing:
|
||||
- **The picture is decoded synchronously** (`gdk_texture_new_from_filename()` into
|
||||
`gtk_picture_set_paintable()`), not through `gtk_picture_set_filename()`: that path uses the image
|
||||
loader's sandboxed, asynchronous decoder, which here fails silently -- the picture keeps a
|
||||
paint-able and an allocation, so a probe reports success while the pane shows an empty rectangle.
|
||||
paint-able and an allocation, so a probe reports success while the page shows an empty rectangle.
|
||||
It is then **scaled once to the row's size** and cached by file: a `GtkPicture`'s natural size is
|
||||
its content, so the stored 480-pixel preview given straight to a row claims the row's whole width
|
||||
and squeezes the text out of it -- a size request on the picture is a floor, not a cap.
|
||||
@@ -929,8 +949,8 @@ Three things worth knowing:
|
||||
one at a time was eight of those, which is most of what made the switch feel slow. They are now
|
||||
enabled in a single call, and a rebuild whose plan is unchanged clears the buffer instead of
|
||||
allocating a new one.
|
||||
- The switch in the pane's header stops the recording; the cache is only useful if it was already
|
||||
on when the event you wanted happened, which is why it defaults to on.
|
||||
- The switch in the trigger panel starts and stops the recording; the cache is only useful if it was
|
||||
already on when the event you wanted happened, so switch it on before the event you are waiting for.
|
||||
|
||||
## Opening an exported waveform
|
||||
|
||||
@@ -1241,8 +1261,10 @@ reveals it, while one whose *pane is collapsed* (the dock, in a window too narro
|
||||
plot) is dropped, because it is unmapped with its pane (a hidden page keeps its own *visible* flag
|
||||
true -- the widget's own flag, which is all `is_visible()` walks -- so the old test walked over
|
||||
surfaces that were not on screen); a narrow window therefore walks five steps. The dock's stop
|
||||
describes the readouts and the **Edit** buttons; it names no slide-over page, so it closes the panel
|
||||
like the plot's. The cursors and measurements pages have no stop of their own, and their contextual
|
||||
describes the readouts and their **Edit** buttons and the Events block under them (one click on a row
|
||||
replays it, **Manage** opens the list); it names no slide-over page, so it closes the panel like the plot's.
|
||||
There is no stop for the `events` page, so nothing in the tour opens it; the contextual help's
|
||||
event-history hint is attached to the page's heading the way the cursors' and measurements' are. The cursors and measurements pages have no stop of their own, and their contextual
|
||||
help "(i)" hints are attached to the pages' headings the way the trigger's and the signals' are: they
|
||||
are drawn while the page is open, and the dock has none. The chooser's
|
||||
tour works the same way over its own two pages: its tabs step shows the *Waveforms* page and
|
||||
@@ -3021,11 +3043,16 @@ now — they are listed so they are not mistaken for anything else:
|
||||
**who** cell ellipsizes at **5 characters** -- the width the 280 px pin allows; with 8 the readouts'
|
||||
minimum width was 304 px and widened the sidebar -- and its tooltip carries the full text. (6) The dock and
|
||||
the pages **share one string** for every value (see `docs/claude/invariants-readouts-and-cursors.md`).
|
||||
(7) The **Events block and the `events` page are available** while recording is on, a replay is on screen, or
|
||||
the cache holds events -- **not** "outside trigger mode" as the spec has it, because a replay or a group leaves
|
||||
trigger mode and the block would grey itself under the click that started it. (8) The **dock's event rows carry no
|
||||
thumbnails** (the page's do) and show a **hollow marker on the members of the group on screen**, which the page's
|
||||
rows do not. (9) The dock lists at most 24 events; the page lists them all.
|
||||
The signal badges, the signal pages and the drag (step 2) are done. Still to come, in the spec's
|
||||
order: the events section in the dock, the deletion of the left pane and the Events toggle, and
|
||||
`USER-GUIDE.md` and the manual's figures (4). Until then the events pane, the header's Events toggle
|
||||
and the manual (which still describes the side panel's tabs, and the cursors and measurements as
|
||||
panes of it) still describe the previous layout.
|
||||
order: `USER-GUIDE.md` and the manual's figures (4). The events section in the dock and the `events`
|
||||
page are done, and the left events pane, its divider and the header's Events toggle are deleted. Until
|
||||
the manual is swept, it (which still describes the side panel's tabs, and the cursors, measurements
|
||||
and events as panes of it) still describes the previous layout.
|
||||
- **A plot holds at most eight signals** (`RowSet::kSlotsPerRow`), because a `Plot`
|
||||
cannot give up a trace and the slots are therefore created up front.
|
||||
- **One toggle drives both cursor orientations.** The vertical markers and the
|
||||
|
||||
@@ -102,10 +102,12 @@ touching the data path.
|
||||
own and registered under its id (`trigger`, `source-<id>`, `signals` (the signal list) and
|
||||
`signal:<name>` (one signal's page, `src/ui_signal_page.cpp`, created on demand by `signal_page_ensure()`
|
||||
and pruned by `signal_pages_sync()`, which `rebuild_signal_panels()` ends with), `cursors` (the cursors
|
||||
panel, unchanged) and `measure` (the measurements panel, unchanged; `m` toggles it); `events` is reserved
|
||||
for a later step). Both of those are registered whether or not they are open (a closed page's widgets
|
||||
exist, unmapped), and both are insensitive until the stream has signals, by the same
|
||||
`update_signal_availability()` call that sets the dock's sensitivity. One page is open at a time and `App::slideover.page`
|
||||
panel, unchanged) and `measure` (the measurements panel, unchanged; `m` toggles it); `events` (the full events list, `events-pane.ui`'s `events_box`: the Live, Compare and Accumulate
|
||||
controls and the multi-select list with its thumbnails; open it only through `open_events_page()`, which
|
||||
refreshes the list first, because the slide-over has no on-open hook). They are registered whether or not
|
||||
they are open (a closed page's widgets exist, unmapped); `cursors` and `measure` are insensitive until the
|
||||
stream has signals, by the same `update_signal_availability()` call that sets the dock's sensitivity, and
|
||||
`events` follows `sync_events_availability()` (below). One page is open at a time and `App::slideover.page`
|
||||
is its id, empty when closed. The open page is **not** a session value. Esc, *Close*, a second click on the
|
||||
badge and a click on the plot close it; the click-away is a capture-phase observer (see
|
||||
[invariants-gtk-and-chrome.md](invariants-gtk-and-chrome.md)). With two or more `source-*` pages, an
|
||||
@@ -120,7 +122,7 @@ touching the data path.
|
||||
pick and focus from `slideover.source_pages` being non-empty every tick, so anything that indexes the badge
|
||||
(the tour's `badges` step targets the whole bar, `[badges]`) must tolerate it being absent.
|
||||
- **The dock** is the `AdwOverlaySplitView` sidebar (`side_box` > `dock_scroller` > `dock_page`), **280 px**
|
||||
and fixed. It holds one thing, the **Readouts** box (`src/ui_dock.cpp`, `build_readouts_dock()`): a
|
||||
and fixed. It holds two things, the **Readouts** box and, under a separator, the **Events** block (below). The Readouts box (`src/ui_dock.cpp`, `build_readouts_dock()`): a
|
||||
**Cursors** block (heading, an **Edit** button that opens the `cursors` page, a `GtkGrid` of a header row
|
||||
`A B delta` and one line per page line) and a **Measurements** block (heading, **Edit** opening the
|
||||
`measure` page, one line per measurement: chip, type, value; a click on a line opens that measurement's
|
||||
@@ -132,6 +134,22 @@ touching the data path.
|
||||
kind's panels (those are slide-over pages). The window's measurement editor is a dialog
|
||||
(`open_measure_dialog()`), not part of the `measure` page.
|
||||
|
||||
The **Events** block (`build_events_dock()`, also `ui_dock.cpp`) is a heading with a **Manage** button
|
||||
(`open_events_page()`) over a short list (`dock_events_list`, 112 px, scrolled, no thumbnails): one compact
|
||||
row per `EventRecord`, newest first, capped at `kDockEventsMax` (24) with a dim "... N more, see Manage"
|
||||
row after them. A row's cells are a one-character marker (filled = the event on screen; hollow = a member
|
||||
of the group on screen -- dock only, the page's rows do not mark members; blank otherwise), the title
|
||||
(`event_row_title`, ellipsized, full text in the tooltip) and `#<seq>`; **one click replays** it
|
||||
(`replay_event()`, the body of the page's double-click handler). Both views are rebuilt from one
|
||||
`EventStore::list()` by `refresh_events_pane()`, but on different cadences: **the dock follows every
|
||||
latch** (`record_event()` refreshes it unconditionally), while the page's list is refreshed per latch only
|
||||
while the page is open and by `open_events_page()` otherwise. `mark_events_on_screen()` updates titles and
|
||||
markers in place and never rebuilds either list. The block, and the page, are insensitive (never hidden)
|
||||
unless `sync_events_availability()` says `event_recording || replaying || events_cached` -- a different
|
||||
rule from the spec's (see [invariants-trigger-and-pinning.md](invariants-trigger-and-pinning.md)).
|
||||
There is no events pane, divider or header toggle any more: the page is reached from Manage (or by a
|
||||
caller of `open_events_page()`), and the plot is never squeezed by events.
|
||||
|
||||
**When each is built and when it is read.** `build_slideover()` runs in `on_activate` right after the tour
|
||||
overlay is built (the tour's overlay wraps the whole window, so its dim rectangles cover the slide-over
|
||||
too); the trigger form is registered as
|
||||
|
||||
@@ -210,9 +210,10 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
entry points in `source_dialog.cpp` (`chooser_tour_begin`, `chooser_tour_autostart`), and *that* walk
|
||||
is what a first run meets first -- see the tour bullets in the help-affordance section below.
|
||||
- **A `GtkPaned` hands its start child at least its minimum unless the position says otherwise.**
|
||||
Putting the events pane in a paned is what makes its width the user's to drag -- but with the
|
||||
position left alone, `shrink-start-child` or not, the pane's minimum is reserved as an empty column
|
||||
while it is *closed*, and the plot pays for a pane nobody opened. The position is the only knob that
|
||||
(The events pane that taught this is gone -- its list is a slide-over page now, with no divider -- but
|
||||
the rule holds for any paned.) With the
|
||||
position left alone, `shrink-start-child` or not, a closed side child's minimum is reserved as an empty
|
||||
column, and the plot pays for a pane nobody opened. The position is the only knob that
|
||||
says "no room": store the width when it is closed (reading `gtk_paned_get_position()` *before*
|
||||
zeroing it) and set 0, restoring it when the pane opens. `gtk_paned_set_shrink_start_child(TRUE)` is
|
||||
still needed, or the minimum would clamp even a zero position.
|
||||
@@ -220,7 +221,8 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
= FALSE` makes both the revealer's minimum and its natural size zero -- and its box then hands it
|
||||
everything the expanding sibling beside it did not claim, so a *collapsed* pane reserved a
|
||||
310-pixel empty column between the window edge and a plot drawn 630 pixels wide in a 1270-pixel
|
||||
window. Every vertical revealer in the side panel hides this, because there the leftover goes down
|
||||
window. (That pane was a revealer beside the plot; it is a slide-over page now, so no revealer sits in
|
||||
a horizontal box any more.) Every vertical revealer in the side panel hid this, because there the leftover goes down
|
||||
instead of across. `gtk_widget_set_visible()` is what a layout honours, so anything that must not
|
||||
take room has to be hidden and not merely unrevealed; the revealer keeps the slide while the pane
|
||||
*is* open. The allocated width is how this was found -- the revealer's own measure said 0.
|
||||
@@ -278,8 +280,9 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
clicked — a switch that greyed out with its own pane could never turn it back on. That is why the
|
||||
sensitive target is the pane's **body** (`trigger_body`, `cursors_body`) and not the pane.
|
||||
- **A list box outlives the children of the pane it sits in, and the teardown order is not yours to
|
||||
choose.** Tearing the window down finalizes the events pane's buttons, then the list box, and the
|
||||
list's own `selected-rows-changed` then reaches a handler holding the freed pointers: four
|
||||
choose.** Tearing the window down finalizes the events page's buttons, then its list box, and the
|
||||
list's own `selected-rows-changed` then reaches a handler holding the freed pointers (`update_group_buttons()`
|
||||
therefore keeps its `app.window == nullptr` guard): four
|
||||
`Gtk-CRITICAL`s from `gtk_widget_set_sensitive` and a read of memory nobody owns. `close-request` is
|
||||
*not* the hook -- `UDPSCOPE_EXIT_AFTER_MS` ends the self-check with `gtk_window_destroy()`, which does
|
||||
not emit it. The hook that does work is `App::window`: every teardown path clears it *before* the
|
||||
@@ -296,22 +299,26 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
- **A pointer to a widget inside a list survives the list being rebuilt.** `events_thumb` named a row's
|
||||
picture across `refresh_events_pane()`, which removes every row first: the next reader of that
|
||||
pointer got a `Gtk-CRITICAL` from `gtk_picture_get_paintable` on freed memory (the four criticals
|
||||
read as "the pane is empty"). A rebuild has to null what it is about to destroy, not only rebuild it.
|
||||
read as "the page is empty"). A rebuild has to null what it is about to destroy, not only rebuild it
|
||||
(`rebuild_events_pane_list()`, the page half of `refresh_events_pane()`, nulls `events_thumb` before it removes the page list's children). A handler on a
|
||||
row that rebuilds the list destroys the widget it was emitted from: `outlive_the_rebuild(widget)`. The dock's
|
||||
rows are a second list built from the same records; the dock row's click handler is safe only because
|
||||
`replay_event()` never rebuilds the dock list.
|
||||
- **`gtk_picture_set_filename()` can render nothing, silently.** It hands the file to the image
|
||||
loader's sandboxed, asynchronous decoder, which on this machine fails without a message: the
|
||||
picture keeps a *paint-able* and takes an allocation, so a probe that checks
|
||||
`gtk_picture_get_paintable()` reads success while the pane shows an empty rectangle (the event
|
||||
pane's preview did exactly that). `gdk_texture_new_from_filename()` decodes the same file on this
|
||||
`gtk_picture_get_paintable()` reads success while the page shows an empty rectangle (the event
|
||||
page's preview did exactly that). `gdk_texture_new_from_filename()` decodes the same file on this
|
||||
thread and `gtk_picture_set_paintable()` hands over the result -- and a NULL texture at least says
|
||||
so. A picture that must be on screen is also a picture with a **height request**: `can_shrink`
|
||||
alone asks for no height at all in a box.
|
||||
- **A cache that is the *only* source of a widget's content must be created by the thing that reads
|
||||
it.** The event pane's thumbnails come from an `EventThumbCache` hung off the list box with
|
||||
it.** The events page's thumbnails (the dock's rows have none) come from an `EventThumbCache` hung off the page's list box with
|
||||
`g_object_set_data_full`, and `event_preview_texture()` answers out of that cache or not at all.
|
||||
Nothing ever created it — so for as long as the pane existed, **every** row came up without a
|
||||
Nothing ever created it — so for as long as the list existed, **every** row came up without a
|
||||
picture, which is what "event screenshot are unusable and sometime missing" was. A missing cache
|
||||
there is not a slow path, it is a blank one; `event_thumb_cache()` therefore creates one on first
|
||||
use. Owned by the **list**, because nothing else has the pane's lifetime: textures of a list that
|
||||
use. Owned by the **list**, because nothing else has the page's lifetime: textures of a list that
|
||||
no longer exists are so much held memory, and the destroy notify is where they are unref'd.
|
||||
- **Hover magnification is a tooltip, not a popover of ours.** A row's thumbnail is 138 px wide and
|
||||
the stored preview is 480: the small one says *which* event this is, and the zoom says what is in
|
||||
|
||||
@@ -40,6 +40,14 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
moved the plot column by about 180 px, which the layout probe read as a different `done_box` and a
|
||||
taller figure.
|
||||
|
||||
- **Events never take the plot's width.** The events list is a slide-over page (an overlay child over the plot) and
|
||||
the dock's Events block lives in the fixed 280 px column, so there is no events divider, revealer or pane beside the
|
||||
plot to squeeze it; the first latch must not move the plot area (`[events] ... plot_w_stable=1`, which compares the
|
||||
allocated width before and after). A dock row is a fixed-width ellipsized label (title at 18 characters, `#seq`), the
|
||||
list is a fixed 112 px scroller capped at `kDockEventsMax` rows plus one "N more" row, and it has no thumbnails: the
|
||||
dock's width and height do not depend on how many events there are or what they say ("layout must not depend on
|
||||
content"). The thumbnails are the *page's* (138 px wide).
|
||||
|
||||
- **`plot::Plot` cannot remove or retarget a trace.** Hence fixed trace slots created
|
||||
up front and rebinding via `SlotSeries`; a *name* change (legend text) is the one
|
||||
thing that forces a rebuild. Cursors are parked outside the axis range instead of
|
||||
@@ -131,7 +139,7 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
the Source badge is on screen for the E-1608 and hidden for UDPS, and its text differs by kind). A change that makes it grow with the signal count has
|
||||
taken the scroller out of the measurement.
|
||||
- **The dock's width is fixed too, and it is the readouts' width that holds it there** (`side_column`
|
||||
`width-request` 260, the split view's 280 px floor and cap). The dock holds only the compact readouts, so a
|
||||
`width-request` 260, the split view's 280 px floor and cap). The dock holds only the compact readouts and the Events block (a fixed-height list, below), so a
|
||||
section's visibility is never what makes it taller; the scroller absorbs the rest. **The readouts' minimum
|
||||
width is the sum of their cells' character widths, and it must stay under 280** (the Readouts box is 268:
|
||||
280 less its 6 px margins): a cursor line is the `who` cell (5 characters) plus three value cells (9 each),
|
||||
@@ -139,7 +147,7 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
tooltip carries the text and the layout never depends on what a value reads. The `who` cell was 8
|
||||
characters, which made the readouts' minimum width 304 px; that pushed the sidebar to 304 px
|
||||
(`dock_w=304`) and defeated the 280 pin. Widening any cell is the same
|
||||
trap -- read `[readouts] ... dock_w=` (must be 280) and the `dock cursors:` / `dock measures:` `fits=`.
|
||||
trap -- read `[readouts] ... dock_w=` (must be 280) and the `dock cursors:` / `dock measures:` `fits=`, and for the Events block `dock events: ... dock_w=` (268).
|
||||
- **Trace slots are identified by signal name, not channel index.** Enabling a signal
|
||||
or a new CONFIG renumbers the channel list, so an index would silently re-point a
|
||||
trace at another signal.
|
||||
|
||||
@@ -440,15 +440,24 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
|
||||
## A latch is also a file
|
||||
|
||||
The pane that lists the cache is **on demand**: the toolbar's event-history button and the pane are
|
||||
collapsed until it is asked for, because a column of stale events beside a live plot is what a user
|
||||
who has stopped looking at events does not want. What makes it *available* -- (recording is on) or
|
||||
(an event is on screen) -- is decided in one place, `sync_events_availability()`, so the button and
|
||||
the pane cannot disagree; a disabled cache with nothing being examined takes the button away *and*
|
||||
collapses the pane, because a visible empty column is what "off" must not look like. Trigger mode is
|
||||
deliberately **not** one of the conditions: a stored event being examined is a file, which has no
|
||||
trigger, and tying the pane to the trigger would close the list under the click that opened an event.
|
||||
The recording switch lives in the trigger panel, not in the pane it empties.
|
||||
The cache is listed twice, from one `EventStore::list()`: the dock's compact **Events block** and the full list on the
|
||||
`events` slide-over page (the left pane, its divider and the header toggle are gone, so events no longer take
|
||||
any of the plot's width). What makes it *available* -- recording is on, **or** a replay is on screen, **or** the
|
||||
cache already holds something (`event_recording || replaying || events_cached`) -- is decided in one place,
|
||||
`sync_events_availability()`, and it sets the block's sensitivity (`dock_events_set_sensitive()`) and the page's
|
||||
(`events_box`) from the one `available`, so they cannot disagree. Unavailable means **insensitive, never hidden**
|
||||
("disable, do not hide"): the block stays on screen greyed, and turning recording off with the page open leaves it up
|
||||
and greyed rather than stale and live.
|
||||
|
||||
**This deliberately differs from the redesign spec**, which has the block insensitive "outside trigger mode". Trigger
|
||||
mode is *not* one of the conditions: a stored event being examined is a file, which has no trigger, and a replay or a
|
||||
group *leaves* trigger mode, so a block that went grey outside trigger mode would grey itself under the very click
|
||||
that started the replay -- the one-click replay in the dock would disable the dock it was clicked in. Do not "fix" this
|
||||
to match the spec. The recording switch lives in the trigger panel, not in the block it empties.
|
||||
|
||||
The two views follow the latch on different cadences: the dock is rebuilt on **every** latch (`record_event()`), the
|
||||
page's list only while the page is open, and `open_events_page()` refreshes it when it was closed; a caller that
|
||||
opens the page any other way shows a list that is as stale as the last time it was open.
|
||||
|
||||
## Opening a file is a detour, so *Go live* is a restore
|
||||
|
||||
|
||||
@@ -491,7 +491,7 @@ Two lines of the self-check output are assertions in disguise:
|
||||
column and says there is one scroller and no stack. These are captured on the tick or at the exit probe:
|
||||
the dump runs after the window is destroyed. A source with extra panels adds *no* dock section: its panels
|
||||
are slide-over pages (`source-<id>`), so the line reads the same on the E-1608 and the probe kind.
|
||||
- `[readouts] pages cursors=1 measure=1 dock_children=1 dock_w=280 pages_sensitive=1 signals=1` (in the exit
|
||||
- `[readouts] pages cursors=1 measure=1 dock_children=3 dock_w=280 pages_sensitive=1 signals=1` (in the exit
|
||||
dump; `dock_children` counts the direct children of the dock page) -- the two pages and the dock against the
|
||||
stream. **`cursors=1 measure=1`**: both slide-over pages are registered, always and from the start (not when
|
||||
the stream has signals); `0` is a page that was never added, and then the dock's Edit buttons and the `m` key
|
||||
@@ -500,9 +500,10 @@ Two lines of the self-check output are assertions in disguise:
|
||||
`update_signal_availability()` writes them with the dock), so a run against a port with no sender reads
|
||||
`pages_sensitive=0 signals=0`, a normal run `pages_sensitive=1 signals=1`. `pages_sensitive=1` with `signals=0`
|
||||
is a page that can be opened and read with nothing to read; `pages_sensitive=0` with `signals=1` is a
|
||||
stream whose pages never came back after the signal list arrived. **`dock_children=1`**: the dock
|
||||
page holds the readouts box and nothing else (a second child is a section that crept back into the dock; 0 is a
|
||||
dock that was built empty).
|
||||
stream whose pages never came back after the signal list arrived. **`dock_children=3`**: the dock
|
||||
page holds the readouts box, a separator and the Events block (`build_events_dock()`) and nothing else (a fourth
|
||||
child is a section that crept back into the dock; 1 is a dock without its Events block, 0 a dock that was built
|
||||
empty).
|
||||
- `help layer: contextual=on hints=12 ids={events plot status cursors measures trigger trigger_advanced events_record trigger_search signals signals_config window} icon=dialog-information-symbolic(ok) hint=12x12; tour=idle steps=6 step=0 auto=1 requested=0` (in the exit
|
||||
dump, beside `dock:`) — the help affordance layer as the last tick found it. **`hints=` must equal
|
||||
the number of ids**, and the ids are the surfaces the contextual help is on: eleven of the window
|
||||
@@ -1601,24 +1602,33 @@ run they report:
|
||||
## `[events]`
|
||||
|
||||
```
|
||||
[events] live, recording off: button_visible=0 pane=0 pane_w=0 tick=1 recording=0
|
||||
[events] recording on, no trigger: button_visible=1 pane=0 pane_w=0 tick=1 recording=1
|
||||
[events] after trigger on: button_visible=1 pane=0 pane_w=0 tick=1 recording=1
|
||||
[events] live, recording off: available=0 dock_sensitive=0 page_sensitive=0 page=none
|
||||
[events] recording on, no trigger: available=1 dock_sensitive=1 page_sensitive=1 page=none
|
||||
[events] after trigger on: available=1 dock_sensitive=1 page_sensitive=1 page=none
|
||||
[events] after events page opened: available=1 dock_sensitive=1 page_sensitive=1 page=events
|
||||
[events] open alone: page=events page_box=347
|
||||
[events] preview redrawn: signals=3 stored=480x132 row_drawn=1
|
||||
[events] dir=/home/u/.cache/udpscope/events/127.0.0.1-21023 n=3 newest: stem=20261013-184140-0003 dur=2.000000 pre=1.600000 post=0.400000 edge=5.660100 signals=3 samples=20001 rate=10000 seq=3 thumb_bytes=190134 recorded=1 pane=1
|
||||
[events] activated row0: replaying=1 pane=1 pane_w=240 pane_box=228 tick=0 window=2.000 axis=[-1.600,0.400] marker=0.000 thumbnail=1 thumb_size=138x46 thumb_visible=1 rows_kept=1 selected=2/2
|
||||
[events] dir=/home/u/.cache/udpscope/events/127.0.0.1-21023 n=3 newest: stem=20261013-184140-0003 dur=2.000000 pre=1.600000 post=0.400000 edge=5.660100 signals=3 samples=20001 rate=10000 seq=3 thumb_bytes=190134 recorded=1 page=1 plot_w_stable=1 plot_w=756/756
|
||||
[events] activated row0: replaying=1 page=events page_box=347 tick=0 window=2.000 axis=[-1.600,0.400] marker=0.000 thumbnail=1 thumb_size=138x46 thumb_visible=1 rows_kept=1 selected=2/2
|
||||
[events] dock events: rows=3 pane_rows=3 same_titles=1 sensitive=1 dock_w=268 on_screen=1 marked=0 members=0 max_ok=1 expected_marked=1 at=after pane replay
|
||||
[events] dock replay: clicked=1 replaying=1 waveform_changed=1
|
||||
[events] dock events: rows=3 pane_rows=3 same_titles=1 sensitive=1 dock_w=268 on_screen=1 marked=1 members=0 max_ok=1 expected_marked=1 at=after dock click
|
||||
[events] replayed: channels=3 samples=20001 replaying=1 waveform=1
|
||||
[events] live again: replaying=0 waveform=0
|
||||
[events] live restored (after replay): window=0.500000 (want 0.500000) trigger=1 (want 1) channel='square' (want 'square') level=0 (want 0) enabled=1 armed=1 pinned=0 held=0 mode=2 pending=0 gen=10/5
|
||||
[events] recording off, trigger off: button_visible=1 pane=1 pane_w=240 tick=1 recording=0
|
||||
[events] recording off, trigger on: button_visible=1 pane=1 pane_w=240 tick=1 recording=0
|
||||
[events] recording off, trigger off: available=1 dock_sensitive=1 page_sensitive=1 page=events
|
||||
[events] recording off, trigger on: available=1 dock_sensitive=1 page_sensitive=1 page=events
|
||||
[events] compare buttons: cached=2 selected=2 compare=1 accumulate=1
|
||||
[events] compare: kind=1 rows=1 channels=8 samples0=32964 traces0=4 traces1=-1 window=2.000 recording=1 pane=1
|
||||
[events] dock events: rows=2 pane_rows=2 same_titles=1 sensitive=1 dock_w=268 on_screen=0 marked=-1 members=0 max_ok=1 expected_marked=-1 at=two events latched
|
||||
[events] compare: kind=1 rows=1 channels=8 samples0=32964 traces0=4 traces1=-1 window=2.000 recording=1 page=events
|
||||
[events] dock events: rows=2 pane_rows=2 same_titles=1 sensitive=1 dock_w=268 on_screen=0 marked=-1 members=2 max_ok=1 expected_marked=-1 at=comparison up
|
||||
[events] compare plots: area_b=1 rows_a=1 rows_b=1 list=4 x_a=[-2.000,0.000] x_b=[-2.000,0.000] same_x=1
|
||||
[events] compare sync: y_a=-3.500 y_b=-3.500 mirrored=1
|
||||
[events] accumulate: kind=2 rows=1 channels=8 samples0=32964 selected=2 traces0=8 window=2.000
|
||||
[events] group left: replaying=0 kind=0 waveform=0
|
||||
[events] live restored (after a group): window=0.500000 (want 0.500000) trigger=1 (want 1) channel='square' (want 'square') level=0 (want 0) enabled=1 armed=1 pinned=0 held=0 mode=2 pending=0 gen=22/16
|
||||
[events] dock replay: clicked=1 replaying=1 (second click, live before)
|
||||
[events] dock events: rows=2 pane_rows=2 same_titles=1 sensitive=1 dock_w=268 on_screen=1 marked=1 members=0 max_ok=1 expected_marked=1 at=second dock click
|
||||
```
|
||||
|
||||
`thumb_size` is the built size of the row's picture: `kEventThumbW` wide, and as tall as the *stored*
|
||||
@@ -1628,8 +1638,18 @@ reads `138x75`) as well as with the CSS border and the display's scale factor. W
|
||||
`0x0`, and a height pinned to the floor for *every* event says the aspect fit was dropped and tall
|
||||
previews are being squashed again.
|
||||
|
||||
**The E-1608 never latches an event** (its stream carries no trigger edge to record): on `--source e1608` the probe reads
|
||||
`n=0`, the `recording on` lines read `1 1 1` (recording is on, so the block is available), and the two
|
||||
`recording off` lines read `available=0 dock_sensitive=0 page_sensitive=0 page=events` -- the page is open and
|
||||
greyed, the "disabled, not hidden" path -- while `activated row0:` reads `replaying=0 ... marker=-1000000000000.000
|
||||
thumbnail=0 rows_kept=0` and the `dock events:` / `dock replay:` lines read `rows=0`, `clicked=0 replaying=0`. The
|
||||
`page=` of the first availability lines is whichever `source-*` page the kind opened at start-up, not `none`.
|
||||
A UDPS run against a **fresh cache** reads the same `n=0` and `rows=0` on the opening `dir=` / `dock` lines (nothing
|
||||
has latched when they are printed) and fills in from the group leg on; a UDPS run with a populated cache is the one
|
||||
that proves `same_titles` and the dock click on real rows.
|
||||
|
||||
`[events] preview redrawn:` is the repair path, and the probe forces it: step 1 deletes the newest
|
||||
`.bmp`, drops the thumbnail cache and refreshes the pane, so this line is `EventStore::redraw_preview()`
|
||||
`.bmp`, drops the thumbnail cache and refreshes the page's list, so this line is `EventStore::redraw_preview()`
|
||||
drawing the picture again from the `.npy`. `stored` is the file it wrote, read back out of the BMP
|
||||
header — `480` wide and `44 * signals` tall — and `row_drawn=1` says the row got a paintable from it.
|
||||
The probe therefore needs a cache a **previous** run populated: it fires ~4.5 s in, before this run's
|
||||
@@ -1668,23 +1688,25 @@ session that then disagrees with it takes the pair apart.
|
||||
| `signals`, `samples` | the event's channels and rows | a table built from the *rows* instead of the snapshot: only what happened to be plotted |
|
||||
| `thumb_bytes` | a real 24-bit BMP, `54 + 480 * (44 * signals) * 3` rounded up per row to a multiple of four -- so it *grows with the signal count* (190134 at three signals, 380214 at six) | a preview that is empty or missing -- the widget snapshot of a `GtkGLArea`, which is NULL -- or a picture back on a fixed total height, which gave a six-signal event 26 px a signal and an eight-signal one 20: a smear, not a waveform |
|
||||
| `recorded` | 1 while the trigger panel's switch is on | the switch not wired, or recording defaulting off (it must be on: the cache is for the event you did not expect) |
|
||||
| `pane` | 1 once the history button has been clicked | the button's handler not revealing the pane |
|
||||
| `live` | `button_visible=0 pane=0** | a pane or a button on screen with no trigger mode: a column of nothing beside a live plot |
|
||||
| `after trigger on` | `button_visible=1 pane=0` | the pane revealed by trigger mode alone (it must be on demand from the toolbar button), or the button missing in trigger mode |
|
||||
| `recording off, trigger off` and `recording off, trigger on` | `button_visible=1 pane=1 recording=0` in both **once the cache holds anything** (the probe's own run does by this point) | the button going away with the switch when events are already written -- an event on disk outlives the switch that wrote it -- or `pane_w` not being 240 with the pane open. With an *empty* cache the same two lines read `button_visible=0 pane=0`, which is the empty column the item exists to prevent |
|
||||
| `page` (on the `dir=` line) | 1 once the `events` page is open (the probe opens it with `open_events_page()`) | a page that did not open, or one that opened before its list was refreshed |
|
||||
| `plot_w_stable`, `plot_w=a/b` | 1, with the plot area's width before and after the first latch equal and both > 0 (756/756 on the documented window) | events squeezing the plot again (the old pane did), or two unrealised `-1` widths read as equal (the probe requires > 0) |
|
||||
| `available`, `dock_sensitive`, `page_sensitive` (the lines headed `live, recording off`, `recording on, no trigger`, `after trigger on`, `after events page opened`, `recording off, trigger off/on`) | the three equal on every line, because both views follow `sync_events_availability()`: `0 0 0` live with recording off and an empty cache; `1 1 1` once recording is on, **and it stays `1 1 1` in trigger mode and with the page open** (a replay or a group leaves trigger mode, and a block that greyed itself under that click is the trap) | the dock block and the page disagreeing (one of the two not wired to the sync), or the block going `0` under trigger mode. After recording is switched off the lines read `1 1 1` **once the cache holds anything** (the probe's own run does by this point): an event on disk outlives the switch that wrote it. With an *empty* cache they read `0 0 0` -- insensitive, **never hidden** ("disable, do not hide") |
|
||||
| `page` (on the availability lines) | `none` until the probe opens the page, then `events` | the page not registered (`open_events_page()` returning false), or a page left open by an earlier leg |
|
||||
| `open alone` | `page=events page_box=` a width near the slide-over's (347 on the documented window) | the page's box measuring 0 (an unrevealed `GtkRevealer` as the content instead of `events_box`), or a page not opened at all |
|
||||
| `dock events` | `same_titles=1`, `rows` equal `pane_rows` (both capped at 24: `pane_rows` is cut to the dock's `kDockEventsMax`), `sensitive=1`, **`dock_w` below 280** (268: the block fits the fixed dock; more means a row that no longer ellipsizes), `max_ok=1` (dock children <= 24 + the "N more" row). `on_screen` counts filled markers: **1 after a replay, 0 while live or in a group**; `marked` is the index of the first filled row (-1 when none), `expected_marked=1` when the filled row is the replayed record's file (-1 where no replay is named); `members` counts hollow markers: **2 with a comparison up**, 0 otherwise. The `at=` text names the moment | `same_titles=0` is the two lists rebuilt from different data or one of them stale (the dock follows every latch, the page only while open or on Manage -- a latch landing between the page's refresh and the read can show `rows=9 pane_rows=8` once, which is a probe-time race, not a defect, if it does not persist); `on_screen` above 1 is two filled markers (`event_on_screen` not shared with the title); `members=0` in a comparison is the group not marking its members; `max_ok=0` is the cap lost |
|
||||
| `dock replay` | `clicked=1 replaying=1 waveform_changed=1`; the second line, from live, reads `clicked=1 replaying=1` | the dock row's click not reaching `replay_event()` (`clicked=0` with a non-empty list), or the click rebuilding the dock list under its own handler. With an empty cache (`rows=0`) it reads `clicked=0 replaying=0`, which is not a defect |
|
||||
| `thumbnail`, `thumb_size`, `thumb_visible` | 1, a non-zero size, and 1 | the picture loaded through `gtk_picture_set_filename()`, which fails silently (the paint-able is there, the allocation is there, nothing is drawn), or a `GtkPicture` given a size request and no cap -- its natural size is its content, so the row's text is squeezed off. `thumb_size` is the reading that catches both. It also catches the cache never being *created*: `event_preview_texture()` answers from the cache or not at all, so a reader that does not create one leaves **every** row blank, which is what `thumbnail=0` meant for as long as it read 0 |
|
||||
| `preview redrawn` | `stored=480x<44*signals>` and `row_drawn=1` | a preview that cannot be repaired. The `.npy` is the event and the `.bmp` is only a picture of it, so a write that failed, a truncated file or a cache from an older build must be redrawn rather than shown as a blank row -- `row_drawn=0` is a row the user reported as "sometimes missing" |
|
||||
| `axis`, `marker`, `pane_box` (in `activated row0`) | `[-pre, +post]` of the event just opened, `marker=0.000`, and a `pane_box` close to `pane_w` | an opened event framed on something other than its own pin; a trigger line left **parked** (a large negative) rather than on the edge, which is a file opened without its marker; or a pane whose *rendered* width is 0 while the divider says 240 -- the position is what was asked for, `pane_box` is what the pane actually got, and a divider at 240 with a zero-width pane is still no pane |
|
||||
| `axis`, `marker` (in `activated row0`) | `[-pre, +post]` of the event just opened, `marker=0.000` | an opened event framed on something other than its own pin; a trigger line left **parked** (a large negative) rather than on the edge, which is a file opened without its marker. `page=events` and `page_box` (the page's width, as in `open alone`) say the page is still up and has a width: a replay from the page must not close it |
|
||||
| `rows_kept`, `selected` (in `activated row0`) | `rows_kept=1` and `selected=N/N` -- the counts either side of the activation equal | the list **rebuilt** by the activation. It is the row *object* that is compared, not its index: a rebuilt list has a row 0 too, and only identity tells a list that was left alone from one that was torn down under the user's scroll position and their comparison selection. `mark_events_on_screen()` must rewrite the titles in place |
|
||||
| `replaying` (last line) | 0 | a replay with no way back: *Live* must start a fresh client and thread |
|
||||
| `window`, `trigger`, `channel`, `level` (in `live restored`) | each equal to its `(want …)` | the way back out of a file view discarding what the live view was set to. One wrong half names which writer won: a `window` that reads the **session file's** value is the deferred `apply_layout()` beating the restore; `trigger=0` is the toggle the file view switched off and nobody switched back; an empty or shifted `channel` is an index restored instead of a name, after a rebuild renumbered (or a group renamed) the channels; and a `level` that moved on its own is `rebuild_trigger_signals()` re-defaulting it because `trigger_level_touched` was not restored with the configuration |
|
||||
| `pending`, `gen` (in `live restored`) | `pending=0`, and the two generations different | the restore still waiting. It is gated on the stream's first CONFIG, so `pending=1` says the channel list the stored trigger name needs never arrived -- with `channels=0` behind it, a session file that enabled nothing |
|
||||
| `pinned`, `held`, `mode` (in `live restored`) | `pinned=0`, **or** `pinned=1` with `held=1` | a file's pin surviving the way back, which is a live view that cannot be navigated. `apply_time_window()` drops it; the second reading is the legitimate case -- a restored trigger that was *on* can latch again before the probe reads, and a held capture is pinned, correctly |
|
||||
| `pane_w` | the divider's position: 0 while the pane is closed, its remembered width (240 by default) while it is open | the pane reserving a column while closed (a paned hands its start child its minimum unless the position says otherwise), or a width that is not remembered |
|
||||
| `tick` | 1 in live mode, **0** once a file is on screen | the signal list still offering "acquire this signal" for samples that are already stored |
|
||||
| `window` (in `activated row0`) | the **event's** span -- the probe sets 0.5 s just before the click, so reading the event's span says opening it framed it | a replay that leaves the previous window alone, or one that restates it every frame instead of once |
|
||||
| `compare buttons` | `selected=2` and both buttons sensitive | the buttons reading something other than the selection, or staying insensitive with a valid pair selected |
|
||||
| `compare` | `kind=1 rows=1 channels=8 samples0=33045 traces0=4`, `pane=1` | `samples0` above the stamps pushed -- a live producer left running, pushing four stream signals into the group's eight channels; `rows=1` because the layout is the user's and must not be re-derived; `traces0=4` because the four slots the layout holds are renamed, not replaced |
|
||||
| `compare` | `kind=1 rows=1 channels=8 samples0=33045 traces0=4`, `page=events` | `samples0` above the stamps pushed -- a live producer left running, pushing four stream signals into the group's eight channels; `rows=1` because the layout is the user's and must not be re-derived; `traces0=4` because the four slots the layout holds are renamed, not replaced |
|
||||
| `compare plots` | `area_b=1 rows_a=1 rows_b=1 list=4 same_x=1` | no second plot on screen, a second plot with a different row count, two plots not on one x window, or `list` above the signal count -- the panel listing each event's copy of a signal as a signal of its own |
|
||||
| `compare sync` | `mirrored=1`, with `y_a` and `y_b` equal to the range the probe set on the first plot | the mirror not running, or running the wrong way: a y (or x span) change in one plot that the other never sees |
|
||||
| `accumulate` | `kind=2 rows=1 channels=8 samples0=33045 traces0=8 window=2.000` | the second event's layers not landing: `traces0` stays at the four signals the layout holds when the slots were not free, or the row named after a previous group's suffix so nothing binds |
|
||||
@@ -2360,14 +2382,15 @@ is what the compact-row CSS rule is for.
|
||||
## `events pane:`
|
||||
|
||||
```
|
||||
[udpscope] events pane: compare=1 accumulate=1 list=1 revealer=1 nested=1
|
||||
[udpscope] events pane: compare=1 accumulate=1 list=1 nested=1
|
||||
```
|
||||
|
||||
The events pane's frame read from the window: the two group actions, the list, and the revealer the `.ui`
|
||||
builds, plus `nested` — the list being *inside* that revealer, which is the one thing a lost id cannot show
|
||||
(a list parented elsewhere reads 1 everywhere else). `[events]` is the other half, and it drives the pane end
|
||||
to end: recording off, recording on, the trigger, the history button that reveals it, a row click and the
|
||||
replay, then Compare and Accumulate — so the file's buttons are read by being used.
|
||||
The `events` page's frame read from the window (the page's content is `events_box` of `events-pane.ui`; there is
|
||||
no revealer, divider or header toggle any more, so none is read): the two group actions, the list, plus `nested`
|
||||
— the list being *inside* `events_box`, which is the one thing a lost id cannot show (a list parented elsewhere
|
||||
reads 1 everywhere else). **All four must read 1.** `[events]` is the other half, and it drives the page end
|
||||
to end: recording off, recording on, the trigger, the page opened with `open_events_page()`, a row activation
|
||||
and the replay, then Compare and Accumulate — so the file's buttons are read by being used.
|
||||
|
||||
## `[windowpop]`
|
||||
|
||||
|
||||
+34
-25
@@ -433,14 +433,14 @@ precedent, not an exception.
|
||||
config that has seen the first-run tour, which would close the page it opens (see the next-but-one bullet).
|
||||
The reading's own entry is in `probe-readings.md`. (Probe name `dock_section_probe`; the variable and the
|
||||
`[side-tab-probe]` tag keep their old names so existing scripts keep working. The measurements used to be a
|
||||
dock section the probe scrolled out of view, with padding; the dock now holds only readouts, so there is
|
||||
dock section the probe scrolled out of view, with padding; the dock now holds the readouts and the Events block, so there is
|
||||
no scroll and no padding.)
|
||||
- `UDPSCOPE_PROBE_CURSORS_MS` and `UDPSCOPE_PROBE_MEASURES_MS` also print the dock's copy of what they read:
|
||||
a `dock cursors:` line after every `[cursors-probe]` report and a `dock measures:` line at the
|
||||
measures probe's added / settled / final stops. **`same_text=1` on every one** is the assertion that the
|
||||
dock and the page show one string; `fits=1` that the Readouts box is no wider than the dock. Entries in
|
||||
`probe-readings.md`. The exit chain also prints one `[readouts]` line (pages registered, the dock's one
|
||||
child, the dock's width, the pages' sensitivity, whether the stream has signals) on every exit-dump run.
|
||||
`probe-readings.md`. The exit chain also prints one `[readouts]` line (pages registered, the dock page's three
|
||||
children, the dock's width, the pages' sensitivity, whether the stream has signals) on every exit-dump run.
|
||||
- `UDPSCOPE_PROBE_DOCK_SESSION_MS=2500` with `UDPSCOPE_EXIT_AFTER_MS=8000` -- the session file's `dock` key,
|
||||
end to end: the dock hidden, saved and reloaded; shown again; and a file from the previous version
|
||||
(`side_tab = "measure"`, no `dock`) loaded without an error. Three `[dock-session]` lines, each `ok=1`;
|
||||
@@ -721,41 +721,50 @@ as distinct from the fault injection below, which exercises ingest:
|
||||
leg of it (`title='Opening <basename>' detail='reading the recording'`, before `waveform loaded:`).
|
||||
A run that names its source with `--server` prints **no** `loading:` line at all: every other probe
|
||||
here is on that path and must stay on it.
|
||||
- **The event cache round-trips, and only where it is available.** `UDPSCOPE_PROBE_EVENTS_MS` reads
|
||||
the pane's availability first, in the state the app starts in -- `[events] live: button_visible=0
|
||||
pane=0 recording=1`, **no pane and no button without trigger mode** -- then turns trigger mode on
|
||||
through the real toggle (`[events] after trigger on: button_visible=1 pane=0`: the button appears,
|
||||
the pane stays collapsed) and clicks the button, waits for a latch, then reads the cache and replays
|
||||
the newest event through the pane's own row:
|
||||
- **The event cache round-trips, and only where it is available.** `UDPSCOPE_PROBE_EVENTS_MS` needs a
|
||||
config that has seen the tour (it opens a slide-over page) and a throwaway `XDG_CACHE_HOME`. It reads the
|
||||
availability first, in the state the app starts in -- `[events] live, recording off: available=0
|
||||
dock_sensitive=0 page_sensitive=0 page=none` (the dock block and the page agree, both insensitive and neither
|
||||
hidden) -- then switches recording on (`available=1 dock_sensitive=1 page_sensitive=1`; **trigger mode is not
|
||||
a condition**, and the line after the trigger is turned on reads the same), opens the `events` page with
|
||||
`open_events_page()` (`page=events`, `[events] open alone: page=events page_box=347`), waits for a latch, then
|
||||
reads the cache and replays the newest event through the page's own row:
|
||||
`[events] dir=… n=1 newest: dur=2.000000 pre=1.000000 post=1.000000 edge=… signals=4 samples=20001
|
||||
rate=10000 seq=1 thumb_bytes=230454 recorded=1 pane=1`, then
|
||||
`[events] activated row0: replaying=1 thumbnail=1 thumb_size=108x36 thumb_visible=1` (the size is
|
||||
what says the picture is *on screen*: a paint-able alone is not), `[events] replayed: channels=4
|
||||
samples=20001 replaying=1 waveform=1` and `[events] live again: replaying=0 waveform=0`. It ends on
|
||||
the pane's availability, through the trigger panel's switch: `[events] recording off, trigger off` and
|
||||
`[events] recording off, trigger on` -- once the cache holds events (the probe's own run has written
|
||||
some by this point) both read `button_visible=1 pane=1 recording=0`, because a button that shows the
|
||||
cache must not go away with the switch that wrote it; against an *empty* cache the same two lines read
|
||||
`button_visible=0 pane=0`, which is the empty column the item exists to prevent. **`samples` must equal the
|
||||
export's row count for the same window** (`20001` for a 2 s event at 10 kS/s), `thumb_bytes` must be
|
||||
a real BMP (`54 + 480*160*3`-ish, i.e. hundreds of kB, not 0), `thumbnail` must be 1 (the row loaded
|
||||
the picture) and the last line must read `replaying=0` -- a replay with no way back is a one-way
|
||||
rate=10000 seq=1 thumb_bytes=230454 recorded=1 page=1 plot_w_stable=1 plot_w=756/756` (**`plot_w_stable=1`**: the
|
||||
first latch did not move the plot, which the old pane did), then
|
||||
`[events] activated row0: replaying=1 page=events … thumbnail=1 thumb_size=138x46 thumb_visible=1` (the size is
|
||||
what says the picture is *on screen*: a paint-able alone is not), the dock's reading of the same cache --
|
||||
`[events] dock events: rows=N pane_rows=N same_titles=1 sensitive=1 dock_w=268 on_screen=1 … at=after pane
|
||||
replay` -- then **one click on a dock row**, `[events] dock replay: clicked=1 replaying=1 waveform_changed=1`,
|
||||
and the dock read again (`marked=1`, `expected_marked=1`: exactly one filled marker, on the replayed record),
|
||||
`[events] replayed: channels=4 samples=20001 replaying=1 waveform=1` and `[events] live again: replaying=0
|
||||
waveform=0`. It ends on the availability, through the trigger panel's switch: `[events] recording off,
|
||||
trigger off` and `[events] recording off, trigger on` -- once the cache holds events (the probe's own run has
|
||||
written some by this point) both read `available=1 dock_sensitive=1 page_sensitive=1`, because a block that
|
||||
shows the cache must not go away with the switch that wrote it; against an *empty* cache (and on the E-1608,
|
||||
which never latches) the same two lines read `0 0 0` with the page still open -- insensitive, never hidden.
|
||||
**`samples` must equal the export's row count for the same window** (`20001` for a 2 s event at 10 kS/s),
|
||||
`thumb_bytes` must be a real BMP (`54 + 480*160*3`-ish, i.e. hundreds of kB, not 0), `thumbnail` must be 1 (the
|
||||
row loaded the picture) and the last line must read `replaying=0` -- a replay with no way back is a one-way
|
||||
door. It then **waits for a second latch** (bounded: twelve retries) and drives the two group layouts
|
||||
through the buttons: `[events] compare buttons: cached=2 selected=2 compare=1 accumulate=1` (the
|
||||
buttons read the selection), `[events] halve`… `[events] halves a_first=20001 a_second=0 b_first=0
|
||||
b_second=20001` -- **the two halves of the ring, and the reading that says a comparison's gap is a
|
||||
gap: the first event's channel is finite only in the first half and the second's only in the second**
|
||||
-- `[events] compare: kind=1 rows=4 channels=8 samples0=40002 traces0=2 traces1=2 window=4.000
|
||||
recording=1 pane=1` (`samples0` must be exactly the stamps pushed, **not more**: a live producer left
|
||||
running pushes its own four signals into the group's eight channels), `[events] compare plots:
|
||||
recording=1 page=events` (`samples0` must be exactly the stamps pushed, **not more**: a live producer left
|
||||
running pushes its own four signals into the group's eight channels), the dock line `members=2 … at=comparison
|
||||
up` (the hollow markers on the group's members, which only the dock shows), `[events] compare plots:
|
||||
area_b=1 rows_a=1 rows_b=1 x_a=[-2.000,0.000] x_b=[-2.000,0.000] same_x=1` (the second plot exists, is
|
||||
on screen, mirrors the row count, and both are on one x window), then the sync -- the probe sets a y
|
||||
range nobody would arrive at by accident on the *first* plot and reads the second a few frames later:
|
||||
`[events] compare sync: y_a=-3.500 y_b=-3.500 mirrored=1` -- and `[events] accumulate: kind=2 rows=1
|
||||
channels=8 samples0=33045 selected=2 traces0=8 window=2.000` (`traces0=8`: the four signals the layout
|
||||
holds plus one layer each for the second event, **in the same row**, which is what an accumulation
|
||||
means). Run it with a throwaway
|
||||
`XDG_CACHE_HOME` and `UDPSCOPE_EXIT_AFTER_MS` long enough for the second latch (20 s).
|
||||
means). The exit dump adds `events pane: compare=1 accumulate=1 list=1 nested=1` and the readouts line's
|
||||
`dock_children=3`. Run it with a throwaway
|
||||
`XDG_CACHE_HOME` and `UDPSCOPE_EXIT_AFTER_MS` long enough for the second latch (20 s). The dock row cap
|
||||
(24) is only exercised by a cache that has filled past it: `max_ok=1`.
|
||||
- **A waveform round-trips.** Export from a live run (`UDPSCOPE_EXPORT_MS` with
|
||||
`UDPSCOPE_EXPORT_PATH`, against the mock at 10 kS/s), then open that file with `--open` and an
|
||||
exit dump. The load must print
|
||||
|
||||
Reference in new issue
Block a user