Skip to content

Commit 2fdaffe

Browse files
abstract out states, this looks much cleaner
1 parent 5a6ad77 commit 2fdaffe

File tree

7 files changed

+53
-38
lines changed

7 files changed

+53
-38
lines changed

core/pioreactor/background_jobs/base.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from pioreactor.pubsub import Client
2525
from pioreactor.pubsub import create_client
2626
from pioreactor.pubsub import QOS
27-
from pioreactor.state import JobState as states
27+
from pioreactor.states import JobState as st
2828
from pioreactor.utils import append_signal_handlers
2929
from pioreactor.utils import get_running_pio_job_id
3030
from pioreactor.utils import is_pio_job_running
@@ -115,7 +115,7 @@ class _BackgroundJob(metaclass=PostInitCaller):
115115
State management & hooks
116116
---------------------------
117117
118-
So this class controls most of the state convention that we follow (states inspired by Homie):
118+
So this class controls most of the state convention that we follow (st inspired by Homie):
119119
120120
┌──────────┐
121121
│ │
@@ -139,7 +139,7 @@ class _BackgroundJob(metaclass=PostInitCaller):
139139
140140
https://asciiflow.com/#/share/eJzNVEsKgzAQvYrM2lW7Uc%2BSjehQAmksmoIi3qJ4kC7F0%2FQkTS0tRE0crYuGWWSEeZ95wRpkfEaI5FUIH0RcYQ4R1AxKBlEYhD6DSt8OwVHfFJZKNww8wnnc%2BsViTBKhjOYzRqHYXm2nKURWqBdT2xFsGDrnDYytzLsioEzVjr5sQ7b2GoOyNWOGWCbn2pzewizaR0bmyCab%2BAJyyRVJ0PBSvBzjtFphQE%2BlvEgyKTFRmBrU%2B3rZIzXL%2B3Kn1t4dqXn2M6Cafqbu%2FxxicYHUSBZpHC0VoBCIFy5PP%2F9TV%2Bgkb87BBg00T7Hk%2FaY%3D)
141141
142-
states-mermaid-diagram
142+
st-mermaid-diagram
143143
init --> ready
144144
init --> lost
145145
ready --> lost
@@ -236,11 +236,11 @@ class _BackgroundJob(metaclass=PostInitCaller):
236236
"""
237237

238238
# Homie lifecycle (normally per device (i.e. an rpi) but we are using it for "nodes", in Homie parlance)
239-
INIT: pt.JobState = states.INIT
240-
READY: pt.JobState = states.READY
241-
DISCONNECTED: pt.JobState = states.DISCONNECTED
242-
SLEEPING: pt.JobState = states.SLEEPING
243-
LOST: pt.JobState = states.LOST
239+
INIT: pt.JobState = st.INIT
240+
READY: pt.JobState = st.READY
241+
DISCONNECTED: pt.JobState = st.DISCONNECTED
242+
SLEEPING: pt.JobState = st.SLEEPING
243+
LOST: pt.JobState = st.LOST
244244

245245
# initial state is disconnected, set other metadata
246246
state = DISCONNECTED
@@ -318,7 +318,7 @@ def __init__(self, unit: pt.Unit, experiment: pt.Experiment, source: str = "app"
318318
}
319319

320320
# this comes _after_ adding state to published settings
321-
self.set_state(states.INIT)
321+
self.set_state(st.INIT)
322322

323323
self._set_up_exit_protocol()
324324
self._blocking_event = threading.Event()
@@ -358,7 +358,7 @@ def __post__init__(self) -> None:
358358
"""
359359
# setting READY should happen after we write to the job manager, since a job might do a long-running
360360
# task in on_ready, which delays writing to the db, which means `pio kill` might not see it.
361-
self.set_state(states.READY)
361+
self.set_state(st.READY)
362362

363363
@property
364364
def job_key(self):
@@ -488,7 +488,7 @@ def _callback(client, userdata, message: pt.MQTTMessage) -> t.Optional[T]:
488488

489489
def set_state(self, new_state: pt.JobState) -> None:
490490
"""
491-
The preferred way to change states is to use this function (instead of self.state = state). Note:
491+
The preferred way to change st is to use this function (instead of self.state = state). Note:
492492
493493
- no-op if in the same state
494494
- will call the transition callback
@@ -551,8 +551,8 @@ def clean_up(self) -> None:
551551
"""
552552
Disconnect from brokers, set state to "disconnected", stop any activity.
553553
"""
554-
if self.state is not states.DISCONNECTED:
555-
self.set_state(states.DISCONNECTED)
554+
if self.state is not st.DISCONNECTED:
555+
self.set_state(st.DISCONNECTED)
556556
self._clean_up_resources()
557557

558558
def add_to_published_settings(self, setting: str, props: pt.PublishableSetting) -> None:
@@ -745,7 +745,7 @@ def exit_gracefully(reason: int | str, *args) -> None:
745745
pass
746746

747747
def init(self) -> None:
748-
self.state = states.INIT
748+
self.state = st.INIT
749749

750750
try:
751751
# we delay the specific on_init until after we have done our important protocols.
@@ -759,7 +759,7 @@ def init(self) -> None:
759759
self._log_state(self.state)
760760

761761
def ready(self) -> None:
762-
self.state = states.READY
762+
self.state = st.READY
763763

764764
try:
765765
self.on_ready()
@@ -771,7 +771,7 @@ def ready(self) -> None:
771771
self._log_state(self.state)
772772

773773
def sleeping(self) -> None:
774-
self.state = states.SLEEPING
774+
self.state = st.SLEEPING
775775

776776
try:
777777
self.on_sleeping()
@@ -788,7 +788,7 @@ def lost(self) -> None:
788788
# 1. Monitor can send a lost signal if `check_against_processes_running` triggers.
789789
# I think it makes sense to ignore it?
790790

791-
self.state = states.LOST
791+
self.state = st.LOST
792792
self._log_state(self.state)
793793

794794
def disconnected(self) -> None:
@@ -805,7 +805,7 @@ def disconnected(self) -> None:
805805
self.logger.debug("Error in on_disconnected:")
806806
self.logger.debug(e, exc_info=True)
807807

808-
self.state = states.DISCONNECTED
808+
self.state = st.DISCONNECTED
809809
self._log_state(self.state)
810810

811811
# we "set" the internal event, which will cause any event.waits to end blocking. This should happen last.
@@ -863,7 +863,7 @@ def _publish_defined_settings_to_broker(
863863
self._publish_setting(name)
864864

865865
def _log_state(self, state: pt.JobState) -> None:
866-
if state in {states.READY, states.DISCONNECTED, states.LOST}:
866+
if state in {st.READY, st.DISCONNECTED, st.LOST}:
867867
self.logger.info(state.capitalize() + ".")
868868
else:
869869
self.logger.debug(state.capitalize() + ".")
@@ -1167,9 +1167,9 @@ def _desired_dodging_mode(self, enable_dodging_od: bool, od_state: pt.JobState |
11671167
# enable_dodging_od is true - user wants it on
11681168
if od_state is None:
11691169
return False
1170-
if od_state in {states.READY, states.SLEEPING, states.INIT}:
1170+
if od_state in {st.READY, st.SLEEPING, st.INIT}:
11711171
return True
1172-
if od_state in {states.LOST, states.DISCONNECTED}:
1172+
if od_state in {st.LOST, st.DISCONNECTED}:
11731173
return False
11741174
return False
11751175

@@ -1207,7 +1207,7 @@ def set_currently_dodging_od(self, value: bool):
12071207
def set_enable_dodging_od(self, value: bool):
12081208
"""Turn dodging on/off based on user intent, then align mode with current OD state."""
12091209
self.enable_dodging_od = value
1210-
od_state = states.READY if is_pio_job_running("od_reading") else states.DISCONNECTED
1210+
od_state = st.READY if is_pio_job_running("od_reading") else st.DISCONNECTED
12111211

12121212
desired = self._desired_dodging_mode(self.enable_dodging_od, od_state)
12131213
self.set_currently_dodging_od(desired)

core/pioreactor/background_jobs/od_reading.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
from pioreactor.calibrations import load_active_calibration
6868
from pioreactor.config import config
6969
from pioreactor.pubsub import publish
70+
from pioreactor.states import JobState as st
7071
from pioreactor.utils import adcs as madcs
7172
from pioreactor.utils import argextrema
7273
from pioreactor.utils import local_intermittent_storage
@@ -1283,7 +1284,7 @@ def _log_relative_intensity_of_ir_led(self) -> None:
12831284
}
12841285

12851286
def _unblock_internal_event(self) -> None:
1286-
if self.state != self.READY:
1287+
if self.state is not st.READY:
12871288
return
12881289

12891290
self._set_for_iterating.set()

core/pioreactor/background_jobs/stirring.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from pioreactor.background_jobs.base import BackgroundJobWithDodging
2121
from pioreactor.calibrations import load_active_calibration
2222
from pioreactor.config import config
23+
from pioreactor.states import JobState as st
2324
from pioreactor.utils import clamp
2425
from pioreactor.utils import is_pio_job_running
2526
from pioreactor.utils import JobManager
@@ -481,7 +482,7 @@ def poll(self, poll_for_seconds: float) -> Optional[float]:
481482
timestamp=current_utc_datetime(), measured_rpm=self._measured_rpm
482483
)
483484

484-
if recent_rpm == 0 and self.state == self.READY: # and not is_testing_env():
485+
if recent_rpm == 0 and self.state is st.READY: # and not is_testing_env():
485486
self.logger.warning(
486487
"Stirring RPM is 0 - attempting to restart it automatically. It may be a temporary stall, target RPM may be too low, insufficient power applied to fan, or not reading sensor correctly."
487488
)
@@ -499,13 +500,13 @@ def poll(self, poll_for_seconds: float) -> Optional[float]:
499500
return self._measured_rpm
500501

501502
def update_dc_with_measured_rpm(self, measured_rpm: Optional[float]) -> None:
502-
if measured_rpm is None or self.state != self.READY:
503+
if measured_rpm is None or self.state is not st.READY:
503504
return
504505
self._estimate_duty_cycle += self.pid.update(measured_rpm)
505506
self.set_duty_cycle(self._estimate_duty_cycle)
506507

507508
def poll_and_update_dc(self, poll_for_seconds: Optional[float] = None) -> None:
508-
if self.rpm_calculator is None or self.target_rpm is None or self.state != self.READY:
509+
if self.rpm_calculator is None or self.target_rpm is None or self.state is not st.READY:
509510
return
510511

511512
if poll_for_seconds is None:
@@ -557,7 +558,7 @@ def set_target_rpm(self, value: float) -> None:
557558
self.pid.set_setpoint(self.target_rpm)
558559

559560
def sleep_if_ready(self, seconds):
560-
if self.state == self.READY:
561+
if self.state is st.READY:
561562
sleep(seconds)
562563

563564
def block_until_rpm_is_close_to_target(
@@ -585,7 +586,7 @@ def block_until_rpm_is_close_to_target(
585586

586587
def should_exit() -> bool:
587588
"""Encapsulates exit conditions to simplify the main loop."""
588-
return self.state != self.READY or self.currently_dodging_od
589+
return self.state is not st.READY or self.currently_dodging_od
589590

590591
with paused_timer(self.rpm_check_repeated_timer): # Automatically pause/unpause
591592
assert isinstance(self.target_rpm, float)

core/pioreactor/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import typing as t
66

77
from msgspec import Meta
8-
from pioreactor.state import JobState # noqa: F401
8+
from pioreactor.states import JobState # noqa: F401
99

1010
if t.TYPE_CHECKING:
1111
from pioreactor.pubsub import Client

core/pioreactor/utils/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from pioreactor.pubsub import create_client
3636
from pioreactor.pubsub import patch_into
3737
from pioreactor.pubsub import subscribe_and_callback
38+
from pioreactor.states import JobState as st
3839
from pioreactor.utils.networking import resolve_to_address
3940
from pioreactor.utils.timing import catchtime
4041
from pioreactor.utils.timing import current_utc_timestamp
@@ -163,7 +164,7 @@ def __init__(
163164
self.unit = unit
164165
self.experiment = experiment
165166
self.name = name
166-
self.state = "init"
167+
self.state = st("init")
167168
self.exit_event = Event()
168169
self._source = source
169170
self.is_long_running_job = is_long_running_job
@@ -213,7 +214,7 @@ def __init__(
213214
)
214215
assert self.mqtt_client is not None
215216

216-
self.state = "init"
217+
self.state = st("init")
217218
self.publish_setting("$state", self.state)
218219

219220
self.start_passive_listeners()
@@ -230,13 +231,13 @@ def _on_disconnect(self, *args):
230231
self._exit()
231232

232233
def __enter__(self) -> Self:
233-
self.state = "ready"
234+
self.state = st("ready")
234235
self.publish_setting("$state", self.state)
235236

236237
return self
237238

238239
def __exit__(self, *args) -> None:
239-
self.state = "disconnected"
240+
self.state = st("disconnected")
240241
self._exit()
241242
self.publish_setting("$state", self.state)
242243
if not self._externally_provided_client:

frontend/src/Pioreactors.jsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2353,7 +2353,7 @@ function FlashLEDButton({ unit, disabled }){
23532353
)}
23542354

23552355

2356-
function PioreactorCard({unit, isUnitActive, experiment, config, originalLabel, modelDetails}){
2356+
function PioreactorCard({unit, isUnitActive, experiment, config, originalLabel, modelDetails = {}}){
23572357
const [jobFetchComplete, setJobFetchComplete] = useState(false)
23582358
const [label, setLabel] = useState("")
23592359
const {client, subscribeToTopic } = useMQTT();
@@ -2690,7 +2690,7 @@ function PioreactorCard({unit, isUnitActive, experiment, config, originalLabel,
26902690
)}
26912691

26922692

2693-
function InactiveUnits({ units, config, experiment }){
2693+
function InactiveUnits({ units, config, experiment, availableModels }){
26942694
return (
26952695
<React.Fragment>
26962696
<div style={{display: "flex", justifyContent: "space-between", marginBottom: "10px", marginTop: "15px"}}>
@@ -2700,9 +2700,21 @@ function InactiveUnits({ units, config, experiment }){
27002700
</Box>
27012701
</Typography>
27022702
</div>
2703-
{(units || []).map(unit =>
2704-
<PioreactorCard key={unit.pioreactor_name} isUnitActive={false} unit={unit.pioreactor_name} modelName={unit.model_name} modelVersion={unit.model_version} config={config} experiment={experiment} />
2705-
)}
2703+
{(units || []).map((unit) => {
2704+
const modelDetails = (availableModels || []).find(
2705+
({ model_name, model_version }) => model_name === unit.model_name && model_version === unit.model_version
2706+
);
2707+
return (
2708+
<PioreactorCard
2709+
key={unit.pioreactor_name}
2710+
isUnitActive={false}
2711+
unit={unit.pioreactor_name}
2712+
modelDetails={modelDetails || {}}
2713+
config={config}
2714+
experiment={experiment}
2715+
/>
2716+
);
2717+
})}
27062718
</React.Fragment>
27072719
)}
27082720

0 commit comments

Comments
 (0)