docs(omny): spell out CLI-first operator interface for the vision layer
CI for csaxs_bec / test (push) Successful in 2m3s

Per feedback: the vision device(s) should be operable from the command
line as their own separately-addressable interface, not gated behind
GUI widget work. Notes this falls out of BEC's normal device-RPC model
for free (same mechanism get_last_image() already uses client-side),
and proposes thin omny.ovision_* wrapper methods matching the existing
otransfer_* wrapper convention around dev.omny_samples.*. Downgrades the
GUI mode-picker open question to strictly additive, not a prerequisite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Mupip8x7Hean6E3FFvpbj
This commit is contained in:
x01dc
2026-09-06 13:21:05 +02:00
co-authored by Claude Sonnet 5
parent d04932b68d
commit e8baa759e9
@@ -131,6 +131,12 @@ publishing a transformed view instead of (or alongside) the raw feed.
in-between interface rather than a camera-specific patch: the GUI dock watches the
`VisionInterface`'s output, and swapping/reconfiguring the source camera or the active mode
doesn't change what the dock is looking at.
- **Command-line addressable from day one, independent of any GUI work.** `set_mode()` and any other
controls are plain device RPC methods — like `get_last_image()`, they're callable directly from a
BEC session (`dev.omny_vision_parking.set_mode("edges")`) the moment the device exists, with no
GUI widget required. The operator-facing question "how does someone switch modes" therefore has an
answer on day one regardless of whether/when a GUI mode-picker gets built — see the dedicated CLI
section below.
- **Two independent consumers**, justifying building this generically rather than folding it
straight into the sample-transfer use case:
- **`SampleVisionMonitor`** (Layer 2, below) uses it for registration + the diagnostic overlay it
@@ -174,6 +180,34 @@ top of Layers 0/1*, not a separate camera-coupled implementation:
scoring and any live processed view of that same pair) avoids two devices both owning a signal for
the same physical camera pair — likely the cleaner shape, but worth prototyping both.
### Operator interface: command line first, a GUI mode-picker second
Per feedback: the tool an operator uses to drive this should not be gated behind new GUI widget
work — it should be addressable on the command line as its own, separately-callable interface,
independent of whether/when a GUI control for it exists.
- **This falls out of BEC's normal device-RPC model, not something to build specially.** Every
method on a device (`VisionInterface.set_mode()`, `SampleVisionMonitor.save_reference()`/`score()`)
is already callable directly from a BEC ipython session the moment the device exists —
`dev.omny_vision_parking.set_mode("edges")`, `dev.omny_vision_parking.score("put_mount_start_3")`
— the exact same mechanism `get_last_image()` already relies on for its own direct client-side
calls in `x_ray_eye_align.py`. No dedicated CLI layer needs to be designed from scratch; it's a
free consequence of this being a device.
- **Thin per-endstation wrapper methods, matching the existing convention.** Raw device names
(`omny_vision_parking`, `omny_vision_samplestage`) aren't the ergonomic surface an operator should
have to remember. `omny_sample_transfer_mixin.py` already establishes the pattern for exactly this
problem — its `otransfer_is_sample_slot_used()`/`otransfer_set_sample_slot()`/etc. block (lines
1175-1255) is a set of thin one-line wrappers around `dev.omny_samples.*`, added, per its own
comment, "so every sample-storage command tab-completes under `omny.otransfer_*`". The same shape
applies here: short `omny.ovision_*` wrappers (e.g. `ovision_set_mode(pair, mode)`,
`ovision_score(tag)`, `ovision_save_reference(tag)`) that dispatch to the right
`SampleVisionMonitor`/`VisionInterface` instance for the currently-active camera pair (using
`_omnycam_active_pair`, already tracked — see "Existing infrastructure" above), so an operator
never needs to know the underlying device names at all.
- **GUI mode-picker (the open question above) becomes strictly additive**, not a prerequisite —
the command-line interface is complete and usable the day `VisionInterface`/`SampleVisionMonitor`
land, regardless of GUI work landing later or not at all.
## Proposed CV pipeline (classical, no deep learning)
Per the original discussion this grew out of: classical machine vision is enough for a
@@ -252,10 +286,12 @@ this should not be an all-or-nothing flag flip. Proposed phases for the later im
`omny_vision_parking`, `omny_vision_samplestage` as two fixed device-config entries) is simpler
and matches how OMNY's own two camera pairs are already fixed, not dynamic; leaning toward this
unless a concrete need for a dynamic pairing shows up.
- **How an operator switches `VisionInterface`'s mode from the GUI.** The existing
`smear_preview_toggle` (`x_ray_eye.py:386-873`) is a single on/off switch between two fixed
channels; a `VisionInterface` with several selectable modes (raw/grayscale/edges/CLAHE/overlay)
needs a small mode picker, not just a toggle — new GUI widget surface, not sketched yet.
- **A GUI mode-picker for `VisionInterface`** (as opposed to the command-line control, which is
covered — see "Operator interface" above). The existing `smear_preview_toggle`
(`x_ray_eye.py:386-873`) is a single on/off switch between two fixed channels; a `VisionInterface`
with several selectable modes (raw/grayscale/edges/CLAHE/overlay) would need a small mode picker
instead, not just a toggle — new GUI widget surface, not sketched yet, and not a blocker for
anything else in this plan.
- **Overlay compositing vs. the GUI's own annotations.** `x_ray_eye_align.py`'s alignment GUI
already lets the operator click to place points/markers on the live image; a `VisionInterface`
"overlay" mode drawing its own graphics (contours, diff highlights) onto the frame it republishes