Skip to content

Commit c83a3b9

Browse files
docs
1 parent 7668ab2 commit c83a3b9

File tree

6 files changed

+152
-82
lines changed

6 files changed

+152
-82
lines changed

core/pioreactor/calibrations/protocols/README.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

core/pioreactor/calibrations/registry.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Device = TypeVar("Device", bound=str)
1313
ProtocolName = str
1414

15+
# Registry of device -> protocol name -> protocol class (populated via CalibrationProtocol subclasses).
1516
calibration_protocols: dict[str, dict[ProtocolName, type["CalibrationProtocol[Any]"]]] = defaultdict(dict)
1617

1718

core/pioreactor/calibrations/session_flow.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def advance(self, ctx: "SessionContext") -> "StepLike | None":
3636

3737

3838
StepLike = str | SessionStep | type[SessionStep]
39+
# Registry of step_id -> SessionStep class for a protocol's session flow.
3940
StepRegistry = dict[str, type[SessionStep]]
4041

4142

core/pioreactor/web/tasks.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
# -*- coding: utf-8 -*-
2+
"""
3+
Huey background tasks used by the web API, including calibration actions invoked
4+
from `core/pioreactor/web/unit_calibration_sessions_api.py` via the session executor.
5+
This module also hosts the calibration action registry that maps action names
6+
to Huey tasks so unit API handlers can dispatch without hardcoded strings.
7+
"""
8+
# -*- coding: utf-8 -*-
29
from __future__ import annotations
310

411
import configparser
@@ -10,6 +17,7 @@
1017
import shutil
1118
import stat
1219
import zipfile
20+
from collections.abc import Callable
1321
from pathlib import Path
1422
from shlex import join
1523
from subprocess import check_call
@@ -39,6 +47,30 @@
3947
from pioreactor.web.config import huey
4048
from pioreactor.whoami import get_unit_name
4149

50+
CalibrationActionHandler = tuple[
51+
Any,
52+
int,
53+
str,
54+
Callable[[Any], dict[str, Any]],
55+
]
56+
57+
# Registry of calibration action -> handler that returns a Huey task, timeout, label, and normalizer.
58+
calibration_actions: dict[str, Callable[[dict[str, Any]], CalibrationActionHandler]] = {}
59+
60+
61+
def register_calibration_action(
62+
action: str,
63+
handler: Callable[[dict[str, Any]], CalibrationActionHandler],
64+
) -> None:
65+
calibration_actions[action] = handler
66+
67+
68+
def get_calibration_action(action: str) -> Callable[[dict[str, Any]], CalibrationActionHandler]:
69+
handler = calibration_actions.get(action)
70+
if handler is None:
71+
raise ValueError(f"Unknown calibration action: {action}")
72+
return handler
73+
4274

4375
logger = create_logger(
4476
"background_tasks",
@@ -430,6 +462,104 @@ def calibration_read_voltage() -> float:
430462
return float(voltage_in_aux())
431463

432464

465+
def _default_normalizer(result: Any) -> dict[str, Any]:
466+
if isinstance(result, dict):
467+
return result
468+
return {}
469+
470+
471+
def _voltage_normalizer(result: Any) -> dict[str, Any]:
472+
return {"voltage": float(result)}
473+
474+
475+
def _voltages_normalizer(result: Any) -> dict[str, Any]:
476+
return {"voltages": result}
477+
478+
479+
def _od_readings_normalizer(result: Any) -> dict[str, Any]:
480+
return {"od_readings": result}
481+
482+
483+
def _stirring_normalizer(result: Any) -> dict[str, Any]:
484+
if isinstance(result, dict):
485+
return result
486+
return {}
487+
488+
489+
def _register_core_calibration_actions() -> None:
490+
register_calibration_action(
491+
"pump",
492+
lambda payload: (
493+
calibration_execute_pump(
494+
payload["pump_device"],
495+
float(payload["duration_s"]),
496+
float(payload["hz"]),
497+
float(payload["dc"]),
498+
),
499+
300,
500+
"Pump action",
501+
_default_normalizer,
502+
),
503+
)
504+
register_calibration_action(
505+
"od_standards_measure",
506+
lambda payload: (
507+
calibration_measure_standard(
508+
float(payload["rpm"]),
509+
payload["channel_angle_map"],
510+
),
511+
300,
512+
"OD measurement",
513+
_voltages_normalizer,
514+
),
515+
)
516+
register_calibration_action(
517+
"od_reference_standard_read",
518+
lambda payload: (
519+
calibration_reference_standard_read(float(payload["ir_led_intensity"])),
520+
300,
521+
"Reference standard reading",
522+
_od_readings_normalizer,
523+
),
524+
)
525+
register_calibration_action(
526+
"stirring_calibration",
527+
lambda payload: (
528+
calibration_run_stirring(
529+
float(payload["min_dc"]) if (payload.get("min_dc") is not None) else None,
530+
float(payload["max_dc"]) if (payload.get("max_dc") is not None) else None,
531+
),
532+
300,
533+
"Stirring calibration",
534+
_stirring_normalizer,
535+
),
536+
)
537+
register_calibration_action(
538+
"read_voltage",
539+
lambda payload: (
540+
calibration_read_voltage(),
541+
30,
542+
"Voltage read",
543+
_voltage_normalizer,
544+
),
545+
)
546+
register_calibration_action(
547+
"save_calibration",
548+
lambda payload: (
549+
calibration_save_calibration(
550+
payload["device"],
551+
payload["calibration"],
552+
),
553+
300,
554+
"Saving calibration",
555+
_default_normalizer,
556+
),
557+
)
558+
559+
560+
_register_core_calibration_actions()
561+
562+
433563
@huey.task()
434564
@huey.lock_task("export-data-lock")
435565
def export_experiment_data_task(

core/pioreactor/web/unit_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@
3636
from pioreactor.web.app import publish_to_error_log
3737
from pioreactor.web.app import publish_to_log
3838
from pioreactor.web.app import query_temp_local_metadata_db
39-
from pioreactor.web.calibration_sessions_api import register_calibration_session_routes
4039
from pioreactor.web.config import huey
4140
from pioreactor.web.plugin_registry import registered_unit_api_routes
41+
from pioreactor.web.unit_calibration_sessions_api import register_calibration_session_routes
4242
from pioreactor.web.utils import abort_with
4343
from pioreactor.web.utils import attach_cache_control
4444
from pioreactor.web.utils import create_task_response

core/pioreactor/web/calibration_sessions_api.py renamed to core/pioreactor/web/unit_calibration_sessions_api.py

Lines changed: 19 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
# -*- coding: utf-8 -*-
2+
"""
3+
Calibration session HTTP API.
4+
5+
Uses step registries from calibration protocols and dispatches hardware actions
6+
through Huey tasks defined in `core/pioreactor/web/tasks.py`.
7+
The unit API routes here for session start/advance/abort and invokes the
8+
calibration action executor to perform privileged hardware work.
9+
"""
10+
# -*- coding: utf-8 -*-
211
from __future__ import annotations
312

413
from typing import Any
@@ -17,88 +26,20 @@
1726
from pioreactor.calibrations.structured_session import load_calibration_session
1827
from pioreactor.calibrations.structured_session import save_calibration_session
1928
from pioreactor.calibrations.structured_session import utc_iso_timestamp
20-
from pioreactor.web import tasks
29+
from pioreactor.web.tasks import get_calibration_action
2130
from pioreactor.web.utils import abort_with
2231

2332

2433
def _execute_calibration_action(action: str, payload: dict[str, Any]) -> dict[str, Any]:
25-
if action == "pump":
26-
task = tasks.calibration_execute_pump(
27-
payload["pump_device"],
28-
float(payload["duration_s"]),
29-
float(payload["hz"]),
30-
float(payload["dc"]),
31-
)
32-
try:
33-
success = task(blocking=True, timeout=300)
34-
except TaskException as exc:
35-
raise ValueError(f"Pump action failed: {exc}") from exc
36-
except HueyException as exc:
37-
raise ValueError("Pump action timed out.") from exc
38-
if not success:
39-
raise ValueError("Pump action failed.")
40-
return {}
41-
42-
if action == "od_standards_measure":
43-
task = tasks.calibration_measure_standard(
44-
float(payload["rpm"]),
45-
payload["channel_angle_map"],
46-
)
47-
try:
48-
voltages = task(blocking=True, timeout=300)
49-
except TaskException as exc:
50-
raise ValueError(f"OD measurement failed: {exc}") from exc
51-
except HueyException as exc:
52-
raise ValueError("OD measurement timed out.") from exc
53-
return {"voltages": voltages}
54-
55-
if action == "od_reference_standard_read":
56-
task = tasks.calibration_reference_standard_read(float(payload["ir_led_intensity"]))
57-
try:
58-
readings = task(blocking=True, timeout=300)
59-
except TaskException as exc:
60-
raise ValueError(f"Reference standard reading failed: {exc}") from exc
61-
except HueyException as exc:
62-
raise ValueError("Reference standard reading timed out.") from exc
63-
return {"od_readings": readings}
64-
65-
if action == "stirring_calibration":
66-
task = tasks.calibration_run_stirring(
67-
float(payload["min_dc"]) if (payload.get("min_dc") is not None) else None,
68-
float(payload["max_dc"]) if (payload.get("max_dc") is not None) else None,
69-
)
70-
try:
71-
calibration_payload = task(blocking=True, timeout=300)
72-
except TaskException as exc:
73-
raise ValueError(f"Stirring calibration failed: {exc}") from exc
74-
except HueyException as exc:
75-
raise ValueError("Stirring calibration timed out.") from exc
76-
return calibration_payload
77-
78-
if action == "read_voltage":
79-
task = tasks.calibration_read_voltage()
80-
try:
81-
voltage = task(blocking=True, timeout=30)
82-
except TaskException as exc:
83-
raise ValueError(f"Voltage read failed: {exc}") from exc
84-
except HueyException as exc:
85-
raise ValueError("Voltage read timed out.") from exc
86-
return {"voltage": float(voltage)}
87-
88-
if action == "save_calibration":
89-
task = tasks.calibration_save_calibration(
90-
payload["device"],
91-
payload["calibration"],
92-
)
93-
try:
94-
result = task(blocking=True, timeout=300)
95-
except TaskException as exc:
96-
raise ValueError(f"Saving calibration failed: {exc}") from exc
97-
except HueyException as exc:
98-
raise ValueError("Saving calibration timed out.") from exc
99-
return result
100-
101-
raise ValueError("Unknown calibration action.")
34+
handler = get_calibration_action(action)
35+
task, timeout_s, error_label, normalize = handler(payload)
36+
try:
37+
result = task(blocking=True, timeout=timeout_s)
38+
except TaskException as exc:
39+
raise ValueError(f"{error_label} failed: {exc}") from exc
40+
except HueyException as exc:
41+
raise ValueError(f"{error_label} timed out.") from exc
42+
return normalize(result)
10243

10344

10445
def _get_step_registry(protocol) -> Any:

0 commit comments

Comments
 (0)