feat: device property readout, and the display range as a histogram panel - #153
Merged
pskeshu merged 2 commits intoSep 5, 2026
Merged
Conversation
`GET /api/devices/properties?device=<label>`, or no device for the list. Gently models four properties on the Coherent Scientific Remote — the per-line `PowerSetpoint (%)` — because those are the four it writes. The adapter exposes many more, and among them are the ones that answer whether a laser is actually emitting: enable state, interlock, faults, power readback. None of that is reachable today, so gently-project#106 has been argued from what the code commands rather than from what the hardware reports. That gap matters most when nobody can look at the instrument. The operator is working on this microscope remotely: a camera can only report photons that reach it, which needs the specimen in focus, and the light-sheet view is exactly the surface that cannot be judged out of focus. The controller can report its own state regardless of any of that. Each property comes back with its value, its allowed values where the adapter declares them, and a `read_only` flag. The allowed set turns an opaque string into a state machine you can reason about, and read-only marks a readback rather than a setting — which is precisely the distinction gently-project#106 turns on. No `require_control`: it commands nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two things were wrong with the control this replaces, and the operator named
both: the contrast adjustment sat on top of the image, and the image is the
main thing anyone interacts with.
## Nothing sits on the pixels
The controls moved out of the frame into a panel attached beneath it. An
overlay hides data, and this one also swallowed clicks in the bottom strip of
the image — a cost I had already had to write off as a `ponytail:` note, which
was the tell that it was in the wrong place.
`ImageView.attach(id, { controlsHost })` takes an external host. The zoom badge
stays over the frame, because it reports the view rather than controlling it.
## The artefact is the status
Modelled on ImageJ/Fiji's Brightness/Contrast, which every microscopist already
knows. Its insight is that the histogram and the mapping belong in one picture:
the transfer line is drawn across the data it applies to, so where black and
white fall relative to where the signal actually sits is a single glance. B&C
carries no numeric status readout, and neither does this — the operator's own
suggestion, and it is right.
Counts are on a log axis. A microscopy histogram is dominated by background,
and on a linear axis the part that matters is a flat line along zero.
Auto takes the 0.1st and 99.9th percentiles, not min and max: a handful of hot
pixels must not define white.
The histogram is computed client-side by drawing the frame to an offscreen
canvas and binning it — no new endpoint, no new traffic. Throttled to 400ms,
because the frame rate is far above what an eye needs from a histogram and each
pass is a full readback of the decoded image.
## What it is honest about
It is a histogram of the frame AS DISPLAYED: 8-bit, already percentile-stretched
per frame by the device layer before JPEG encoding. Not raw camera counts. The
panel says so, and the label carries the explanation.
That is not a limitation to hide, it is the argument for gently-project#149. Anything the
server-side stretch clipped is already gone, and this panel is what will make
that visible — nuclei jammed against the top of the range are now something an
operator can see rather than something I inferred from reading the encoder.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two commits, both from working remotely on the scope.
e2fb62b— read-only device property dumpGET /api/devices/properties?device=<label>, or no device for the list.Gently models four properties on the Coherent Scientific Remote — the per-line
PowerSetpoint (%)— because those are the four it writes. The adapter exposes many more, and among them are the ones that say whether a laser is actually emitting: enable state, interlock, faults, power readback. None of it was reachable, so #106 has been argued from what the code commands rather than what the hardware reports.The gap matters most right now: the operator is on this microscope remotely, and cannot verify the light sheet by eye. A camera only reports photons that reach it, which needs the specimen in focus — and the light-sheet view is precisely the surface that cannot be judged out of focus. The controller can report its own state regardless.
Each property returns its value, its allowed values where the adapter declares them, and a
read_onlyflag. Allowed values turn an opaque string into a state machine;read_onlymarks a readback rather than a setting, which is the distinction #106 turns on.No
require_control— it commands nothing.4ed9c2f— display range becomes a histogram panelTwo things were wrong with the old control, and the operator named both: it sat on top of the image, and the image is the main thing anyone interacts with.
Nothing sits on the pixels now. The controls moved out of the frame into a panel attached beneath it. The overlay had also been swallowing clicks in the bottom strip of the image — a cost already conceded as a
ponytail:note, which was the tell it was in the wrong place.ImageView.attach(id, { controlsHost })takes an external host. The zoom badge stays, because it reports the view rather than controlling it.The artefact is the status. Modelled on ImageJ/Fiji's Brightness/Contrast, at the operator's suggestion. Its insight is that the histogram and the mapping belong in one picture: the transfer line is drawn across the data it applies to, so where black and white fall relative to where the signal sits is a single glance. B&C carries no numeric status readout, and neither does this.
It is honest about what it shows: a histogram of the frame as displayed — 8-bit, already percentile-stretched per frame by the device layer. Not raw camera counts, and the panel says so. That is the argument for #149, not a limitation to hide: nuclei jammed against the top of the range become something an operator can see rather than something inferred from reading the encoder.
ruff, format and both mypy runs pass. JS suites 7 + 13 + 32.