v1b1 changes#1000
Draft
rickwierenga wants to merge 70 commits intomainfrom
Draft
Conversation
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>
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>
feee0ba to
162dd2c
Compare
Makes gripper_length and gripper_z_offset required on PreciseFlexArmBackend (no sensible backend-level defaults — they depend on the mounted gripper) and moves the per-model defaults (162 mm length, 0 mm z offset) to the PreciseFlex400 Device wrapper so users can override them when installing a non-stock gripper. Also renames PF400Params.z_tool_offset to gripper_z_offset for naming consistency with the user-facing parameter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Rick Wierenga <rick_wierenga@icloud.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BioCam
reviewed
Apr 27, 2026
…ding invalid C0 RA pn= (#1012)
PIPChannel.request_firmware_version and STARDriver.request_firmware_version now parse the RF response through parse_star_firmware_version_date and return a datetime.date, matching what STARHead96Backend already did. ztouch_probe_z_height uses version.year < 2022 directly instead of regex re-parsing the raw string. Drop the chatterbox-None fallback from STARHead96Backend.request_firmware_version: chatterbox paths should explicitly override the method (or accept the ValueError) rather than silently advertising a fabricated date. Legacy STARBackend.request_pip_channel_version keeps its str return type by inlining the raw RF query — the new PIPChannel method now returns datetime so direct delegation would have broken the legacy contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the read-window from a backend/device-construction parameter to a per-call argument on BarcodeScanner.scan(read_time=...). Backends for scanners without a configurable window (chatterbox, Keyence) accept and ignore.
scan() / scan_barcode() now return None when the read window elapses with no successful decode. Reserved for the "nothing seen" case; hardware faults still raise BarcodeScannerError. Liconic propagates the Optional through read_barcode_inline / scan_barcode.
…0-100, PF400 park override
- GripperLocation → CartesianPose; add JointPose=Dict[int,float] alias
- PreciseFlexGripperLocation → PreciseFlexCartesianPose; PFAxis → Axis
- Cart.rail field → rail_position; consistent rail_position kwarg naming
- Percentage params suffixed _pct (speed_pct, acceleration_pct, finger_speed_pct, etc.)
with 0-100 ValueError validation across set_profile_*, set_motion_profile_values,
set_monitor_speed (lower bound relaxed from 1 to 0), and _set_grasp_data
- park() consolidates the firmware "movetosafe" call (move_to_safe removed)
- New PreciseFlex400Backend overrides park() with explicit joint pose
{BASE:170, SHOULDER:0, ELBOW:180, WRIST:-180}; PreciseFlex400 device wires it up
- Legacy shim updated to delegate to renamed new-module API; legacy public API preserved
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.
No description provided.