def stop_ir_led(self): if self.fake_data: return ir_channel = config.get("leds", "ir_led") led_intensity(ir_channel, intensity=0, unit=self.unit, experiment=self.experiment)
def on_disconnected(self) -> None: self._latest_settings_ended_at = current_utc_time() self._send_details_to_mqtt() with suppress(AttributeError): self.run_thread.join() for channel in self.edited_channels: led_intensity(channel, 0, unit=self.unit, experiment=self.experiment)
def set_led_intensity(self, channel: LedChannel, intensity: float) -> bool: """ This first checks the lock on the LED channel, and will wait a few seconds for it to clear, and error out if it waits too long. Parameters ------------ Channel: The LED channel to modify. Intensity: float A float between 0-100, inclusive. """ for _ in range(12): success = led_intensity( channel, intensity, unit=self.unit, experiment=self.experiment, pubsub_client=self.pub_client, source_of_event=self.job_name, ) if success: self.edited_channels.add(channel) return True time.sleep(0.1) self.logger.warning( f"Unable to update channel {channel} due to a long lock being on the channel." ) return False
def test_lock_will_prevent_led_from_updating() -> None: channel: LedChannel = "A" unit = get_unit_name() exp = get_latest_experiment_name() assert led_intensity(channels=channel, intensities=20, unit=unit, experiment=exp) with lock_leds_temporarily([channel]): assert not led_intensity( channels=channel, intensities=10, unit=unit, experiment=exp) with local_intermittent_storage("leds") as cache: assert float(cache[channel]) == 20
def on_disconnect(self): self.latest_settings_ended_at = current_time() self._send_details_to_mqtt() try: self.timer_thread.cancel() except AttributeError: pass for job in self.sub_jobs: job.set_state("disconnected") for channel in self.edited_channels: led_intensity(channel, 0, unit=self.unit, experiment=self.experiment) self._clear_mqtt_cache()
def test_error_is_thrown_if_lengths_are_wrong() -> None: unit = get_unit_name() exp = get_latest_experiment_name() with pytest.raises(ValueError): led_intensity(channels=["A", "B"], intensities=[20], unit=unit, experiment=exp) with pytest.raises(ValueError): led_intensity(channels=["A", "B"], intensities=20, unit=unit, experiment=exp) assert led_intensity(channels=["A"], intensities=20, unit=unit, experiment=exp)
def test_local_cache_is_updated() -> None: channel: LedChannel = "B" unit = get_unit_name() exp = get_latest_experiment_name() assert led_intensity(channels=channel, intensities=20, unit=unit, experiment=exp) with local_intermittent_storage("leds") as cache: assert float(cache["B"]) == 20
def start_ir_led(self): ir_channel = config.get("leds", "ir_led") r = led_intensity( ir_channel, intensity=100, source_of_event=self.job_name, unit=self.unit, experiment=self.experiment, ) if not r: raise ValueError( "IR LED could not be started. Stopping OD reading.") time.sleep(0.25) # give LED a moment to get to max value return
def test_ambient_light_interference(logger: Logger, unit: str, experiment: str) -> None: # test ambient light IR interference. With all LEDs off, and the Pioreactor not in a sunny room, we should see near 0 light. adc_reader = ADCReader( channels=ALL_PD_CHANNELS, dynamic_gain=False, initial_gain=16, fake_data=is_testing_env(), ) adc_reader.setup_adc() led_intensity( ALL_LED_CHANNELS, intensities=[0] * len(ALL_LED_CHANNELS), unit=unit, source_of_event="self_test", experiment=experiment, verbose=False, ) readings = adc_reader.take_reading() assert all([readings[pd_channel] < 0.005 for pd_channel in ALL_PD_CHANNELS]), readings
def test_lock_will_prevent_led_from_updating_single_channel_but_not_others_passed_in( ) -> None: unit = get_unit_name() exp = get_latest_experiment_name() assert led_intensity(channels=["A", "B"], intensities=[20, 20], unit=unit, experiment=exp) with local_intermittent_storage("leds") as cache: assert float(cache["B"]) == 20 assert float(cache["A"]) == 20 with lock_leds_temporarily(["A"]): assert not led_intensity(channels=["A", "B"], intensities=[10, 10], unit=unit, experiment=exp) with local_intermittent_storage("leds") as cache: assert float(cache["B"]) == 10 assert float(cache["A"]) == 20
def test_all_positive_correlations_between_pds_and_leds( logger: Logger, unit: str, experiment: str ) -> None: """ This tests that there is a positive correlation between the IR LED channel, and the photodiodes as defined in the config.ini. """ from pprint import pformat INTENSITIES = list( range(10, 50, 5) ) # better to err on the side of MORE samples than less - it's only a few extra seconds... current_experiment_name = get_latest_experiment_name() results: dict[tuple[LedChannel, PdChannel], float] = {} adc_reader = ADCReader( channels=ALL_PD_CHANNELS, dynamic_gain=False, initial_gain=16, # I think a small gain is okay, since we only varying the lower-end of LED intensity fake_data=is_testing_env(), ).setup_adc() # set all to 0, but use original experiment name, since we indeed are setting them to 0. led_intensity( ALL_LED_CHANNELS, intensities=[0] * len(ALL_LED_CHANNELS), unit=unit, source_of_event="self_test", experiment=current_experiment_name, verbose=False, ) for led_channel in ALL_LED_CHANNELS: varying_intensity_results: dict[PdChannel, list[float]] = { pd_channel: [] for pd_channel in ALL_PD_CHANNELS } for intensity in INTENSITIES: # turn on the LED to set intensity led_intensity( led_channel, intensities=intensity, unit=unit, experiment=current_experiment_name, verbose=False, source_of_event="self_test", ) # record from ADC, we'll average them readings1 = adc_reader.take_reading() readings2 = adc_reader.take_reading() # Add to accumulating list for pd_channel in ALL_PD_CHANNELS: varying_intensity_results[pd_channel].append( 0.5 * (readings1[pd_channel] + readings2[pd_channel]) ) # compute the linear correlation between the intensities and observed PD measurements for pd_channel in ALL_PD_CHANNELS: results[(led_channel, pd_channel)] = round( correlation(INTENSITIES, varying_intensity_results[pd_channel]), 2 ) # set back to 0 led_intensity( led_channel, intensities=0, unit=unit, experiment=current_experiment_name, verbose=False, source_of_event="self_test", ) logger.debug(f"Correlations between LEDs and PD:\n{pformat(results)}") detected_relationships = [] for (led_channel, pd_channel), measured_correlation in results.items(): if measured_correlation > 0.925: detected_relationships.append( ( config["leds"].get(led_channel, fallback=led_channel), config["od_config.photodiode_channel"].get( pd_channel, fallback=pd_channel ), ) ) publish( f"pioreactor/{unit}/{experiment}/self_test/correlations_between_pds_and_leds", dumps(detected_relationships), retain=True, ) # we require that the IR photodiodes defined in the config have a # correlation with the IR led pd_channels_to_test: list[PdChannel] = [] for (channel, angle_or_ref) in config["od_config.photodiode_channel"].items(): if angle_or_ref != "": channel = cast(PdChannel, channel) pd_channels_to_test.append(channel) ir_led_channel = config["leds_reverse"][IR_keyword] for ir_pd_channel in pd_channels_to_test: assert ( results[(ir_led_channel, ir_pd_channel)] > 0.925 ), f"missing {ir_led_channel} ⇝ {ir_pd_channel}"
def set_led_intensity(self, channel, intensity): self.edited_channels.append(channel) led_intensity(channel, intensity, unit=self.unit, experiment=self.experiment)