44import uuid
55from time import sleep
66from typing import cast
7+ from typing import ClassVar
78
89from pioreactor import structs
910from pioreactor import types as pt
1213from pioreactor .background_jobs .od_reading import start_od_reading
1314from pioreactor .calibrations import utils as calibration_utils
1415from pioreactor .calibrations .registry import CalibrationProtocol
16+ from pioreactor .calibrations .session_flow import advance_session
17+ from pioreactor .calibrations .session_flow import CalibrationComplete
18+ from pioreactor .calibrations .session_flow import get_session_step
1519from pioreactor .calibrations .session_flow import run_session_in_cli
1620from pioreactor .calibrations .session_flow import SessionContext
17- from pioreactor .calibrations .session_flow import SessionEngine
1821from pioreactor .calibrations .session_flow import SessionExecutor
22+ from pioreactor .calibrations .session_flow import SessionStep
23+ from pioreactor .calibrations .session_flow import StepRegistry
1924from pioreactor .calibrations .session_flow import steps
2025from pioreactor .calibrations .structured_session import CalibrationSession
2126from pioreactor .calibrations .structured_session import CalibrationStep
@@ -138,15 +143,10 @@ def start_reference_standard_session(
138143 )
139144
140145
141- def reference_standard_flow (ctx : SessionContext ) -> CalibrationStep :
142- if ctx .session .status != "in_progress" :
143- if ctx .session .result is not None :
144- return steps .result (ctx .session .result )
145- return steps .info ("Calibration ended" , "This calibration session has ended." )
146+ class Intro (SessionStep ):
147+ step_id = "intro"
146148
147- if ctx .step == "intro" :
148- if ctx .inputs .has_inputs :
149- ctx .step = "record_readings"
149+ def render (self , ctx : SessionContext ) -> CalibrationStep :
150150 return steps .info (
151151 "OD reference standard calibration" ,
152152 (
@@ -155,106 +155,121 @@ def reference_standard_flow(ctx: SessionContext) -> CalibrationStep:
155155 ),
156156 )
157157
158- if ctx . step == "record_readings" :
158+ def advance ( self , ctx : SessionContext ) -> SessionStep | None :
159159 if ctx .inputs .has_inputs :
160- if is_pio_job_running ("od_reading" ):
161- raise ValueError ("OD reading should be turned off." )
162-
163- ir_led_intensity = get_ir_led_intensity ()
164- channel_angle_map = get_channel_angle_map (
165- cast (pt .ODCalibrationDevices , ctx .session .target_device )
166- )
167-
168- od_readings = _record_reference_standard_for_session (ctx , ir_led_intensity )
169- recorded_ods = [0.0 , 1000 * STANDARD_OD ]
170- timestamp = current_utc_datetime ().strftime ("%Y-%m-%d_%H-%M" )
171-
172- calibration_links : list [dict [str , str | None ]] = []
173- if isinstance (od_readings , dict ):
174- for raw_channel , od_reading_payload in od_readings .items ():
175- pd_channel = cast (pt .PdChannel , raw_channel )
176- if pd_channel not in channel_angle_map :
177- continue
178- angle = channel_angle_map [pd_channel ]
179- od_value = float (od_reading_payload ["od" ])
180-
181- recorded_voltages = [0.0 , 1000 * od_value ]
182- curve_data_ = calibration_utils .calculate_poly_curve_of_best_fit (
183- recorded_ods , recorded_voltages , degree = 1
184- )
185- calibration = structs .ODCalibration (
186- created_at = current_utc_datetime (),
187- calibrated_on_pioreactor_unit = get_unit_name (),
188- calibration_name = f"od{ angle } -optics-calibration-jig-{ timestamp } " ,
189- angle = angle ,
190- curve_data_ = curve_data_ ,
191- curve_type = "poly" ,
192- recorded_data = {"x" : recorded_ods , "y" : recorded_voltages },
193- ir_led_intensity = ir_led_intensity ,
194- pd_channel = pd_channel ,
195- )
196- calibration_links .append (ctx .store_calibration (calibration , f"od{ angle } " ))
197- else :
198- for raw_channel , od_reading_struct in od_readings .ods .items ():
199- pd_channel = cast (pt .PdChannel , raw_channel )
200- if pd_channel not in channel_angle_map :
201- continue
202- angle = channel_angle_map [pd_channel ]
203- od_value = float (od_reading_struct .od )
204-
205- recorded_voltages = [0.0 , 1000 * od_value ]
206- curve_data_ = calibration_utils .calculate_poly_curve_of_best_fit (
207- recorded_ods , recorded_voltages , degree = 1
208- )
209- calibration = structs .ODCalibration (
210- created_at = current_utc_datetime (),
211- calibrated_on_pioreactor_unit = get_unit_name (),
212- calibration_name = f"od{ angle } -optical-reference-standard-{ timestamp } " ,
213- angle = angle ,
214- curve_data_ = curve_data_ ,
215- curve_type = "poly" ,
216- recorded_data = {"x" : recorded_ods , "y" : recorded_voltages },
217- ir_led_intensity = ir_led_intensity ,
218- pd_channel = pd_channel ,
219- )
220- calibration_links .append (ctx .store_calibration (calibration , f"od{ angle } " ))
221-
222- if not calibration_links :
223- raise ValueError ("No matching channels were recorded for this calibration." )
224-
225- ctx .complete ({"calibrations" : calibration_links })
160+ return RecordReadings ()
161+ return None
162+
163+
164+ class RecordReadings (SessionStep ):
165+ step_id = "record_readings"
166+
167+ def render (self , ctx : SessionContext ) -> CalibrationStep :
226168 return steps .action (
227169 "Record reference standard" ,
228170 "Continue to record OD readings against the optical reference standard." ,
229171 )
230172
231- return steps .info ("Unknown step" , "This step is not recognized." )
173+ def advance (self , ctx : SessionContext ) -> SessionStep | None :
174+ if is_pio_job_running ("od_reading" ):
175+ raise ValueError ("OD reading should be turned off." )
176+
177+ ir_led_intensity = get_ir_led_intensity ()
178+ channel_angle_map = get_channel_angle_map (cast (pt .ODCalibrationDevices , ctx .session .target_device ))
179+
180+ od_readings = _record_reference_standard_for_session (ctx , ir_led_intensity )
181+ recorded_ods = [0.0 , 1000 * STANDARD_OD ]
182+ timestamp = current_utc_datetime ().strftime ("%Y-%m-%d_%H-%M" )
183+
184+ calibration_links : list [dict [str , str | None ]] = []
185+ if isinstance (od_readings , dict ):
186+ for raw_channel , od_reading_payload in od_readings .items ():
187+ pd_channel = cast (pt .PdChannel , raw_channel )
188+ if pd_channel not in channel_angle_map :
189+ continue
190+ angle = channel_angle_map [pd_channel ]
191+ od_value = float (od_reading_payload ["od" ])
192+
193+ recorded_voltages = [0.0 , 1000 * od_value ]
194+ curve_data_ = calibration_utils .calculate_poly_curve_of_best_fit (
195+ recorded_ods , recorded_voltages , degree = 1
196+ )
197+ calibration = structs .ODCalibration (
198+ created_at = current_utc_datetime (),
199+ calibrated_on_pioreactor_unit = get_unit_name (),
200+ calibration_name = f"od{ angle } -optics-calibration-jig-{ timestamp } " ,
201+ angle = angle ,
202+ curve_data_ = curve_data_ ,
203+ curve_type = "poly" ,
204+ recorded_data = {"x" : recorded_ods , "y" : recorded_voltages },
205+ ir_led_intensity = ir_led_intensity ,
206+ pd_channel = pd_channel ,
207+ )
208+ calibration_links .append (ctx .store_calibration (calibration , f"od{ angle } " ))
209+ else :
210+ for raw_channel , od_reading_struct in od_readings .ods .items ():
211+ pd_channel = cast (pt .PdChannel , raw_channel )
212+ if pd_channel not in channel_angle_map :
213+ continue
214+ angle = channel_angle_map [pd_channel ]
215+ od_value = float (od_reading_struct .od )
216+
217+ recorded_voltages = [0.0 , 1000 * od_value ]
218+ curve_data_ = calibration_utils .calculate_poly_curve_of_best_fit (
219+ recorded_ods , recorded_voltages , degree = 1
220+ )
221+ calibration = structs .ODCalibration (
222+ created_at = current_utc_datetime (),
223+ calibrated_on_pioreactor_unit = get_unit_name (),
224+ calibration_name = f"od{ angle } -optical-reference-standard-{ timestamp } " ,
225+ angle = angle ,
226+ curve_data_ = curve_data_ ,
227+ curve_type = "poly" ,
228+ recorded_data = {"x" : recorded_ods , "y" : recorded_voltages },
229+ ir_led_intensity = ir_led_intensity ,
230+ pd_channel = pd_channel ,
231+ )
232+ calibration_links .append (ctx .store_calibration (calibration , f"od{ angle } " ))
233+
234+ if not calibration_links :
235+ raise ValueError ("No matching channels were recorded for this calibration." )
236+
237+ ctx .complete ({"calibrations" : calibration_links })
238+ return CalibrationComplete ()
239+
240+
241+ _REFERENCE_STANDARD_STEPS : StepRegistry = {
242+ Intro .step_id : Intro ,
243+ RecordReadings .step_id : RecordReadings ,
244+ }
232245
233246
234247def advance_reference_standard_session (
235248 session : CalibrationSession ,
236249 inputs : dict [str , object ],
237250 executor : SessionExecutor | None = None ,
238251) -> CalibrationSession :
239- engine = SessionEngine (flow = reference_standard_flow , session = session , mode = "ui" , executor = executor )
240- engine .advance (inputs )
241- return engine .session
252+ return advance_session (_REFERENCE_STANDARD_STEPS , session , inputs , executor )
242253
243254
244255def get_reference_standard_step (
245256 session : CalibrationSession , executor : SessionExecutor | None = None
246257) -> CalibrationStep | None :
247- engine = SessionEngine (flow = reference_standard_flow , session = session , mode = "ui" , executor = executor )
248- return engine .get_step ()
258+ return get_session_step (_REFERENCE_STANDARD_STEPS , session , executor )
249259
250260
251261class ODReferenceStandardProtocol (CalibrationProtocol [pt .ODCalibrationDevices ]):
252262 target_device = pt .OD_DEVICES
253263 protocol_name = "od_reference_standard"
254264 description = "Calibrate OD using the Pioreactor Optical Reference Standard."
265+ step_registry : ClassVar [StepRegistry ] = _REFERENCE_STANDARD_STEPS
266+
267+ @classmethod
268+ def start_session (cls , target_device : pt .ODCalibrationDevices ) -> CalibrationSession :
269+ return start_reference_standard_session (target_device )
255270
256271 def run ( # type: ignore
257272 self , target_device : pt .ODCalibrationDevices , * args , ** kwargs
258273 ) -> list [structs .ODCalibration ]:
259274 session = start_reference_standard_session (target_device )
260- return run_session_in_cli (reference_standard_flow , session )
275+ return run_session_in_cli (_REFERENCE_STANDARD_STEPS , session )
0 commit comments