예제 #1
0
    def run(self, counter=None):
        time.sleep(8)  # wait some time for data to arrive
        if (self.latest_growth_rate is None) or (self.latest_od is None):
            self.logger.debug("Waiting for OD and growth rate data to arrive")
            if not ("od_reading" in pio_jobs_running()) and (
                    "growth_rate_calculating" in pio_jobs_running()):
                self.logger.warn(
                    "`od_reading` and `growth_rate_calculating` should be running."
                )

            event = events.NoEvent(
                "waiting for OD and growth rate data to arrive")

        elif self.state != self.READY:
            event = events.NoEvent(f"currently in state {self.state}")

        elif (time.time() - self.most_stale_time) > 5 * 60:
            event = events.NoEvent(
                "readings are too stale (over 5 minutes old) - are `od_reading` and `growth_rate_calculating` running?"
            )
        else:
            try:
                event = self.execute(counter)
            except Exception as e:
                self.logger.debug(e, exc_info=True)
                self.logger.error(e)
                event = events.NoEvent("")

        self.logger.info(f"triggered {event}.")
        self.latest_event = event
        return event
예제 #2
0
    def execute(self, *args, **kwargs) -> events.Event:
        if self.latest_od <= self.min_od:
            return events.NoEvent(
                f"latest OD less than OD to start diluting, {self.min_od:.2f}")
        else:
            fraction_of_alt_media_to_add = self.pid.update(
                self.latest_growth_rate, dt=self.duration / 60
            )  # duration is measured in hours, not seconds (as simple_pid would want)

            # dilute more if our OD keeps creeping up - we want to stay in the linear range.
            if self.latest_od > self.max_od:
                self.logger.info(
                    f"executing triple dilution since we are above max OD, {self.max_od:.2f}."
                )
                volume = 2.5 * self.volume
            else:
                volume = self.volume

            alt_media_ml = fraction_of_alt_media_to_add * volume
            media_ml = (1 - fraction_of_alt_media_to_add) * volume

            self.execute_io_action(alt_media_ml=alt_media_ml,
                                   media_ml=media_ml,
                                   waste_ml=volume)
            event = events.AltMediaEvent(
                f"PID output={fraction_of_alt_media_to_add:.2f}, alt_media_ml={alt_media_ml:.2f}mL, media_ml={media_ml:.2f}mL"
            )
            event.media_ml = media_ml  # can be used for testing later
            event.alt_media_ml = alt_media_ml
            return event
예제 #3
0
 def execute(self, *args, **kwargs) -> events.Event:
     if self.latest_od >= self.target_od:
         self.execute_io_action(media_ml=self.volume, waste_ml=self.volume)
         return events.DilutionEvent(
             f"latest OD={self.latest_od:.2f} >= target OD={self.target_od:.2f}"
         )
     else:
         return events.NoEvent(
             f"latest OD={self.latest_od:.2f} < target OD={self.target_od:.2f}"
         )
예제 #4
0
    def execute(self, *args, **kwargs) -> events.Event:
        if self.latest_od <= self.min_od:
            return events.NoEvent(
                f"current OD, {self.latest_od:.2f}, less than OD to start diluting, {self.min_od:.2f}"
            )
        else:
            output = self.pid.update(self.latest_od, dt=self.duration)

            volume_to_cycle = output * self.volume

            if volume_to_cycle < 0.01:
                return events.NoEvent(
                    f"PID output={output:.2f}, so practically no volume to cycle"
                )
            else:
                self.execute_io_action(media_ml=volume_to_cycle, waste_ml=volume_to_cycle)
                e = events.DilutionEvent(
                    f"PID output={output:.2f}, volume to cycle={volume_to_cycle:.2f}mL"
                )
                e.volume_to_cycle = volume_to_cycle
                e.pid_output = output
                return e
예제 #5
0
 def execute(self, *args, **kwargs) -> events.Event:
     if self.previous_od is None:
         return events.NoEvent("skip first event to wait for OD readings.")
     elif self.latest_od >= self.target_od and self.latest_od >= self.previous_od:
         # if we are above the threshold, and growth rate is greater than dilution rate
         # the second condition is an approximation of this.
         self.execute_io_action(alt_media_ml=self.volume,
                                waste_ml=self.volume)
         return events.AltMediaEvent(
             f"latest OD, {self.latest_od:.2f} >= Target OD, {self.target_od:.2f} and Latest OD, {self.latest_od:.2f} >= Previous OD, {self.previous_od:.2f}"
         )
     else:
         self.execute_io_action(media_ml=self.volume, waste_ml=self.volume)
         return events.DilutionEvent(
             f"latest OD, {self.latest_od:.2f} < Target OD, {self.target_od:.2f} or Latest OD, {self.latest_od:.2f} < Previous OD, {self.previous_od:.2f}"
         )
예제 #6
0
 def execute(self, *args, **kwargs) -> events.Event:
     return events.NoEvent("nothing occurs in Silent.")
예제 #7
0
 def execute(self, *args, **kwargs) -> events.Event:
     return events.NoEvent("never execute dosing events in Silent mode")