Adding io layer

This PR introduces a third layer of abstraction in PLR: the io layer. This will live in between backends and the physical reality. ALL communication of PLR with machines will go through this layer. Some IO objects will be thin wrappers, others will be more complicated.

Backends will now have an io attribute, similar to have frontends have a backend attribute. Backends will still be responsible for creating machine specific commands, but communication will be done in the io layer.

Why? This is for testing. If the communication that goes through the io layer doesn’t change, it means we are communicating the same commands to the outside world: machines will do the same thing. Having ALL communication in this layer makes that easy. It is now easy to write integration tests. When you change something in your protocol or pull from plr:main, if the io layer sends and receives the same data, the protocol will be the same.

The source of truth is a log file generated during a real automation run (or a liquid test). This means that the protocol is proven to work in physical reality, otherwise you wouldn’t get a log file (tempering is possible, but you’re your own adversary). When you change your protocol, you can easily grab that new log file and make it the file that you validate against. No manually updating tests.

Checking happens through a central object called the LogReader. This reads the commands in a log file. The IO objects will then validate that if they receive a write command the data matches that in the log file. If they receive a read command they will check that in physical reality, the specific device actually returned some data (the validation object will then actually return this data so that the backend can function normally. The objects in the IO layer (USB, FTDI, Serial, …) will all call into the single LogReader and request the next line. This ensures that not only do the IO objects read/write the correct information, they also do so in the right order. For example, the door on your cytomat is opened before the iswap puts the plate in, not after.

An added benefit of checking against log files is that more complicates sequences like step_off_foil will work even in testing. step_off_foil requests the channel positions and positions the channels based on that. Previously, it was impossible to test this function.

You can find an example notebook in docs/user_guide/validation.ipynb

it actually uses Needleman Wunsch to align firmware commands in case of an error!

l

1 Like

Would this be something we want to implement in the opentrons backend? Ie, intercepting some kind of log files to check the expected values. We could do this either by gathering the log files from opentrons_simulate, or by making our own fake io LogReader with more consistent nomenclature.

The disadvantage of opentrons_simulate is the output changes per opentrons version. The disadvantage of our own stack is that the io might not guarantee changes are consistent, as we play around with getting the interface right.

yes, since it will use the HTTP object in the io layer (thin wrapper around python requests). It is testing HTTP requests to your server

1 Like