From 877932c6f854334c282ff4a577888edc1c377343 Mon Sep 17 00:00:00 2001 From: CamDavidsonPilon Date: Tue, 13 Aug 2024 23:01:48 -0400 Subject: [PATCH] support new od config; sync-configs should apply to leader-only pioreactors too --- CHANGELOG.md | 32 +++++++++++++++++++++++- pioreactor/background_jobs/od_reading.py | 11 +++++--- pioreactor/cli/pios.py | 8 +++--- pioreactor/tests/test_led_intensity.py | 27 ++++++++++++++------ update_scripts/upcoming/update.sh | 3 +-- 5 files changed, 62 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd48b2fb..bac26e64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,39 @@ ### Upcoming +#### Enhancements + + - introduce a new od_reading config,`turn_off_leds_during_reading`, which enables / disables turning off the other LEDS during an OD snapshot. By default, it is set to 1 (enables). + - leader-only Pioreactors also have a `config_hostname.local` file now. + - a new top-level section in experiment profiles, `inputs`, allows you to define variables that can be used in expressions. This is useful if you are copy the same constant over an over again, and want a quick way to change it once. Example: + + ``` + inputs: + growth_phase_temp: 37.0 + stationary_phase_temp: 30.0 + od_threshold: 1.6 + + common: + jobs: + temperature_automation: + actions: + ... + - type: update + hours_elapsed: 12.0 + if: ${{ ::od_reading:od1.od < od_threshold }} + options: + target_temperature: ${{ stationary_phase_temp }} + - type: update + hours_elapsed: 12.0 + if: ${{ ::od_reading:od1.od >= od_threshold }} + options: + target_temperature: ${{ growth_phase_temp }} + + ``` + #### Bug fixes - more resilience to "UI state" diverging from "bioreactor state". Often, this occurred when two jobs stared almost immediately (often a networking issue), and the last job would halt since it couldn't get the required resources, however any MQTT data would be overwritten by the last job. Now, multiple places in the request pipeline will reduce duplication and prevent two jobs from starting too close to each other. - - Improved stirring clean up when stopped in quick succession after starting. + - improved stirring clean up when stopped in quick succession after starting. - if a network isn't found, the `monitor` job will not stall, but warn and continue. #### Breaking changes diff --git a/pioreactor/background_jobs/od_reading.py b/pioreactor/background_jobs/od_reading.py index 4248e9ba..896c45a7 100644 --- a/pioreactor/background_jobs/od_reading.py +++ b/pioreactor/background_jobs/od_reading.py @@ -1018,10 +1018,13 @@ def add_post_read_callback(cls, function: Callable): @property def ir_led_on_and_rest_off_state(self) -> dict[pt.LedChannel, pt.LedIntensityValue]: - return { - channel: (self.ir_led_intensity if channel == self.ir_channel else 0.0) - for channel in led_utils.ALL_LED_CHANNELS - } + if config.getboolean("od_reading.config", "turn_off_leds_during_reading", fallback=True): + return { + channel: (self.ir_led_intensity if channel == self.ir_channel else 0.0) + for channel in led_utils.ALL_LED_CHANNELS + } + else: + return {self.ir_channel: self.ir_led_intensity} def record_from_adc(self) -> structs.ODReadings: """ diff --git a/pioreactor/cli/pios.py b/pioreactor/cli/pios.py index 37f223f9..c576dc67 100644 --- a/pioreactor/cli/pios.py +++ b/pioreactor/cli/pios.py @@ -395,7 +395,7 @@ def _thread_function(unit: str): multiple=True, default=(UNIVERSAL_IDENTIFIER,), type=click.STRING, - help="specify a hostname, default is all active units", + help="specify a hostname, default is all units", ) @click.option( "--shared", @@ -405,7 +405,7 @@ def _thread_function(unit: str): @click.option( "--specific", is_flag=True, - help="sync the worker specific config.ini(s)", + help="sync the specific config.ini(s)", ) @click.option( "--skip-save", @@ -415,14 +415,14 @@ def _thread_function(unit: str): @click.option("-y", is_flag=True, help="(does nothing currently)") def sync_configs(units: tuple[str, ...], shared: bool, specific: bool, skip_save: bool, y: bool) -> None: """ - Deploys the shared config.ini and worker specific config.inis to the workers. + Deploys the shared config.ini and specific config.inis to the pioreactor units. If neither `--shared` not `--specific` are specified, both are set to true. """ from sh import ErrorReturnCode_12 # type: ignore logger = create_logger("sync_configs", unit=get_unit_name(), experiment=UNIVERSAL_EXPERIMENT) - units = universal_identifier_to_all_workers(units) + units = add_leader(universal_identifier_to_all_workers(units)) if not shared and not specific: shared = specific = True diff --git a/pioreactor/tests/test_led_intensity.py b/pioreactor/tests/test_led_intensity.py index 4f7a7167..e95637af 100644 --- a/pioreactor/tests/test_led_intensity.py +++ b/pioreactor/tests/test_led_intensity.py @@ -46,28 +46,39 @@ def test_change_leds_intensities_temporarily() -> None: unit = get_unit_name() exp = "test_change_leds_intensities_temporarily" - led_intensity({"A": 20, "B": 20}, unit=unit, experiment=exp) + original_settings: dict[LedChannel, float] = {"A": 20, "B": 20, "D": 1.0, "C": 0.0} + led_intensity(original_settings, unit=unit, experiment=exp) with change_leds_intensities_temporarily({"A": 10}, unit=unit, experiment=exp): with local_intermittent_storage("leds") as cache: assert float(cache["A"]) == 10 - assert float(cache["B"]) == 20 + assert float(cache["B"]) == original_settings["B"] with local_intermittent_storage("leds") as cache: - assert float(cache["A"]) == 20 - assert float(cache["B"]) == 20 + assert float(cache["A"]) == original_settings["A"] + assert float(cache["B"]) == original_settings["B"] + assert float(cache["C"]) == original_settings["C"] + assert float(cache["D"]) == original_settings["D"] with change_leds_intensities_temporarily({"A": 10, "C": 10, "D": 0}, unit=unit, experiment=exp): with local_intermittent_storage("leds") as cache: assert float(cache["A"]) == 10 - assert float(cache["B"]) == 20 + assert float(cache["B"]) == original_settings["B"] assert float(cache["C"]) == 10 assert float(cache["D"]) == 0 with local_intermittent_storage("leds") as cache: - assert float(cache["A"]) == 20 - assert float(cache["C"]) == 0 - assert float(cache["D"]) == 0 + assert float(cache["A"]) == original_settings["A"] + assert float(cache["B"]) == original_settings["B"] + assert float(cache["C"]) == original_settings["C"] + assert float(cache["D"]) == original_settings["D"] + + with change_leds_intensities_temporarily({}, unit=unit, experiment=exp): + with local_intermittent_storage("leds") as cache: + assert float(cache["A"]) == original_settings["A"] + assert float(cache["B"]) == original_settings["B"] + assert float(cache["C"]) == original_settings["C"] + assert float(cache["D"]) == original_settings["D"] def test_local_cache_is_updated() -> None: diff --git a/update_scripts/upcoming/update.sh b/update_scripts/upcoming/update.sh index bd37d20b..77e72c64 100644 --- a/update_scripts/upcoming/update.sh +++ b/update_scripts/upcoming/update.sh @@ -8,8 +8,6 @@ export LC_ALL=C PIO_DIR=/home/pioreactor/.pioreactor -# all pioreactors get a unit_config, include leader-only pioworekrs -touch $PIO_DIR/unit_config.ini HOSTNAME=$(hostname) @@ -18,6 +16,7 @@ HOSTNAME=$(hostname) LEADER_HOSTNAME=$(crudini --get $PIO_DIR/config.ini cluster.topology leader_hostname) if [ "$HOSTNAME" = "$LEADER_HOSTNAME" ]; then + touch "$PIO_DIR/config_$HOSTNAME.ini" # create if it doesn't exist. # stirring -> stirring.config # Iterate over each ini file in the directory