Byonoy resource modelling

the byonoy is a nice small on deck plate reader

i wrote an initial backend for it that you can see in PR 617 add byonoy luminescence plate reader by rickwierenga · Pull Request #617 · PyLabRobot/pylabrobot · GitHub

i am thinking about how to model this as a resource. the plate reader consists of two parts, one of which is stationary during a run (the base) and a top that you move to and from the base. In between the top and the base can be a plate. See the picture below. From left to right: top, base, example plate.

I was considering making the base a ResourceHolder. However, it has two distinct child locations: one for the base and one for the plate. Also, a ResourceHolder is programmed to hold at most one resource, whereas this would hold two.

I don’t think we have a good universal model for this. Perhaps I will introduce a new resource subclass to model this unique behavior. One annoying part with this is that when moving the plate and top using a robotic, we should obviously have smart default locations. for example:

await lh.move_resource(plate, base)
await lh.move_resource(top, base)

should both ‘just work’. This means we would have to extend the if statement in lh.drop_resource with the new custom resource class. This obviously not ideal in terms of scaling, so curious if anyone has better thoughts on this.

3 Likes

I was thinking about how to build for a Byonoy to incorporate into our system, and I was going to implement a ResourceStack. I would then have generated my own local functions to replace_top, etc. however, adding that to the backend if the base were a native ResourceStack would be awesome.

Perhaps having the PR extend ResourceStack as well as the PRbackend, similar to the OT module implementation.

2 Likes

awesome! i hope to merge this PR really really soon. i just wanna have a basic idea of resource modeling here :slight_smile:

stack is an interesting idea. for stack currently, it’s mostly resources actually stacked on top of each other, meaning each resource’s location is dependent on the resource below it in the stack. for the plate readers, the top lid position sits directly on the base (in this case of the luminescence reader) regardless of whether there’s a plate

I also don’t think that this is a ResourceStack because there is no stacking happening.

Instead this is best described as a nesting process, in my opinion.
(Another example for which we will need this is Hamilton’s vacuum filter extraction system that @ben recently mentioned in another thread)

But taking a step back: What do we want the resource model to achieve for us?

I don’t think this should work: this would indicate inexplicit background logic.

But this sounds very similar but more explicit to me:

await lh.move_resource(plate, abs_reader_base["plate_holder"])
await lh.move_resource(upper_hull, abs_reader_base["hull_holder"])

(“upper hull” appears to be the term Byonoy is using: link)

→ I am proposing to model the machine as a Carrier with 2 resource_holders, one nested into the other.
Each resource_holder can then have its own dedicated child_location (one for the plate origin and one for the hull origin).
To make accessing each resource_holder (or “site”) easier we can modify the numerical indexing to keyword indexing (e.g. “plate_holder”, “hull_holder” … can be anything intuitive).

Do you think this covers all the desired functionality with existing resource models?

The thing I don’t like about this proposal is that there are no warnings:
It would be possible to move the hull onto the base and then command the plate to be added to the plate_holder, leading to a terrible crash.

One way to get around this: generate a new Carrier subclass which could be called NestedHolderCarrier which can be made to handle the access constraints.

A resource that is capable of holding multiple other resources like this is usually called “carrier” in PLR (plate/tube/trough/… carriers as well as plate hotel stacks).

But this proposal is not ideal either:
though I believe it provides the desired technical functionalities it would generate a NestedHolderCarrier assigned to a ResourceHolder on a PlateCarrier / MFXCarrier, if I’m not mistaken?
Which breaks the workcell/deck tree hierarchy…

2 Likes

interesting point, and yes i agree

(frontend logic most likely, but point stands)

love this idea

however, the easy crash scenario is not optimal.

but what is the equivalent of this that exists in PLR right now? like aspirating from a plate with a lid is not possible? i don’t think there’s anything like this for drop_resource. this implies we have to make some change to drop_resource, but it does not have to be another elif line to the big if statement. i propose maybe adding a can_put_resource or something, that drop_resource calls? the byonoy can have a custom subclass that is implicitly understood by drop_resource but overrides that new method can_put_resource?

yes

to be technically accurate, not precisely. nothing says that you can’t have a carrier as a child of a carrier. Except for PlateCarriers, Carrier children are ResourceHolder and Carrier is a Resource

1 Like

Are you proposing we start the work on PLR’s crash prevention feature with the arm command ‘put_resource’? :fire:

there already is some crash prevention stuff (PLR's safety features - #2 by rickwierenga)

Okay so…

Task 1: solve crash possibility via a general, PLR-wide safety feature that checks whether the z-space above the destination is already occupied when calling put_resource

Task 2: solve the nesting resource model challenge (e.g. Byonoy devices and vacuum filter station) by modelling these as a carrier with nested resource_holders

Just to clarify: You guys are okay with this deck tree: Deck > Carrier > PlateHolder > Carrier?

I am aware Safety Feature: Protect Channels during y-Movement by BioCam · Pull Request #355 · PyLabRobot/pylabrobot · GitHub :sweat_smile:

That’s why I emphasised the work, i.e. the bigger picture checking system we’ve been discussing for a while

1 Like

crash possibility for drop_resource

“anti crash” is a huge and abstract thing. implementations of this already exist, some contributed by you

yes

Deck > Carrier > PlateHolder [e.g. PlateHolder for holding byonoy base] → Carrier [Byonoy]

seems reasonable

however something i forgot to mention yesterday is Carriers are always number-indexed which in this case might not make sense

We could have a Carrier-analogous thing called ByonoyResource that just has two named ResourceHolders as children

1 Like

the default is always using code if it already exists but i just don’t see anything like this in PLR. We can hack it a little bit but it’s not a perfect analogy [like for example using PlateCarrier also for incubator racks is 100% the same].

if another thing like this comes up in the future we can think about abstracting it out

what we are preventing with the general can_drop_resource or whatever function is having to add a line to the big if statement in drop_resource that would make LH depend on the byonoy resource (that would obv be a bad thing)

i have named the upper part “reader” in the case of the luminescence reader, because that part connects with a cable and is the PlateReader.

For absorbance, the cable connects to the bottom. In that case I can name the top part “upper hull” (the bottom is the Reader in that case)