Skip to content

Commit 60070aa

Browse files
more model stuff
1 parent d3cef8a commit 60070aa

File tree

12 files changed

+176
-191
lines changed

12 files changed

+176
-191
lines changed

core/pioreactor/automations/temperature/thermostat.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
from pioreactor.background_jobs.temperature_automation import classproperty
66
from pioreactor.background_jobs.temperature_automation import TemperatureAutomationJob
77
from pioreactor.config import config
8-
from pioreactor.models import get_current_model
98
from pioreactor.utils import clamp
109
from pioreactor.utils import is_pio_job_running
1110
from pioreactor.utils.streaming_calculations import PID
11+
from pioreactor.whoami import get_pioreactor_model
1212

1313

1414
class Thermostat(TemperatureAutomationJob):
@@ -92,4 +92,4 @@ def set_target_temperature(self, target_temperature: float | str) -> None:
9292

9393
@classproperty
9494
def MAX_TARGET_TEMP(cls) -> float:
95-
return get_current_model().max_temp_to_reduce_heating
95+
return get_pioreactor_model().max_temp_to_reduce_heating

core/pioreactor/background_jobs/monitor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from pioreactor.hardware import PCB_BUTTON_PIN as BUTTON_PIN
2626
from pioreactor.hardware import PCB_LED_PIN as LED_PIN
2727
from pioreactor.hardware import TEMP
28-
from pioreactor.models import get_current_model
2928
from pioreactor.mureq import HTTPException
3029
from pioreactor.pubsub import get_from
3130
from pioreactor.pubsub import QOS
@@ -37,6 +36,7 @@
3736
from pioreactor.utils.timing import current_utc_timestamp
3837
from pioreactor.utils.timing import RepeatedTimer
3938
from pioreactor.utils.timing import to_datetime
39+
from pioreactor.whoami import get_pioreactor_model
4040

4141
if whoami.is_testing_env():
4242
from pioreactor.utils.mock import MockCallback
@@ -84,7 +84,7 @@ def off(): # functions don't take any arguments, nothing is passed in
8484

8585
@classproperty
8686
def MAX_TEMP_TO_SHUTDOWN(cls) -> float:
87-
return get_current_model().max_temp_to_shutdown
87+
return get_pioreactor_model().max_temp_to_shutdown
8888

8989
@classproperty
9090
def MAX_TEMP_TO_SHUTDOWN_IF_NO_TEMP_AUTOMATION(cls) -> float:

core/pioreactor/background_jobs/temperature_automation.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from pioreactor.automations.base import AutomationJob
1717
from pioreactor.config import config
1818
from pioreactor.logging import create_logger
19-
from pioreactor.models import get_current_model
2019
from pioreactor.structs import Temperature
2120
from pioreactor.utils import clamp
2221
from pioreactor.utils import local_intermittent_storage
@@ -27,6 +26,7 @@
2726
from pioreactor.utils.timing import RepeatedTimer
2827
from pioreactor.utils.timing import to_datetime
2928
from pioreactor.version import rpi_version_info
29+
from pioreactor.whoami import get_pioreactor_model
3030

3131

3232
class classproperty(property):
@@ -35,7 +35,10 @@ def __get__(self, obj, objtype=None):
3535

3636

3737
def is_20ml_v1() -> bool:
38-
return whoami.get_pioreactor_model() == "pioreactor_20ml" and whoami.get_pioreactor_version() == (1, 0)
38+
return (
39+
get_pioreactor_model().model_name == "pioreactor_20ml"
40+
and get_pioreactor_model().model_version == (1, 0)
41+
)
3942

4043

4144
class TemperatureAutomationJob(AutomationJob):
@@ -56,15 +59,15 @@ def INFERENCE_SAMPLES_EVERY_T_SECONDS(cls) -> float:
5659

5760
@classproperty
5861
def MAX_TEMP_TO_REDUCE_HEATING(cls) -> float:
59-
return get_current_model().max_temp_to_reduce_heating
62+
return get_pioreactor_model().max_temp_to_reduce_heating
6063

6164
@classproperty
6265
def MAX_TEMP_TO_DISABLE_HEATING(cls) -> float:
63-
return get_current_model().max_temp_to_disable_heating
66+
return get_pioreactor_model().max_temp_to_disable_heating
6467

6568
@classproperty
6669
def MAX_TEMP_TO_SHUTDOWN(cls) -> float:
67-
return get_current_model().max_temp_to_shutdown
70+
return get_pioreactor_model().max_temp_to_shutdown
6871

6972
@classproperty
7073
def INFERENCE_N_SAMPLES(cls) -> int:
@@ -378,13 +381,14 @@ def infer_temperature(self) -> None:
378381
features["time_series_of_temp"] = time_series_of_temp
379382
self.logger.debug(f"{features=}")
380383

384+
hardware_model = get_pioreactor_model()
381385
try:
382-
if whoami.get_pioreactor_model() == "pioreactor_20ml":
383-
if whoami.get_pioreactor_version() == (1, 0):
386+
if hardware_model.model_name == "pioreactor_20ml":
387+
if hardware_model.model_version == "1.0":
384388
inferred_temperature = self.approximate_temperature_20_1_0(features)
385-
elif whoami.get_pioreactor_version() >= (1, 1):
389+
elif hardware_model.model_version >= "1.1":
386390
inferred_temperature = self.approximate_temperature_20_2_0(features)
387-
elif whoami.get_pioreactor_model() == "pioreactor_40ml":
391+
elif hardware_model.model_name == "pioreactor_40ml":
388392
inferred_temperature = self.approximate_temperature_20_2_0(features) # TODO: change me back
389393
else:
390394
raise ValueError("Unknown Pioreactor model.")

core/pioreactor/cli/pio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,12 +325,12 @@ def version(verbose: bool) -> None:
325325
from pioreactor.version import serial_number
326326
from pioreactor.version import get_firmware_version
327327
from pioreactor.version import rpi_version_info
328-
from pioreactor.whoami import get_pioreactor_model_and_version
328+
from pioreactor.whoami import get_pioreactor_model
329329

330330
click.echo(f"Pioreactor app: {tuple_to_text(software_version_info)}")
331331
click.echo(f"Pioreactor HAT: {tuple_to_text(hardware_version_info)}")
332332
click.echo(f"Pioreactor firmware: {tuple_to_text(get_firmware_version())}")
333-
click.echo(f"Model name: {get_pioreactor_model_and_version()}")
333+
click.echo(f"Model name: {get_pioreactor_model().display_name}")
334334
click.echo(f"HAT serial number: {serial_number}")
335335
click.echo(f"Operating system: {platform.platform()}")
336336
click.echo(f"Raspberry Pi: {rpi_version_info}")

core/pioreactor/models.py

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,15 @@
44
"""
55
from __future__ import annotations
66

7-
import logging
7+
import os
88
from pathlib import Path
99

1010
from msgspec import ValidationError
1111
from msgspec.yaml import decode as yaml_decode
12-
from pioreactor.logging import create_logger
1312
from pioreactor.structs import Model
14-
from pioreactor.whoami import get_pioreactor_model
15-
from pioreactor.whoami import get_pioreactor_version
1613
from pioreactor.whoami import is_testing_env
1714

1815

19-
if not is_testing_env():
20-
MODEL_DEFINITIONS_PATH = Path("/home") / "pioreactor" / ".pioreactor" / "models"
21-
else:
22-
MODEL_DEFINITIONS_PATH = Path(".pioreactor") / "models"
23-
24-
2516
## Built-in Pioreactor models with hard-coded defaults
2617
CORE_MODELS = {
2718
("pioreactor_20ml", "1.0"): Model(
@@ -30,7 +21,7 @@
3021
display_name="Pioreactor 20ml, v1.0",
3122
reactor_capacity_ml=20.0,
3223
reactor_max_fill_volume_ml=18.0,
33-
reactor_diameter_mm=20.0,
24+
reactor_diameter_mm=27.0,
3425
max_temp_to_reduce_heating=63.0,
3526
max_temp_to_disable_heating=65.0,
3627
max_temp_to_shutdown=66.0,
@@ -41,18 +32,18 @@
4132
display_name="Pioreactor 20ml, v1.1",
4233
reactor_capacity_ml=20.0,
4334
reactor_max_fill_volume_ml=18.0,
44-
reactor_diameter_mm=20.0,
45-
max_temp_to_reduce_heating=63.0,
46-
max_temp_to_disable_heating=65.0,
47-
max_temp_to_shutdown=66.0,
35+
reactor_diameter_mm=27.0,
36+
max_temp_to_reduce_heating=78.0,
37+
max_temp_to_disable_heating=80.0,
38+
max_temp_to_shutdown=85.0,
4839
),
4940
("pioreactor_40ml", "1.0"): Model(
5041
model_name="pioreactor_40ml",
5142
model_version="1.0",
5243
display_name="Pioreactor 40ml, v1.0",
5344
reactor_capacity_ml=40.0,
5445
reactor_max_fill_volume_ml=38.0,
55-
reactor_diameter_mm=40.0,
46+
reactor_diameter_mm=27.0,
5647
max_temp_to_reduce_heating=78.0,
5748
max_temp_to_disable_heating=80.0,
5849
max_temp_to_shutdown=85.0,
@@ -62,6 +53,12 @@
6253

6354
def load_contrib_model_definitions() -> list[Model]:
6455
"""Load all model definitions from YAML files under MODEL_DEFINITIONS_PATH."""
56+
57+
if not is_testing_env():
58+
MODEL_DEFINITIONS_PATH = Path("/home") / "pioreactor" / ".pioreactor" / "models"
59+
else:
60+
MODEL_DEFINITIONS_PATH = Path(os.environ["DOT_PIOREACTOR"]) / "models"
61+
6562
models: list[Model] = []
6663
if not MODEL_DEFINITIONS_PATH.exists():
6764
return models
@@ -70,6 +67,8 @@ def load_contrib_model_definitions() -> list[Model]:
7067
m = yaml_decode(file.read_bytes(), type=Model)
7168
models.append(m)
7269
except ValidationError as e:
70+
from pioreactor.logging import create_logger
71+
7372
create_logger("models", experiment="$experiment").error(
7473
f"Error loading model definition {file}: {e}"
7574
)
@@ -81,16 +80,3 @@ def load_contrib_model_definitions() -> list[Model]:
8180
**CORE_MODELS,
8281
**{(m.model_name, m.model_version): m for m in load_contrib_model_definitions()},
8382
}
84-
85-
86-
def get_current_model() -> Model:
87-
"""Return the Model struct for this Pioreactor (by env/EERPOM/HARDWARE env).
88-
Falls back to the 20ml v1.0 factory default if unrecognized.
89-
"""
90-
name = get_pioreactor_model()
91-
version = ".".join(map(str, get_pioreactor_version()))
92-
try:
93-
return registered_models[(name, version)]
94-
except KeyError:
95-
logging.getLogger("pioreactor.models").error(f"Unknown Pioreactor model {name} v{version}")
96-
raise ValueError(f"Unknown Pioreactor model {name} v{version}.")

core/pioreactor/structs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,10 @@ class Dataset(JSONPrintedStruct):
381381

382382

383383
class Model(Struct):
384-
"""Defines a Pioreactor model with its physical and automation parameters."""
384+
"""Defines a Pioreactor hardware model with its physical and automation parameters."""
385385

386-
model_name: str
387-
model_version: str
386+
model_name: str # this in combination with model_version should form a unique pair
387+
model_version: str # this in combination with model_name should form a unique pair
388388
display_name: str
389389
# reactor capacity (e.g. 20 or 40 mL)
390390
reactor_capacity_ml: float

core/pioreactor/whoami.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
import time
77
import warnings
88
from functools import cache
9+
from typing import TYPE_CHECKING
910

1011
from pioreactor import mureq
11-
from pioreactor import types as pt
1212
from pioreactor.exc import NotAssignedAnExperimentError
1313
from pioreactor.exc import NoWorkerFoundError
14-
from pioreactor.version import version_text_to_tuple
14+
15+
if TYPE_CHECKING:
16+
from pioreactor import types as pt
17+
from pioreactor.structs import Model
1518

1619

1720
UNIVERSAL_IDENTIFIER = "$broadcast"
@@ -154,18 +157,32 @@ def am_I_a_worker() -> bool:
154157

155158

156159
@cache
157-
def get_pioreactor_version() -> tuple[int, int]:
160+
def get_pioreactor_model() -> Model:
161+
"""Return the Model struct for this Pioreactor (by env/EERPOM/HARDWARE env).
162+
Falls back to the 20ml v1.0 factory default if unrecognized.
163+
"""
164+
from pioreactor.models import registered_models
165+
166+
name = _get_pioreactor_model_name()
167+
version = _get_pioreactor_model_version()
168+
try:
169+
return registered_models[(name, version)]
170+
except KeyError:
171+
raise ValueError(f"Unknown Pioreactor model {name} v{version}.")
172+
173+
174+
def _get_pioreactor_model_version() -> str:
158175
# pioreactor model version
159176
if os.environ.get("MODEL_VERSION"):
160-
return version_text_to_tuple(os.environ["MODEL_VERSION"])
177+
return os.environ["MODEL_VERSION"]
161178

162179
from pioreactor.pubsub import get_from_leader
163180

164181
try:
165182
result = get_from_leader(f"/api/workers/{get_unit_name()}")
166183
result.raise_for_status()
167184
data = result.json()
168-
return version_text_to_tuple(data["model_version"])
185+
return data["model_version"]
169186
except mureq.HTTPErrorStatus as e:
170187
if e.status_code == 404:
171188
raise NoWorkerFoundError(f"Worker {get_unit_name()} is not present in leader's inventory")
@@ -175,8 +192,7 @@ def get_pioreactor_version() -> tuple[int, int]:
175192
raise e
176193

177194

178-
@cache
179-
def get_pioreactor_model() -> str:
195+
def _get_pioreactor_model_name() -> str:
180196
# pioreactor model name
181197
if os.environ.get("MODEL_NAME"):
182198
return os.environ["MODEL_NAME"]
@@ -199,20 +215,6 @@ def get_pioreactor_model() -> str:
199215
raise e
200216

201217

202-
@cache
203-
def get_pioreactor_model_and_version() -> str:
204-
try:
205-
maybe_model = get_pioreactor_model()
206-
except NoWorkerFoundError:
207-
# possibly it's the leader-only?
208-
return ""
209-
210-
if maybe_model:
211-
return f"{maybe_model} v{'.'.join(map(str, get_pioreactor_version()))}"
212-
else:
213-
return ""
214-
215-
216218
@cache
217219
def get_image_git_hash() -> str:
218220
try:

frontend/src/Inventory.jsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,12 @@ function AddNewPioreactor({setWorkers}){
204204
<FormControl required sx={{mt: "15px", ml: "10px", minWidth: "195px"}} variant="outlined" size="small">
205205
<InputLabel>Pioreactor model</InputLabel>
206206
<Select
207-
value={`${model[0]},${model[1]}`}
207+
value={`${model[0]}-${model[1]}`}
208208
onChange={handleModelVersionChange}
209209
label="Pioreactor model"
210210
>
211211
{useAvailableModels().map(({ model_name, model_version, display_name }) => (
212-
<MenuItem key={`${model_name}-${model_version}`} value={`${model_name},${model_version}`}>
212+
<MenuItem key={`${model_name}-${model_version}`} value={`${model_name}-${model_version}`}>
213213
{display_name}
214214
</MenuItem>
215215
))}
@@ -247,15 +247,21 @@ function AddNewPioreactor({setWorkers}){
247247

248248

249249
function WorkerCard({worker, config, leaderVersion}) {
250+
251+
const availableModels = useAvailableModels();
250252
const unit = worker.pioreactor_unit
251253
const isLeader = (config['cluster.topology']?.leader_hostname === unit)
252254
const [activeStatus, setActiveStatus] = React.useState(worker.is_active ? "active" : "inactive")
253255
const [model, setModel] = React.useState([worker.model_name, worker.model_version])
254-
const availableModels = useAvailableModels();
255-
const currentModelDisplayName =
256+
257+
const currentModelDetails =
256258
availableModels.find(
257259
({model_name, model_version}) => model_name === model[0] && model_version === model[1]
258-
)?.display_name || '';
260+
);
261+
const currentModelDisplayName = currentModelDetails ? currentModelDetails.display_name : "Unknown Model";
262+
const currentModelCapacity = currentModelDetails ? currentModelDetails.reactor_capacity_ml : "";
263+
264+
259265
const [experimentAssigned, setExperimentAssigned] = React.useState(null)
260266
const {client, subscribeToTopic} = useMQTT();
261267
const [state, setState] = React.useState(null)
@@ -424,7 +430,7 @@ function WorkerCard({worker, config, leaderVersion}) {
424430
<div style={{display: "flex", justifyContent: "space-between"}}>
425431

426432
<div style={{display: "flex", justifyContent: "left"}}>
427-
<PioreactorIconWithModel badgeContent={model[0] === "pioreactor_40ml" ? "40" : "20"} />
433+
<PioreactorIconWithModel badgeContent={currentModelCapacity} />
428434
<Typography sx={{
429435
fontSize: 20,
430436
color: "rgba(0, 0, 0, 0.87)",

0 commit comments

Comments
 (0)