Hey Cameron,
thanks for the tips
Is the second option you mentioned also taking effect during a running job?
Regarding the context in number 3, I didn’t choose to create the context through a with statement because that to me seems like only doable in a short-term sense. So for blanking it would work but then after the blanking how would the OD reading service be established for the whole duration of the automation without user intervention and without “endangering” the automation process tree due to od_reading job crash / killing?
I had a test run of the automation end because the od_reading job started by the automation encountered an error and shut down which in turn ended the whole process tree.
def _blanking(self):
if self._blanked:
return
# TODO: Add log message for user that blanking is in progress and settings shouldn't be changed
# In order to start blanking we first clean up all running jobs to create a known environment
if is_pio_job_running("growth_rate_calculating"):
with JobManager() as jm:
jm.kill_jobs(
job_name="growth_rate_calculating"
)
self.logger.debug("Killed previous growth_rate_calculating job")
if is_pio_job_running("od_reading"):
with JobManager() as jm:
jm.kill_jobs(job_name="od_reading")
self.logger.debug("Killed previous od_reading job")
# Now we set up the jobs in a way we want them:
# Assuming a running stirring job, reducing the rpms. How to deal with dodging?
# OD reading every 2 seconds (with 35 measurements for growth rate thats 70 seconds)
# Growth rate with reset cache so no previous data taints our results
self._set_rpm(500) # Set rpms to 500
self.od_reader = start_od_reading(
config.get("od_config.photodiode_channel", "1", fallback=None),
config.get("od_config.photodiode_channel", "2", fallback=None),
2, # One reading every 2 seconds
calibration=load_active_calibration("od"),
)
self.logger.debug("Started OD job with 2 second interval for blanking.")
self.growth_rate_calculator = GrowthRateCalculator(
ignore_cache=True, # Redo the initial blanks on start of the job
unit=self.unit,
experiment=self.experiment,
)
# We check if growth rate has reinitialized its baseline, otherwise we wait
while not hasattr(self.growth_rate_calculator, "ekf"):
time.sleep(5)
self.logger.debug("Growth Rate Kalman filter has been initialized")
# Now that a blank has been established we can go back to jobs as they are defined in the config
# Interval for OD reading job can not be changed, therefore its restarted.
self.od_reader.clean_up()
if self._debug:
self.logger.debug(
config.get("od_reading.config", "samples_per_second", fallback=0.2)
)
# Not that the blanking is done a od_reader job with the parameters defined in the config needs to run for the remainder of the experiment -> couple of weeks
self.od_reader = start_od_reading(
config.get("od_config.photodiode_channel", "1", fallback=None),
config.get("od_config.photodiode_channel", "2", fallback=None),
1
/ float(
config.get("od_reading.config", "samples_per_second", fallback=0.2)
), # Fallback is once per 5 seconds
calibration=load_active_calibration("od"),
)
self.logger.debug("Started config OD reading job")
# Stirring job can be changed so we take that value from config
self._set_rpm(config.getfloat("stirring.config", "target_rpm", fallback=400))
self._blanked = True # Set the flag so we do not blank again if returning to ready state; i.e. resuming from pause
def on_disconnected(self) -> None:
# If we end the dosing automation its important that we clean up everything we started in this context
if self.od_reader is not None:
self.od_reader.clean_up()
if self.growth_rate_calculator is not None:
self.growth_rate_calculator.clean_up()
super(PALE_ALE, self).on_disconnected()
This would be the blanking method and the on_disconnect method of the dosing automation I originally wrote and had problems with. I removed the creation of a stirring job from this, as this was causing similar problems as I described for the od_reading job.