Writing custom automation plugins
Writing new automations
An automation is a hands-off way to adjust the environment for the microbes. We currently support three types of automations: dosing, LED, and temperature. A dosing automation involves when to add media (ex: a turbidostat). A LED automation uses the additional LED pockets, and user-supplied LEDs, to add or remove visible, UV, or IR light into the vial (ex: a photobioreactor). A temperature automation is how users can control the temperature of the vial.
Writing an automation involves creating a Python class with specific methods and parent classes. It would be helpful to be familiar with Python classes before beginning. Here's an example of a (naive) turbidostat automation, i.e. it will add fresh media and remove old media when a optical density trigger is exceeded. We'll go through each line of code after:
from pioreactor.background_jobs.subjobs.dosing_automation import DosingAutomation class NaiveTurbidostat(DosingAutomation): key = "naive_turbidostat" def __init__(self, target_od, **kwargs): super(NaiveTurbidostat, self).__init__(**kwargs) self.target_od = target_od def execute(self): if self.latest_od > self.target_od: self.execute_io_action(media_ml=1.0, waste_ml=1.0)
First important thing is to subclass from
from pioreactor.background_jobs.subjobs.dosing_automation import DosingAutomation class NaiveTurbidostat(DosingAutomation): ...
We need a "key" to i) distinguish this from other automations, and ii) be able to be communicated between systems (think: the web UI to Python, and back). The
key class attribute does this for us. Normally, it's the snakecase of the class name.
key = "naive_turbidostat"
Next, we initialize our class. Here we can add settings we want to accept from the user: how much volume to add, what is our target optical density. There is a limited number of settings to change here: at the time of writing,
volume. Note the boilerplate
super(...) are important.
def __init__(self, target_od, **kwargs): super(NaiveTurbidostat, self).__init__(**kwargs) self.target_od = target_od
For example, if we needed
duration to compute some important value, we could "ask" for it by including it in the
def __init__(self, target_od, duration, **kwargs): super(NaiveTurbidostat, self).__init__(**kwargs) self.target_od = target_od self.duration_in_seconds = duration * 60
duration minutes, the function
execute is called. This is the core logic of the automation. In our (simple) case, we want to dilute the vial if we have exceed the
def execute(self): if self.latest_od > self.target_od: self.execute_io_action(media_ml=1.0, waste_ml=1.0)
Since we are working with a fixed volume,
media_ml must equal
waste_ml, else an error will be thrown. What is
latest_od? Our class, when active, is listening to growth-rate-calculating job's output of normalized optical density. Hence when
execute runs, we'll have access to the most up-to-date value of normalized optical density. Likewise, there is a
latest_growth_rate that updates when a new growth-rate value is produced.
How do we run this automation now? We can invoke it in a script like so:
""" run on the command line with $ python3 name_of_script.py Exit with ctrl-c """ import signal from pioreactor.background_jobs.subjobs.dosing_automation import DosingAutomation from pioreactor.whoami import get_unit_name, get_latest_experiment_name class NaiveTurbidostat(DosingAutomation): ... if __name__=="__main__": doser = NaiveTurbidostat( target_od=2.0, unit=get_unit_name(), experiment=get_latest_experiment_name() ) while True: signal.pause()
This will start the job, but you will notice nothing is happening. Likely that's because no optical density measurements are being processed. You'll need to have
Turning your automation in a Python package to share
Following the template here: https://github.com/Pioreactor/pioreactor_custom_dosing_automation, you can package and deploy your automation to your Pioreactor cluster, or for others to use.
Note that in that template package, there are ways to add fields to the configuration (see
additional_config.ini, which gets merged with
config.ini on installation), and adding your automation to the web UI (see the specific folder structure in the