Feat/smargon #314

Merged
menzel merged 6 commits from feat/smargon into main 2026-09-09 12:29:57 +02:00
6 Commits
Author SHA1 Message Date
menzelandClaude Opus 4.8 58f028ec9a config(smargon): REST API port is 3000, not 8080 (:8080 is the web GUI)
CI for csaxs_bec / test (push) Successful in 1m42s
CI for csaxs_bec / test (pull_request) Successful in 1m47s
Confirmed with the smargopolo maintainer: the JSON REST API is on :3000; the
:8080 server is the web GUI (returns HTML, all API paths 404). Point host/port
back to tt-smargopolo:3000 on the coordinator and all axis motors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 14:36:51 +02:00
menzelandClaude Opus 4.8 6ef4cc6113 config(smargon): point beamline config at tt-smargopolo:8080
The cSAXS beamline smargopolo server is http://tt-smargopolo:8080 (port 8080,
not the MX-lab reference's :3000). Set host/port on the coordinator and all axis
motors so smargon.yaml loads as-is at the beamline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 14:36:50 +02:00
menzelandClaude Opus 4.8 3bb4bf60da chore(smargon): tidy sim session config (reflow, comment out sgomega)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 14:36:50 +02:00
menzelandClaude Opus 4.8 73329badb4 refactor(smargon): per-axis motors + shared controller (smaract pattern)
Expose each SCS axis as its own top-level BEC motor sharing a singleton
SmargopoloController per host:port, plus a thin `dev.smargon` coordinator for
whole-goniometer ops. Replaces the single-device-with-axis-subcomponents design.

Why: BEC's motor UX (dev.wm, umv, limits, GUI motor widgets) is keyed on
top-level devices. With sub-components, dev.wm showed `readback N/A` (it looks up
a key equal to the device name) and `limits [0,0]` (the client reads limits from
the root device's low/high_limit_travel signals). General, not a sim artifact.

- controller.py: SmargopoloController(OphydObject), singleton per (host,port,sim);
  owns transport + lock + mode; referencing / move_scs / nudge / q1..q6 diagnostics.
  Reachable as dev.<axis>.controller.<op>() (USER_ACCESS).
- positioner.py: SmargopoloMotor(Device, PositionerBase) -- top-level per-axis motor.
  scs_axis field binds the (user-chosen) BEC name to the fixed SCS axis. Exposes
  low/high_limit_travel signals and aliases readback to the device name, so dev.wm
  shows real per-axis readback + limits (low==high => unbounded). egu auto from axis.
- smargon.py: SmarGon(PSIDeviceBase) reduced to a thin coordinator delegating to the
  shared controller; keeps read-only mode/rosout signals.
- YAML: smargon.yaml + smargon_sim_session.yaml rewritten to per-axis entries
  (sgx/sgy/sgz/sgchi/sgphi[/sgomega]) + coordinator. Names user-configurable; SCS
  binding explicit via scs_axis.
- transport/ layer + fake unchanged. Tests rewritten: 29 pass vs FakeTransport
  (singleton reset fixture; covers sharing, wm readback aliasing, limits, fault paths).

Still DRAFT / untested against hardware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 14:36:50 +02:00
menzelandClaude Opus 4.8 a4b6285e60 feat(smargon): ground SCS limits + host guidance from MX-lab reference client
resources_2/Smargon.py (the MX-lab SmarGonController) gives real per-axis SCS
limits and the server hostname convention. Update smargon.yaml:
- SHX/SHY [-2,2], SHZ [10,22] (home ~18, not centred on 0), CHI [0,65],
  PHI [-180,180]  (was placeholder [-2.5,2.5] for the SH axes; SHZ was wrong).
- host: note it must be the actual reachable server (ref used mxlab1-smargopolo).
- Note PHI is continuous and the reference client wraps to [-180,180) before
  sending -- flagged as an open design question, not yet handled here.

Still DRAFT / untested against hardware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 14:36:50 +02:00
menzelandClaude Opus 4.8 c0f1b83d9f feat(smargon): scaffold SmarGon goniometer device (DRAFT, untested vs hardware)
Initial BEC ophyd integration for the SmarGon goniometer via the smargopolo
RESTful API. Controls the virtual SCS axes (the server runs the kinematics; the
underlying q1..q6 stages are never commanded directly). Structure mirrors the
Canon CR-N300 device: an ophyd-free transport (real RestTransport over urllib +
in-memory FakeTransport) with threaded poll-to-tolerance positioners and a
PSIDeviceBase parent.

Scope / decisions:
- v1 movable axes: SHX SHY SHZ CHI PHI; OMEGA optional via has_omega (YAML).
- Referencing is a deliberate operator action (reference()); moves refuse unless
  Mode.READY.
- Soft limits are user-set; smargopolo owns the coupled hardware limits. Both
  fault paths handled: up-front PUT rejection fails the move immediately, and a
  mid-move fault (mode -> 99 ERROR) aborts the in-flight move with the rosout
  detail (SmarGon._raise_if_error).
- Move completion = readback within (user-set) tolerance, done on first
  in-tolerance sample (robust to active position-hold dither).

Status: DRAFT. 27 unit tests pass against FakeTransport, but NOTHING has been
tested against a real smargopolo server or hardware. Do not commission as-is.

Open questions (to confirm with the smargopolo maintainer, W. Glettig):
- Stop semantics: no explicit stop endpoint; we abort by retargeting an axis to
  its current readback ("Follow Target" halt). Confirm this is the intended way.
- Mid-move fault signalling: we rely on /mode -> 99 (ERROR) with the reason in
  /readbackMCS rosout.msg. Confirm an out-of-range / coupled-limit violation
  reliably drives this, so the abort path is dependable.
- Per-poll cost: the mid-move check adds a /readbackMCS GET alongside the
  /readbackSCS position read (~20 req/s per moving axis at 0.1s). Revisit once we
  know whether /readbackSCS also carries mode, or once the per-motor `state`
  strings can give a truer moving/settled flag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 14:36:50 +02:00