Skip to content

Stacker: sequential ("stacking access") storage capability#1119

Closed
c-reiter wants to merge 2 commits into
PyLabRobot:mainfrom
c-reiter:stacker-capability
Closed

Stacker: sequential ("stacking access") storage capability#1119
c-reiter wants to merge 2 commits into
PyLabRobot:mainfrom
c-reiter:stacker-capability

Conversation

@c-reiter

@c-reiter c-reiter commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a Stacker capability for sequential / LIFO plate storage - the code counterpart to the "Stacking Access (Sequential)" category that's already described in the storage user guide but had no implementation (today every storage device is forced through the random-access Incubator).

Design per the discussion in #1113. @rickwierenga preferred a composable capability over a split-frontend hierarchy, with the transfer position as part of the stacker, so this:

  • is a standalone capability wrapping a StackerBackend (no shared StorageBackend base, no changes to Incubator);
  • owns the transfer position (the "loading tray", borrowing the incubator's term).

What's here

  • Stacker(Machine, Resource) - holds one or more single-ended LIFO stacks (each a ResourceStack(direction="z")) plus a loading tray.
    • downstack(stack) → moves the accessible (top) plate onto the loading tray and returns it.
    • upstack(stack, plate=None) → moves a plate from the loading tray onto a stack (defaults to whatever is on the tray).
    • LIFO is enforced by ResourceStack (only the top plate is accessible); stack height/positions come from Plate.stacking_z_height via the nesting added in ResourceStack: nest stacked plates by Plate.stacking_z_height #1112.
    • Helpers: get_accessible_plate, get_stack_by_plate_name, summary.
  • StackerBackend — minimal device interface: set_stacks, downstack, upstack. Deliberately no door/temperature/shaking (a stacker is not an incubator).
  • StackerChatterboxBackend — no-op backend for tests/demos.
  • 10 tests (LIFO order, nesting height, empty-stack / occupied-tray / no-plate errors, foreign-stack rejection, etc.).

Intended consumers

The Agilent BenchCel (#1109) and the HighRes MicroServe. Migrating the BenchCel from its current faked-PlateCarrier Incubator model onto this capability is a planned follow-up.

Notes / follow-ups

  • Serialization round-trip is intentionally not implemented yet — it needs ResourceStack serialization support (ResourceStack.__init__ takes direction, not size_*, so it doesn't round-trip through the generic path). Stacker.serialize works; Stacker.deserialize raises NotImplementedError for now (and resolves the Machine/Resource MRO ambiguity).
  • Open design questions from Sequential/stacking ('Stacking Access') storage abstraction backed by ResourceStack #1113 still worth your call: a generic "transfer position" abstraction shared with incubators, and whether carousels fold in later.

Adds a `Stacker` capability for sequential/LIFO plate storage, the code
counterpart to the "Stacking Access (Sequential)" category already in the
storage docs. Per the design discussion in PyLabRobot#1113, this is a composable
capability (wrapping a `StackerBackend`) rather than a split frontend
hierarchy, and it does not touch the existing random-access `Incubator`.

- `Stacker(Machine, Resource)`: holds one or more single-ended LIFO
  `ResourceStack` stacks plus a transfer position (the "loading tray",
  borrowing the incubator's term). `downstack` moves the accessible (top)
  plate onto the tray; `upstack` moves a plate from the tray onto a stack.
  Stack height/positions come from `Plate.stacking_z_height` via
  ResourceStack nesting.
- `StackerBackend`: minimal device interface (`set_stacks`, `downstack`,
  `upstack`) — deliberately no door/temperature/shaking.
- `StackerChatterboxBackend` for tests/demos.

Intended for the Agilent BenchCel and HighRes MicroServe; migrating the
BenchCel onto this capability is a follow-up. Serialization round-trip is
left for a follow-up (needs ResourceStack serialization support).

Refs PyLabRobot#1113.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Maps the documented "Random access" / "Stacking access (sequential)"
retrieval patterns to the `Incubator` and new `Stacker` capabilities, and
adds a `Stacker` section (loading tray, LIFO `downstack`/`upstack`,
ResourceStack nesting) with a runnable `StackerChatterboxBackend` example.
@rickwierenga

Copy link
Copy Markdown
Member

high level:

interface looks good overall

it would be good to target v1 with this and make Stacker a Capability (v1) rather than a Machine (v0)

@c-reiter

c-reiter commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1142, which implements this as a v1 Capability (per @rickwierenga's feedback here and on #1113) sharing a LoadingTrayRetrieval base with AutomatedRetrieval, rather than a v0 Machine. Closing in favour of the v1 version.

@c-reiter c-reiter closed this Jul 1, 2026
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