Just pushed some semi-big changes to PLR:
Removed the Simulator in favor of the new Visualizer
I was working on supporting all Machine
s in the simulator, but felt that it was too cumbersome to duplicate the state tracking in both Python (tip trackers and the like) and in JS. Simulating stuff in this way is kinda neat from a technical perspective, but does not provide any real value.
- Instead, you can now spin up a
Visualizer
which will simply visualize what is happening on the deck, including state (as used to be the case in the simulator). - Methods like
pick_up_tips
no longer exist in JavaScript. The only functions in JS now are for state updates. - You can also start Visualizer while running a method on a physical robot such as STAR.
- To do âclassicalâ simulations, use a software backend like the ChatterBox, and visualize the deck.
Example:
from pylabrobot.visualizer import Visualizer
vis = Visualizer(resource=lh)
await vis.setup()
Full tutorial: https://docs.pylabrobot.org/using-the-visualizer.html (adopted from the simulator tutorial)
All Machine
s, including LiquidHandler
, are now a Resource
I wanted the Visualizer to visualize any Resource/Machine, and so LiquidHandler had to become a Resource. PlateReader already was, and it makes a lot of sense for devices to be conceptualized as Resource-subclasses. Machine
is now the base class for âfunctionalâ resources / resources that take a backend.
-
In the case of LH, the LH-resource only has one child: the deck. This preserves hardware agnosticity in the interface.
-
It is now possible to assign multiple liquid handlers to a shared, top level
Lab
resource if youâre using multiple robots. -
LiquidHandler, as a Resource, currently gets its name, size_{x,y,z} from the deck parameter.
-
I will provide methods for conveniently initializing devices (like already exists for plates). Perhaps like
def HamiltonSTAR() -> LiquidHandler
. This will probably return a LiquidHandler configured with deck and backend, but subject to change.
Resource state updates
To accommodate state mirroring, I added/changed some methods on Resource:
serialize_state
: serialize the state for a single resource. Subclasses like Well and TipSpot override this method. Previously, this method was only available on Deck and serialized state for all children too.serialize_all_state
: serialize state for this resource and children. Callsserialize_state
. Subclasses donât override.load_state
: load state as given byserialize_state
. Previously, this method was only available on Deck and loaded all state.load_all_state
: load all state from aresource_name: resource_state
dictionary.save_state_to_file
: save the state of a resource and all children to a fileload_state_from_file
: load state for this resource and all children from a fileregister_state_update_callback
: register a callback method to be called when the state updates. The registered method will receive the new state as the only parameter.ResourceDidUpdateState = Callable[[Dict[str, Any]], None]
.deregister_state_update_callback
: deregister a callback_state_updated
: private method to call all registered callbacks. Subclasses call this when state has been updated.
State is now less dependent on a liquid handler deck, and can easily be monitored by Visualizer but also custom methods (e.g. to get notified of a well running low in volume).
Actionable changes TLDR:
- use Visualizer instead of the simulator
- if you were loading serialized deck state with
load_state
from a dictionary, useload_all_state
.
(all of this is up for discussion)