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
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
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}" )
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
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}" )
def execute(self, *args, **kwargs) -> events.Event: return events.NoEvent("nothing occurs in Silent.")
def execute(self, *args, **kwargs) -> events.Event: return events.NoEvent("never execute dosing events in Silent mode")