OpentronsOT2Backend: validate head8 pickup geometry against the Opentrons API#1136
Draft
BioCam wants to merge 9 commits into
Draft
OpentronsOT2Backend: validate head8 pickup geometry against the Opentrons API#1136BioCam wants to merge 9 commits into
OpentronsOT2Backend: validate head8 pickup geometry against the Opentrons API#1136BioCam wants to merge 9 commits into
Conversation
Characterization tests ahead of the transport-seam refactor (Phase 0). The existing suite already asserts the pickup/drop/aspirate/dispense wire calls; this adds the four remaining ot_api call sites so the seam refactor cannot change them silently: - home() issues one ot_api.health.home() - list_connected_modules() returns ot_api.modules.list_connected_modules() verbatim - stop() cancels the active run via the requestor and clears mounted pipettes - a discard to the deck trash at api_version >= 7.1.0 routes through the addressable area (move_to_addressable_area_for_drop_tip + drop_tip_in_place), not drop_tip No production code changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduces a single transport seam (Phase 1): every ot_api call - including the run-cancel requestor calls in stop() - now goes through self._ot, set to the ot_api module in __init__. Behaviour is unchanged (self._ot is ot_api), but a subclass can now dry-run the backend by swapping the handle for a recording stand-in, the way STARChatterboxBackend overrides STAR's transport. The dedicated `import ot_api.requestor as _req` is dropped; ot_api.requestor is reachable through the handle (self._ot.requestor). Guarded by the Phase 0 characterization tests, which patch ot_api.* and stay green because self._ot is the same module object. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nsport seam Phase 2. A chatterbox sibling to OpentronsOT2Backend that swaps the self._ot transport handle for a recorder, so the real backend logic - pipette selection, tip and volume tracking, the per-operation wire calls - runs unchanged with no hardware and no ot_api library. Mirrors STARChatterboxBackend (transport-only override) rather than OpentronsOT2Simulator (which reimplements the high-level methods single-channel). Issued calls are printed and collected in .commands. The recorder returns canned data for the reads setup() and the operations make back (mounted pipettes, api version, labware define, save_position, modules). Exported from backends/__init__. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mulator Phase 3. Direct tests run the chatterbox with no ot_api installed: setup resolves both mounts, a pickup -> aspirate -> dispense -> trash-discard records exactly one wire call per operation, an unknown pipette name raises, and serialize() captures the mounts. A differential audit runs the same single-channel protocol through the chatterbox and the reference OpentronsOT2Simulator and asserts identical tracked outcomes (tip mounted, source/destination volumes). The audit is scoped to the single-channel overlap, since the simulator cannot represent a multi-channel head. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The OT-2 talks HTTP through ot_api rather than a pylabrobot.io transport, so its wire traffic never reached the IO log other backends produce. Wrap the real ot_api handle in a transparent _IOLogger proxy that logs every call (submodules recursed, plain attributes passed through) at LOG_LEVEL_IO. The chatterbox recorder logs the same way, so a dry run captures the same wire trace as a real run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First slice of building the 8-channel head into the backend itself. Adds the channel model: _pipette_channel_count (8 for a multi, 1 for a single), _channel_map (per-mount channel blocks, left then right), num_channels derived from it, and _pipette_id_for_channel routed through it. A multi mount now reports 8 channels (0 = back / row A) instead of 1; single-channel setups are unchanged, so the characterization net and the chatterbox tests stay green. Per-column command issuing and the pickup geometry guard build on this next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ep 2) Routes pick_up_tips / aspirate / dispense / drop_tips through a new _resolve_pipette_and_primary(ops, use_channels): it maps the channels to the single mount they address (rejecting a mix of mounts) and picks the primary op (lowest nozzle). A multi column op now issues exactly one ot_api command at the primary well while PLR tracks all 8 channels. can_pick_up_tip is likewise routed through the channel map so the multi's channels 1-7 are accepted (previously hardcoded channel 0 = left, 1 = right). The single-channel _get_*_pipette helpers stay - OpentronsOT2Simulator still uses them. Verified in simulation through the chatterbox: one pick_up_tip / one aspirate_in_place / one dispense_in_place for a full 8-channel column, all eight channels tracked, and a cross-mount selection rejected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Remove test_multi_mount_reports_eight_channels: num_channels==9 is already pinned by test_channel_map_multi_mount_is_eight_channels and exercised by the 8-channel column test. - Remove the chatterbox-vs-simulator differential: both backends share the PLR frontend that does the tracking, so they can only diverge by raising, which the full-protocol recording test already guards. - Strip tautological assertEqual(offset_x, offset_x) (x3 in each of test_tip_pick_up and test_tip_drop) and a duplicate well_name assert in test_tip_drop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
d656480 to
2a2c4d9
Compare
…p 3) Adds _check_head8_pickup: a multi pickup must use a channel-0-anchored contiguous block [0..k] (the API anchors at the back nozzle and only declares front-anchored layouts), and the head also grabs occupied tipspots below the selection within its 8-nozzle reach. pick_up_tips rejects such a pickup unless allow_undeclared_tip_pickup is set (per call, keyword-only, or on the backend), in which case _absorb_undeclared_tips accounts for the grabbed tips so PLR tracking stays consistent, with a warning. The keyword-only override keeps OpentronsOT2Simulator's **kwargs signature compatible. Verified through the chatterbox: full column OK, back-anchored [1..7] rejected, grab-extra A1:F1 rejected, and allow_undeclared absorbs all eight with a warning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2a2c4d9 to
8d55661
Compare
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.
The 8-channel head is rigid: the Opentrons API anchors a multi pickup at the back (channel-0) nozzle and only approves front-anchored, contiguous nozzle layouts. Without a check, the backend could send a pickup the API cannot declare, or one whose tipspots do not line up with the nozzles.
This validates an 8-channel pickup before it is sent: the channels must be a channel-0-anchored contiguous block
[0, 1, ..., k], the tipspots must come from a single rack and a single column, and they must run consecutively down that column in channel order at the 9 mm nozzle pitch. A back-anchored selection such as[1..7](which would leave row A and overhang the back edge) is rejected with an explanation. Tips that undeclared nozzles still grab are accounted for in PLR tip tracking so a partial pickup leaves the trackers consistent.Stacked on #1135, which adds the 8-channel model this guards. The self-contained change here is the commit above that base.
🤖 Generated with Claude Code