Exemple #1
0
 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)
Exemple #2
0
    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)
Exemple #3
0
    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
Exemple #4
0
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
Exemple #5
0
    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()
Exemple #6
0
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)
Exemple #7
0
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
Exemple #8
0
 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
Exemple #9
0
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
Exemple #10
0
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
Exemple #11
0
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}"
Exemple #12
0
 def set_led_intensity(self, channel, intensity):
     self.edited_channels.append(channel)
     led_intensity(channel,
                   intensity,
                   unit=self.unit,
                   experiment=self.experiment)