- Collapsed Args:/Returns:/Raises: blocks that only restated the
signature (364 lines): tektronix_base 48% -> ~20% doc density,
helios_laser and uc480_camera likewise. Only docstrings whose entire
body was those sections were touched.
- Preserved verbatim the comments that carry hardware knowledge the code
can't express: uc480's USB split-transaction contention note (with its
measured fps), the IS_ALLOW_STARTER_FW_UPLOAD segfault explanation, the
QImage-copy rationale, and tektronix's NUMFRAMESACQuired warning.
- README: project structure, quick start, and every usage example now
describe code that exists (they referenced hardware/bbd202.py,
CoherentHOPSLaser, get_curve_binary, and 'python -m scanengine.app',
none of which do). Added a headless-scan example and a read-a-scan-file
example, since reuse without the GUI is the point of the refactor.
- SETUP: structure section defers to README instead of keeping a second
stale copy; documents the vendored uEye SDK and the Genesis quarantine.
- ruff is now clean repo-wide: fixed the remaining raise-from, unused
loop variables, placeholder f-strings, and a non-strict zip; the
widget-layout semicolon idiom is an explicit config ignore rather than
22 standing warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gui/qt_workers.py — one QueueWorker base replaces the per-device command
queue + dispatch + signal boilerplate. The loop blocks on the queue
instead of waking 10-20x/second forever (test_idle_worker_does_not_spin
asserts an idle worker burns ~no CPU). PollingQueueWorker adds
self-rescheduling polling: the next poll is queued only after the
previous finishes, so a device slower than the interval can't accumulate
a backlog (test_polling_never_overlaps_or_backs_up).
Helios responsiveness — the concrete bug that motivated the above: a free
running 1 s QTimer queued a status poll that took ~2 s, so the queue grew
for as long as the panel stayed connected.
- helios_laser._query reads until the CR terminator instead of sleeping a
fixed 0.05 + 0.2 s per query
- one _query_int() helper replaces five copies of parse-with-logging
- polling is now driven by the worker; HeliosWindow's QTimer is gone
- dropped __del__, which disabled the laser and wrote to the serial port
from the garbage collector at an unpredictable time
helios_test_app.py — the worker was moveToThread'd but every call site
invoked its methods directly, so all serial I/O (including the sleeps)
ran on the GUI thread; Query All froze the UI for ~2 s. Calls now go
through a queued signal to a pyqtSlot. Also: connect/disconnect cycles
leaked a QThread + worker + 9 connections each time; 16 copies of the
not-connected guard collapse to _require_connection(); the Query Power
button called a method that has never existed (AttributeError popup) and
is now disabled and documented in KNOWN_ISSUES.
DCBiasImageWidget preallocates its image and uses set_data/set_clim, so
the live preview stops rebuilding the array and the whole artist tree per
row (O(rows^2) over a scan).
bbd20x: connect() now raises when no bays respond instead of reporting
success on the wrong port; disconnect() joins with a timeout so a wedged
reader can't hang shutdown; one _channel_for() helper replaces four
copy-pasted axis mappings; hardcoded travel limits become TRAVEL_MM; the
joke error strings are gone.
gui/widgets.py adds the shared ConnectionBar / PortSelector / bounded
LogConsole / StatusGrid for the test benches to adopt. 65 tests passing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- core/sras_format.py: THE v6 implementation — create_scan_file (writer,
byte-identical to the old one, enforced against the Phase-0 goldens),
SrasFile parser with frontier/truncation walk, and zero-copy mmap
load_angle/load_row views for multi-GB files
- core/scan_geometry.py: ScanPlan/AngleGeometry dataclasses, build_plan
(rotated-bbox trig from MainWindow._build_scan_params), travel-limit
validate_plan (limits now a StageLimits dataclass, not literals buried
in the worker), format_eta + EtaEstimator (bounded deque)
- core/config.py: ScanDefaults dataclass replaces the module-import-time
dict globals. FIXES: editing any main-window port used to rewrite
aui_defaults.json without helios_port, silently reverting the Helios
port every time (test_helios_port_survives_partial_update covers it).
Also drops the inert laser_freq_hz plumbing — scans always used the
LASER_FREQ_HZ constant.
- hardware/serial_util.py: shared 8N1 open + scored port enumeration
(promoted from t3r_control_panel); helios_laser and the panel use it
- sc3_aui_app.py and sras_scan_manager.py migrated onto core (three
format implementations down to one); ScanWorker now takes a ScanPlan
- tests: byte-identical writer vs golden, frontier over every truncation
variant, mmap==eager, geometry vs golden fixtures + invariants, config
round-trip. 28 passing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>