Skip to content

STARBackend: resolve installed X-arms into XArmInformation at setup#1167

Open
BioCam wants to merge 2 commits into
PyLabRobot:mainfrom
BioCam:pr1-xarm-information
Open

STARBackend: resolve installed X-arms into XArmInformation at setup#1167
BioCam wants to merge 2 commits into
PyLabRobot:mainfrom
BioCam:pr1-xarm-information

Conversation

@BioCam

@BioCam BioCam commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

The STAR reports its X-arm layout at setup across three replies - arm widths, drive travel ranges, and working envelopes - but nothing fuses them into a single record. The two underlying request methods returned the raw firmware strings, had zero in-tree callers, and left any consumer to re-parse and cross-reference all three sources itself.

This adds the source-of-truth record the X-arm tracking work builds on: one SingleXArmInformation per installed arm, resolved once at setup, mirroring iSWAPInformation (#1055) and Head96Information (#1084).

Resolved parameters

  • number_x_arms — count of installed arms: 1 (left rail only, the common STAR) or 2 (both rails).
  • position — which rail the arm sits on: left or right.
  • width — arm width in mm, from the machine configuration (e.g. 370.0 dual-rail; ≤300 single-rail).
  • model — variant derived from width: hamilton_legacy_star_dual_rail_arm (width > 300) or hamilton_legacy_star_single_right_rail_arm (width ≤ 300).
  • reference_point — where the tracked X refers to, derived from width: center (dual-rail) or right (single-right-rail).
  • x_range — drive travel (min, max) in mm (e.g. dual-rail left (95.0, 1340.4)).
  • workspace_range — reachable X workspace (min, max) in mm (e.g. (-323.2, 1517.4)).

Changes

  • Adds XArmInformation (left/right, number_x_arms) and SingleXArmInformation (the fields above), both frozen(eq=False) like the other Information records.
  • _build_x_arm_information fuses the arm widths, drive travel ranges, and working envelopes into the record at setup, exposed via the x_arm_information property (raises before setup).
  • Model and reference point derive from width via _x_arm_model_and_reference.
  • request_maximal_ranges_of_x_drives and request_working_envelopes_per_arm (renamed from request_present_wrap_size_of_installed_arms) now parse their replies into typed (min, max) dicts instead of returning the raw string.
  • DriveConfiguration.is_present reports whether a drive carries any module, so an absent right arm resolves to None.
  • STARChatterboxBackend emits the matching replies and builds the record at setup like the hardware backend.

Behaviour: additive - the two request methods had no in-tree callers, so the rename and return-shape change affect nothing downstream, and x_arm_information is new surface. The single-rail left-drive minimum is fabricated only in the chatterbox (named _DUAL_RAIL_LEFT_X_MIN, 95.0); hardware reads its own value from the drive-range query, and no single-rail dump exists yet to test that path (#822).

Tests: adds TestXArmInformation (the fused record on the default single-left-arm sim, plus _x_arm_model_and_reference by width) and TestXArmRangeQueries (the range and working-envelope parsers against replies observed on real machines); ruff format, ruff check --select I,F, and mypy are clean, and the STAR suite passes.

🤖 Generated with Claude Code

@BioCam
BioCam requested a review from rickwierenga July 19, 2026 15:55
@BioCam
BioCam force-pushed the pr1-xarm-information branch 2 times, most recently from db798f9 to e960cb1 Compare July 20, 2026 14:11
Fuse the STAR's X-arm configuration information (arm widths, drive travel ranges,
working envelopes) into one record resolved once at setup, mirroring
iSWAPInformation and Head96Information. Adds XArmInformation /
SingleXArmInformation, the x_arm_information property, _build_x_arm_information,
and DriveConfiguration.is_present; the drive-range and working-envelope request
methods now parse into typed dicts instead of returning the raw firmware string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@BioCam
BioCam force-pushed the pr1-xarm-information branch from e960cb1 to 03630d7 Compare July 20, 2026 14:52
Comment on lines +1252 to +1255
@property
def is_present(self) -> bool:
"""Whether this X-drive carries any module, i.e. the X-arm exists."""
return any(vars(self).values())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Comment on lines +1380 to +1399
@dataclass(frozen=True, eq=False)
class XArmInformation:
"""The machine's X-arm layout, resolved once at setup.

The top-level per-machine X-arm record: one `SingleXArmInformation` per installed
arm, keyed by the rail it sits on. A STAR carries an arm on the `left` rail; an
optional second arm on the `right` rail makes `right` non-None (so `number_x_arms`
is 1 or 2). Built by `STARBackend._build_x_arm_information` from the machine
configuration and the X-drive queries, and immutable thereafter. This is the
reference frame the arm-mounted modules - pipetting channels, the 96-head, the
iSWAP - are positioned against.
"""

left: Optional["SingleXArmInformation"] = None
right: Optional["SingleXArmInformation"] = None

@property
def number_x_arms(self) -> int:
"""Number of installed X-arms."""
return sum(arm is not None for arm in (self.left, self.right))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this need a class, why dont we just store these on the star backend?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same reasons as Head96Information, PipChannelInformation and ISWAPInformation:

Organisation, rather than dispersion as individual class attributes.
Retrieval of associated values as one unit.
Easier simulation declaration and testing.
Simplified use in the upcoming xarm upgrades (xrange enforcement and iSWAP range).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yes we certainly need a class for what is currently called SingleXArmInformation but it seems we can just store what is currently XArmInformation on the star backend directly in left_x_arm and right

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean have two separate class attributes + separate helper property to declare the number available?

Remove `XArmInformation`/`SingleXArmInformation` and resolve each X-drive's
geometry (width, travel range, workspace range) directly onto its
`DriveConfiguration` inside `request_extended_configuration`, alongside the
module bits. `model` and `reference_point` become properties derived from
`width`. `right_x_drive` is now `Optional`, `None` when no second arm is
installed, so arm presence is a plain `is None` check. The separate
`_resolve_x_arm_geometry` setup step is gone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants