Skip to content

v1b1 changes#1000

Draft
rickwierenga wants to merge 61 commits intomainfrom
v1b1
Draft

v1b1 changes#1000
rickwierenga wants to merge 61 commits intomainfrom
v1b1

Conversation

@rickwierenga
Copy link
Copy Markdown
Member

No description provided.

rickwierenga and others added 30 commits March 23, 2026 12:42
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
y: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Notebook autoreload creates new class objects, breaking isinstance
checks on backend params (silently falling back to defaults). BackendParams
uses a metaclass with __instancecheck__ that falls back to qualname+module
comparison, which stays stable across reloads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DeviceBackend -> Driver (with backward-compat alias)
- Device._backend -> Device._driver, param backend -> driver
- New CapabilityBackend ABC for capability-specific backend interfaces
- All 15 abstract capability backends now extend CapabilityBackend
- Concrete backends extend both their capability backend and Driver
- Serialization key "backend" -> "driver" (deserialize accepts both)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Every monolithic backend that extended both a CapabilityBackend and Driver
is now split into:
- Driver: owns I/O, connection lifecycle, device-level ops
- CapabilityBackend: protocol translation, encodes capability calls into
  driver commands

Devices split: HepaFan, BioShake, Pico, Opentrons TempModule, Hamilton
HeaterShaker, Hamilton TiltModule, Keyence BarcodeScanner, XPeel, SCILA,
MettlerToledo, A4S, VSpin/Access2, CLARIOstar, SpectraMax 384+/M5.

Also: CapabilityBackend gains _on_setup/_on_stop hooks, Capability._on_setup
calls backend._on_setup, updated creating-capabilities.md, updated all
legacy wrappers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Recording backends and chatterbox backends are now pure CapabilityBackends.
Test devices use a _NullDriver for the Device lifecycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test capabilities directly via cap._on_setup() instead of wrapping
in a fake Device.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix import sorting across 10+ files (ruff format --fix)
- Fix MolecularDevices legacy backend: reference renamed class, update test mocks to patch at correct level (Driver/Protocol instead of legacy wrapper)
- Fix Pico legacy tests: split Driver/MicroscopyBackend usage to match new architecture
- Fix Opentrons temp module: add base-type annotations for if/else branches
- Fix Liconic: use _on_setup/_on_stop (CapabilityBackend API)
- Fix Azenta A4S: type: ignore[safe-super] for abstract Driver methods
- Fix Pico backend: self._snap_images() instead of self._driver._snap_images()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move shaker/tc capabilities into base class with has_shaking,
has_temperature, supports_active_cooling flags. Add resource
definitions for BioShake3000, BioShake3000Elm, BioShake3000ElmDWP,
and BioShakeQ1 from spec sheets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nds (#957)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract autoload firmware protocol into a standalone class that takes a
driver reference and operates on track numbers instead of Carrier objects.
The legacy STARBackend and new STAR device can both wire into this class.
Includes 36 tests covering all command types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PlateReader now delegates reads through AbsorbanceCapability,
LuminescenceCapability, and FluorescenceCapability via adapter backends
that wrap the legacy PlateReaderBackend. Extracted _DictBackendParams
into pylabrobot/legacy/_backend_params.py for reuse across legacy
adapters.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move autoload, cover, x-arm, wash station, and ~44 generic driver
infrastructure methods (firmware queries, EEPROM, area reservation,
configuration) into the new STARDriver architecture. Legacy backend
methods now delegate to new classes or have deprecation docstrings.

- STARAutoload: autoload module control (carrier loading, barcode, LEDs)
- STARCover: front cover lock/unlock/enable/disable
- STARXArm: left/right X-arm positioning (parameterized by side)
- STARWashStation: dual-chamber wash station drain/fill/init
- STARDriver: generic instrument operations directly on driver
- STARChatterboxDriver: updated with all subsystems
- STAR device only exposes capabilities (PIP, Head96, iSWAP)
- Subsystems live on the driver, accessed via star._driver
- 114 tests across all subsystems
- Right X-arm and wash station are conditional on hardware config
- X-arm methods use mm (PLR standard), not 0.1mm firmware units
- Fixed pre-existing assertion bugs in release_occupied_area and
  set_instrument_configuration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These methods send a command to hardware and wait for a response,
so request_ better reflects the I/O semantics. Covers PreciseFlex,
capability interfaces (temperature, humidity), and all vendor backends
(Azenta, Agilent, BMG, Byonoy, Hamilton, INHECO, Liconic, Molecular
Devices, Opentrons, Qinstruments, Thermo Fisher). Legacy public APIs
keep get_ names unchanged; only internal delegations are updated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move 14 multi-channel PIP operations to STARPIPBackend: channel
positioning (Y/Z), initialization, spread, z-safety, foil piercing.
Parameters use mm (PLR standard) with internal 0.1mm conversion.

Key changes:
- pierce_foil and step_off_foil on STARPIPBackend with explicit deck param
- iSWAP-parked checks on Y-movement methods
- Channel min Y spacing queried from firmware in driver setup()
- Right X-arm conditional on right_x_drive_large
- Wash station conditional on wash_station_*_installed
- Legacy backend aliases (left_x_arm, iswap) for PIPBackend compat
- Fixed pierce_foil one_by_one bug (z vs z+distance_from_bottom)
- Fixed _ensure_can_reach_position dead fallback (is None vs not)
- Architecture doc updated

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two new dispensing capabilities under bulk_dispensers/:
- SyringeDispensing: dispense(plate, volumes={col: vol}), prime(plate, volume)
- PeristalticDispensing: dispense(plate, volumes={col: vol}), prime(), purge()

Both use BackendParams for device-specific parameters.
Also adds BackendParams to PlateWashingCapability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- EL406Driver: FTDI I/O, batch management, device-level ops, queries
- EL406PlateWashingBackend: manifold ops (wash, aspirate, dispense, prime)
- EL406ShakingBackend: shake/soak
- EL406SyringeDispensingBackend: syringe dispense/prime
- EL406PeristalticDispensingBackend: peristaltic dispense/prime/purge

Legacy code is thin wrappers delegating to new backends. All 385 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tecture

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move plate washer docs from 00_liquid-handling/plate-washing/ to
agilent/biotek/el406/ and heater-shaker docs from
01_material-handling/heating_shaking/ to qinstruments/bioshake/.
Add Manufacturers toctree section with manufacturer-level indexes.
Include migration guide at repo root for future device migrations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move device docs from legacy category dirs (00_liquid-handling/,
01_material-handling/, 02_analytical/) to manufacturer-based layout
mirroring the codebase. Add Manufacturers toctree section and API
reference RST files for all manufacturers with autosummary directives
and autoclass for nested BackendParams. Add Sphinx cross-references
for BackendParams in notebook markdown cells.

Devices migrated: EL406, BioShake, Mettler Toledo WXS205SDU,
Azenta a4S, Azenta XPeel, Liconic STX, Inheco ThermoShake,
Inheco CPAC, Inheco SCILA, Inheco Incubator Shaker, Inheco ODTC,
Thermo Fisher Multidrop Combi.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
rickwierenga and others added 30 commits March 31, 2026 23:27
Move remaining 01_material-handling/ device docs to manufacturer layout:
Brooks PreciseFlex, Agilent VSpin, Hamilton HEPA Fan, Hamilton Heater
Shaker, Hamilton Heater Cooler, TFS Cytomat, Opentrons Temperature
Module. Create pylabrobot/hamilton/heater_cooler/ module (stubbed) with
Device/Driver/CapabilityBackend architecture. Add API reference RST
files for brooks, hamilton, opentrons. Resolve merge with remote
analytical device migrations (bmg_labtech, byonoy, molecular_devices).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Reorder dispense params to match instrument workflow (plate → cassette → speed → height → order → volumes)
- Add cassette_type to DispenseParams
- Move BackendParams dataclasses next to their methods
- Document DispensingOrder: only affects 384+ well plates, no effect on 96-well

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…o STARDriver

- Add PIPChannel class representing a single PIP channel with 10 Px firmware
  methods (RF, RV, RD, DS, RZ, QC, ZL, ZE, SI + empty_tip convenience)
- Extract STAR error classes and firmware parsing to new modules (errors.py,
  fw_parsing.py) so new code no longer imports from legacy
- Legacy STARBackend now owns a STARDriver instance and delegates send_command
  and IO to it, providing single source of truth for state tracking
- Add traversal_height as instance state on PIP, Head96, and iSWAP backends
  with use_traversal_height() context manager for temporary overrides
- Fix immersion_depth_direction bug in STARPIPBackend.aspirate where the
  removed field was still referenced
- Make ensure_iswap_parked public on STARDriver

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move all 13 STAR documentation pages from the legacy category-based
location (docs/user_guide/00_liquid-handling/hamilton-star/) to the
new manufacturer-based layout (docs/user_guide/hamilton/star/).

All notebooks updated to use the new STAR device API:
- LiquidHandler + STARBackend → STAR device with star.pip/head96/iswap
- Backend kwargs → backend_params (STARPIPBackend.AspirateParams, etc.)
- Core grippers → star.core_grippers() context manager
- Autoload → star.driver.autoload

Also:
- Rename iSWAPBackend.move_{x,y,z}_relative → move_relative_{x,y,z}
- Add STAR classes to API reference RST (BackendParams, LLDMode, etc.)
- Remove old hamilton-star directory and toctree reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts:
#	pylabrobot/legacy/liquid_handling/backends/backend.py
#	pylabrobot/legacy/liquid_handling/backends/hamilton/STAR_backend.py
#	pylabrobot/legacy/liquid_handling/backends/hamilton/planning.py
#	pylabrobot/legacy/liquid_handling/channel_positioning.py
#	pylabrobot/legacy/liquid_handling/channel_positioning_tests.py
#	pylabrobot/legacy/liquid_handling/liquid_handler_tests.py
#	pylabrobot/legacy/liquid_handling/utils.py
#	pylabrobot/liquid_handling/liquid_handler.py
#	pylabrobot/resources/container_tests.py
#	pylabrobot/resources/petri_dish_tests.py
#	pylabrobot/resources/well_tests.py
#	pylabrobot/scales/__init__.py
CI fixes:
- Add CommandSyntaxError re-export to unblock test collection (1705 tests)
- Fix F811 redefinitions in 5 legacy shims
- Remove unused imports, reformat 28 files
- Fix SCILA deserialize test (missing "type" key)

Type safety (mypy 224→0):
- Add typed accessor properties on legacy STAR backend (_pip, _iswap, etc.)
- Fix get_temperature→request_temperature bug in legacy temp controller
- Type narrowing and annotations across 22 files

Safety-critical:
- Convert ~45 assert statements to if/raise in 9 production files
  (pip_channel, heater_shaker, tilt_module, bioshake, thermoshake, cpac,
   device, capability, base) — asserts removed by python -O
- Convert tracker assert→raise (tip_tracker, volume_tracker)
- Add ensure_iswap_parked() to all 4 Head96 operations (collision prevention)
- Fix no_tip_tracking()/no_volume_tracking() context managers (add try/finally)
- Fix core gripper unbound method calls→self.driver delegation

Code quality:
- Document all 39 BackendParams classes with field descriptions, units, ranges
- Deduplicate _dispensing_mode_for_op (head96 imports from pip_backend)
- Move liquid_classes from legacy/ to hamilton/ (fix reverse dependency)
- Fix ChannelizedError + PipettingOp imports (new→legacy deps 4→1)
- Add BioShakeDriver super().__init__() call
- Fix storage chatterbox set_temperature state tracking
- Fix raise DeprecationWarning→raise NotImplementedError (11 instances)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Safety & correctness:
- Unify ChannelizedError: legacy re-exports from capabilities (fixes silent
  per-channel recovery failure when legacy backends raise through adapters)
- Add @need_capability_ready to 35 methods across 11 capabilities
- Make Device.stop() and Machine.stop() idempotent (safe double-call)
- Fix GripperArm state timing: validate before hardware, update after success
- Make USB.stop() idempotent
- Fix Head96 center() bug: use get_absolute_location(x="c", y="c") instead of
  + center() to handle rotated plates correctly (was 9mm off)
- Add Plate.plate_type and Trough.bottom_type/through_base_to_container_base
  to serialization

Head96 backend (19 new methods):
- Add initialize, move_to_z_safety, park, move_to_coordinate, move_y, move_z
- Add request_position, request_tip_presence, request_tadm_status
- Add dispensing_drive methods, firmware version query
- Proper _on_setup (check init, park) and _on_stop (z-safety, park)
- Legacy delegates to new backend (14 methods converted)

iSWAP forwarding:
- park_iswap, iswap_open_gripper, iswap_close_gripper now delegate to
  self._iswap.* with proper state tracking

Autoload lifecycle:
- _on_setup checks init status before reinitializing, parks after
- _on_stop parks to safe position
- Legacy request_pump_settings delegates to wash_station

Architecture:
- Move arms/ to capabilities/arms/ with deprecation shims
- Move liquid classes: HamiltonLiquidClass to hamilton/liquid_handlers/liquid_class.py,
  STAR classes to star/liquid_classes/, vantage to hamilton/lh/vantage/
- Create import shims for 19 old module paths with DeprecationWarning
- Fix doc links: Sphinx cross-refs, relative paths, HTML links
- Clean up redundant assertions in legacy Head96 delegation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d setup

- Port clld_probe_z_height, plld_probe_z_height, ztouch_probe_z_height,
  clld_probe_y_position to PIPChannel with proper firmware call matching
- Port clld_probe_x_position to STARXArm
- Move move_y, move_tool_z, move_stop_disk_z, request_tip_length to PIPChannel
- Add move_to_z_safety (per-channel Px:ZA) replacing C0:ZA in probe methods
- Add FirmwareLock (readers-writer): Px commands parallel, C0 exclusive
- Fix notebook char-array serialization bugs across all STAR tutorials
- Reorganize probing notebooks into probing/ folder (x, y, z)
- Update all notebooks to use star.driver.pip / star.driver.iswap
- Add 96-head features: stamping, discard_tips, direct movement, trough support
- Fix setup: instrument pre-init, autoload init check, sequential C0 during setup
- Legacy backend forwards all ported methods to new implementations
- All tests pass, mypy clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d drivers

Every non-legacy backend and driver now logs scientifically meaningful
operations at INFO level with machine identifiers: liquid handling
(aspirate/dispense/tip ops), plate reading (absorbance/fluorescence/
luminescence), temperature control, shaking, centrifugation, sealing,
weighing, robotic plate moves, and barcode scanning.

Logging focuses on experimental intent and measured read-backs rather
than raw I/O (handled by the I/O layer). All loggers standardized to
getLogger(__name__) across the non-legacy codebase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds Optional[BackendParams] to every Driver.setup(),
Device.setup(), CapabilityBackend._on_setup(), and
Capability._on_setup() method across the codebase.

Driver-specific setup parameters (skip_home, skip_reset,
use_cam) are converted to proper SetupParams(BackendParams)
dataclasses following the existing pattern for operation
methods. Legacy callers updated to wrap params accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move request_y_pos, request_x_pos, request_tip_bottom_z_position from
  STARPIPBackend to PIPChannel
- Add request_tadm_enabled (bool) to PIPChannel
- Add position_components_for_free_iswap_y_range to STARPIPBackend
- Fix move_y to enforce _min_spacing_between for neighboring channels
- Delete move_channel_y wrapper from STARPIPBackend
- Update all legacy STAR_backend methods to forward to new system

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ixin

- ShakerBackend.shake(speed, duration, backend_params) is now the abstract method
- HasContinuousShaking mixin adds start_shaking/stop_shaking for backends
  that support continuous (indefinite) shaking
- Hamilton, BioShake, Inheco, chatterbox: ShakerBackend + HasContinuousShaking
- Shaker capability gates start_shaking/stop_shaking behind isinstance check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Plate washing, syringe, and peristaltic capabilities now cache the plate
via a plate property. Capability frontends pass self.plate to the backend
so callers no longer need to pass plate per-call. Setter raises if a
plate is already assigned.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Driver caches plate via plate_holder callbacks, batch() reads from cache
- EL406 device sets plate on driver + capabilities on assignment
- Shaking backend uses ShakeParams for intensity/soak/move_home
- All backend batch(plate) calls become batch() (parameterless)
- Legacy backend sets _cached_plate directly and translates kwargs
- Notebook updated: no plate per-call, split into one-operation cells

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lint/format: fix ruff errors (duplicate import, E402 ordering, unused
import), auto-format 30 files to pass format-check, fix import sorting.

Type safety: fix all mypy errors — setup() signature overrides, missing
shake() on ShakerBackend implementors, union-attr guards, no-any-return,
abstract class instantiation. Replace assert with if/raise in production
code (head96_backend, STAR_backend).

Bug fixes:
- Liconic: set_humidity/request_current_humidity/request_target_humidity
  guarded with has_temperature_control instead of has_humidity_control
- Inheco ThermoShake: set_shaker_speed/set_shaker_shape hardcoded device
  index 1 instead of self.index; float validation used int range()
- Cytomat send_action: UnboundLocalError when timeout=None
- Pump.serialize(): called super().serialize() on Capability (no such
  method), crashed at runtime
- Hamilton STAR driver: 3x mutable default datetime.now() evaluated at
  import time
- Shaker.shake(): plate left locked on error (added try/finally)
- All shake() implementations: missing try/finally for stop_shaking()
- Legacy thermoshake: shake() passed shape to new shake() which expects
  duration; now correctly delegates to start_shaking()
- Legacy STAR: fixed delegation for position_channels_in_y_direction,
  request_tip_length, left_x_arm null guard
- PreciseFlex legacy: proper type conversion for _j/_c delegation,
  restored input validation, fixed socket mock for new driver
- No-go zone: PIP used old offset functions; now uses
  compute_channel_offsets which respects container no-go zones
- Foil piercing: step_off_foil used move_tool_z (extra queries); now
  uses direct KZ firmware commands matching legacy behavior
- Bare except: in cytation.py narrowed to except Exception:
- Removed debug print("rick2") from pip_channel.py
- Chatterbox: print() replaced with logger.debug()

Added HasContinuousShaking mixin to Liconic, Cytomat, and Heraeus
backends that implement start_shaking/stop_shaking.

Tests: all 1719 pass, 0 mypy errors, lint and format clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
STX44_IC does not have humidity control. The humidity tests need a
model with has_humidity_control=True (DC2 suffix) now that the guard
correctly checks has_humidity_control instead of has_temperature_control.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s, docs

- Change arm capability API from pickup_distance_from_top to pickup_distance_from_bottom
- Legacy LH arm ops now delegate through new arm capability via _ArmAdapter
- Add STAR() and STARLet() classes with built-in decks (no deck param)
- Add _HamiltonSTAR private base class
- Update all notebooks and docs to new API
- Add movement examples and inline backend_params to iswap.ipynb

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ckendParams

- Rename *Dispensing → *Dispensing8, PlateWashingCapability → PlateWasher96
- Rename files: syringe.py → syringe8.py, backend.py → backend8.py, etc.
- Rename attributes: .syringe → .syringe_dispenser, .peristaltic → .peristaltic_dispenser, .microscopy → .microscope
- Inline manifold_* forwarding methods into aspirate/dispense/wash/prime
- Inline _syringe_prime, _peristaltic_prime, _peristaltic_purge into public methods
- Move device-specific params to BackendParams (AspirateParams, DispenseParams, WashParams, PrimeParams)
- Validators take params objects directly, no field-by-field unpacking
- Remove columns from DispenseParams (encoded in volumes dict)
- Add plate as real param on PlateWasher96Backend.prime()
- volumes: Union[float, Dict] on frontends, Dict only on backends
- Legacy backend: explicit named params, no **kwargs
- Update all docs, notebooks, rst cross-refs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix close_gripper plate_width_tolerance default: 0 → 2.0mm (matching
  firmware spec default of 20 in 0.1mm units and legacy behavior)
- Set minimum plate_width_tolerance to 0.5mm (firmware rejects lower)
- Add manual movement section under Common Tasks
- Add rotation safety warning and safe-position move before rotate
- Wrap close_gripper demos in try/except (no plate gripped in docs)
- Ensure plate movements alternate between [1] and [2] for end-to-end runs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
dispense() with multiple volume groups was starting/stopping a batch per
group. Wrap the loop in a single batch() so the device stays in ready
state throughout. Add advanced usage section to hello-world notebook
covering manual batching, cross-subsystem protocols, and well masking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… in backends

- Add HasLoadingTray mixin for devices with a loading tray
- Add MolecularDevicesLoadingTrayBackend (sends !OPEN/!CLOSE directly)
- SpectraMaxM5/384Plus: replace PlateHolder with LoadingTray capability
- BioTekLoadingTrayBackend: send J/A commands directly, not via driver.open/close
- Cytation5/Cytation1: add HasLoadingTray mixin

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Rick Wierenga <rick_wierenga@icloud.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Rick Wierenga <rick_wierenga@icloud.com>
Each of the 4 drawers is now a LoadingTray keyed by drawer_id in
`scila.drawers`. Command sequences (PrepareForInput/OpenDoor,
PrepareForOutput/CloseDoor) and drawer_id validation move from the
driver into SCILADrawerLoadingTrayBackend. Re-add CO2-flow warning
suppression (log + continue) that had been lost in the rewrite.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…re (#989)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Hazlam Shamin <69739427+hazlamshamin@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…imbus

Deck is now passed into the driver at __init__ time (not through setup), so
the driver can construct backends with deck as a required arg. PIP/Head96
capabilities also take deck required on __init__. Legacy STARBackend still
receives deck via set_deck (legacy flow) and forwards it to the already-built
STARDriver.

Co-Authored-By: Claude Opus 4.7 (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.

3 participants