Пример #1
0
    def test_acq_getScalingTable(self):
        """Get the scaling table (correction for mapping vertical px with timestamps)
        for the streak Time Range chosen for one sweep."""

        # test a first value
        self.streakunit.timeRange.value = util.find_closest(
            0.000000002, self.streakunit.timeRange.choices)  # 2ns
        self.streakunit.streakMode.value = True
        # Note: RemoteEx automatically stops and restarts "Live" acq when changing settings

        img = self.readoutcam.data.get()

        self.assertIn(model.MD_TIME_LIST, img.metadata)
        self.assertIsNotNone(img.metadata[model.MD_TIME_LIST])

        # check first value in table is the same order as the conversion factor
        # the quotient should be greater than zero
        firstCorrectedValue = img.metadata[model.MD_TIME_LIST][0]
        conversionFactor = self.streakunit.timeRangeFactor
        self.assertGreater(firstCorrectedValue / conversionFactor, 0)

        # test a second value
        # test a second value)
        self.streakunit.timeRange.value = util.find_closest(
            0.001, self.streakunit.timeRange.choices)  # 1ms

        img = self.readoutcam.data.get()

        self.assertIn(model.MD_TIME_LIST, img.metadata)
        self.assertIsNotNone(img.metadata[model.MD_TIME_LIST])

        # check first value in table is the same order as the conversion factor
        # the quotient should be greater than zero
        firstCorrectedValue = img.metadata[model.MD_TIME_LIST][0]
        conversionFactor = self.streakunit.timeRangeFactor
        self.assertGreater(firstCorrectedValue / conversionFactor, 0)

        # check that scaling correction is not included when image is acquired in Focus mode
        # Note: In case we only acquire images in operate mode, we can skip that test.
        self.streakunit.streakMode.value = False

        img = self.readoutcam.data.get()

        self.assertNotIn(model.MD_TIME_LIST, img.metadata)
        self.assertFalse(img.metadata[model.MD_STREAK_MODE])

        # change again to operate mode
        self.streakunit.streakMode.value = True

        img = self.readoutcam.data.get()

        self.assertIn(model.MD_TIME_LIST, img.metadata)
        self.assertIsNotNone(img.metadata[model.MD_TIME_LIST])

        # check first value in table is the same order as the conversion factor
        # the quotient should be greater than zero
        firstCorrectedValue = img.metadata[model.MD_TIME_LIST][0]
        conversionFactor = self.streakunit.timeRangeFactor
        self.assertGreater(firstCorrectedValue / conversionFactor, 0)
Пример #2
0
    def test_acq_getScalingTable(self):
        """Get the scaling table (correction for mapping vertical px with timestamps)
        for the streak Time Range chosen for one sweep."""

        # test a first value
        self.streakunit.timeRange.value = util.find_closest(0.000000002, self.streakunit.timeRange.choices)  # 2ns
        self.streakunit.streakMode.value = True
        # Note: RemoteEx automatically stops and restarts "Live" acq when changing settings

        img = self.readoutcam.data.get()

        self.assertIn(model.MD_TIME_LIST, img.metadata)
        self.assertIsNotNone(img.metadata[model.MD_TIME_LIST])

        # check first value in table is the same order as the conversion factor
        # the quotient should be greater than zero
        firstCorrectedValue = img.metadata[model.MD_TIME_LIST][0]
        conversionFactor = self.streakunit.timeRangeFactor
        self.assertGreater(firstCorrectedValue/conversionFactor, 0)

        # test a second value
        # test a second value)
        self.streakunit.timeRange.value = util.find_closest(0.001, self.streakunit.timeRange.choices)  # 1ms

        img = self.readoutcam.data.get()

        self.assertIn(model.MD_TIME_LIST, img.metadata)
        self.assertIsNotNone(img.metadata[model.MD_TIME_LIST])

        # check first value in table is the same order as the conversion factor
        # the quotient should be greater than zero
        firstCorrectedValue = img.metadata[model.MD_TIME_LIST][0]
        conversionFactor = self.streakunit.timeRangeFactor
        self.assertGreater(firstCorrectedValue/conversionFactor, 0)

        # check that scaling correction is not included when image is acquired in Focus mode
        # Note: In case we only acquire images in operate mode, we can skip that test.
        self.streakunit.streakMode.value = False

        img = self.readoutcam.data.get()

        self.assertNotIn(model.MD_TIME_LIST, img.metadata)
        self.assertFalse(img.metadata[model.MD_STREAK_MODE])

        # change again to operate mode
        self.streakunit.streakMode.value = True

        img = self.readoutcam.data.get()

        self.assertIn(model.MD_TIME_LIST, img.metadata)
        self.assertIsNotNone(img.metadata[model.MD_TIME_LIST])

        # check first value in table is the same order as the conversion factor
        # the quotient should be greater than zero
        firstCorrectedValue = img.metadata[model.MD_TIME_LIST][0]
        conversionFactor = self.streakunit.timeRangeFactor
        self.assertGreater(firstCorrectedValue/conversionFactor, 0)
Пример #3
0
    def test_acq_Live_RingBuffer_subscribe(self):
        """Acquire single image and receive it via the dataport."""

        # Note: AcqStop can be called multiple times even if the acq is already stopped without causing an error
        # However, AcqStart can be only called once and raises an error if called while an acq is running.
        # AcqStop is not an asynchronous command. But it takes time until the status of the async command
        # "AcqStart" is properly finished.
        # Changing settings is not async. RemoteEx blocks as long as the settings are changed.
        # RemoteEx also stops the "Live" mode if a settings change is requested but does not restarts the "Live" mode.

        self.streakunit.streakMode.value = True
        self.streakunit.timeRange.value = util.find_closest(0.000000002, self.streakunit.timeRange.choices)
        self.streakunit.MCPGain.value = 2
        self.readoutcam.exposureTime.value = 0.1  # 100ms
        time.sleep(1)

        # start Live mode
        # and subscribe to dataflow afterwards in order to request one image while acq in Live mode is already running
        # The acq should be automatically stopped and restarted, otherwise a RemoteEx error will be received.
        # error returned: ['7', 'AcqStart', 'async command pending', 'HAcq_mLive']
        self.streakcam.StartAcquisition(self.readoutcam.acqMode)  # acquire images

        def callback(dataflow, image):
            # self.streakcam.AcqAcqMonitor("Off")  # TODO?
            self.readoutcam.data.unsubscribe(callback)
            # Note: MCPGain set to 0 is handled by stream not by driver except when changing from
            # "Operate" mode to "Focus" mode
            size = self.readoutcam.resolution.value
            self.assertEqual(image.shape, size[::-1])  # invert size
            self.assertIn(model.MD_EXP_TIME, image.metadata)
            logging.debug("Got image.")

        self.readoutcam.data.subscribe(callback)
        time.sleep(5)
Пример #4
0
    def test_acq_wavelengthTable(self):
        """Get the scaling table (correction for mapping vertical px with timestamps)
        for the streak Time Range chosen for one sweep."""

        # test a first value
        self.readoutcam.binning.value = (1, 1)
        self.streakunit.timeRange.value = util.find_closest(0.000000002, self.streakunit.timeRange.choices)  # 2ns
        self.streakunit.streakMode.value = True
        # Note: RemoteEx automatically stops and restarts "Live" acq when changing settings

        img = self.readoutcam.data.get()
        wl_list_bin1 = img.metadata[model.MD_WL_LIST]

        self.assertIn(model.MD_TIME_LIST, img.metadata)
        self.assertIn(model.MD_WL_LIST, img.metadata)

        # check wavelength list changes when changing binning
        self.readoutcam.binning.value = (2, 2)

        img = self.readoutcam.data.get()
        wl_list_bin2 = img.metadata[model.MD_WL_LIST]

        self.assertEqual(len(wl_list_bin1)/2, len(wl_list_bin2))

        with self.assertRaises(AssertionError):
            self.assertListEqual(wl_list_bin1, wl_list_bin2)  # there is not assertListNotEqual...
Пример #5
0
    def _on_em_view_mpp_change(self, mpp):
        """ Set the microscope's hfw when the MicroscopeView's mpp value changes

        The canvas calculates the new hfw value.
        """

        # Only change the fov of the hardware if:
        # * this Viewport was  *not* responsible for setting the mpp
        #   (by calling `self.set_horizontal_field_width`)
        # * _and_ if it's displayed on screen (so we don't interfere with
        #   viewports in other tabs that are not currently displayed)
        # This way, we prevent mpp/fov setting loops.
        if not self.self_set_mpp and self.IsShownOnScreen():
            logging.debug("View mpp changed to %s on %s", mpp, self)
            hfw = self.get_fov_from_mpp()
            if hfw is None:
                return
            fov_va = self.microscope_view.fov_va

            try:
                # TODO: Test with a simulated SEM that has HFW choices
                choices = fov_va.choices
                # Get the choice that matches hfw most closely
                hfw = util.find_closest(hfw, choices)
            except NotApplicableError:
                hfw = fov_va.clip(hfw)

            # Indicate that this object was responsible for updating the hardware's HFW, so it won't
            # get updated again in `_on_hw_fov_change`
            self.self_set_fov = True
            logging.debug("Setting hardware FoV to %s", hfw)
            fov_va.value = hfw
        else:
            self.self_set_mpp = False
Пример #6
0
    def test_acq_wavelengthTable(self):
        """Get the scaling table (correction for mapping vertical px with timestamps)
        for the streak Time Range chosen for one sweep."""

        # test a first value
        self.readoutcam.binning.value = (1, 1)
        self.streakunit.timeRange.value = util.find_closest(
            0.000000002, self.streakunit.timeRange.choices)  # 2ns
        self.streakunit.streakMode.value = True
        # Note: RemoteEx automatically stops and restarts "Live" acq when changing settings

        img1 = self.readoutcam.data.get()
        wl_list_bin1 = img1.metadata[model.MD_WL_LIST]

        self.assertIn(model.MD_TIME_LIST, img1.metadata)
        self.assertIn(model.MD_WL_LIST, img1.metadata)

        # check wavelength list changes when changing binning
        self.readoutcam.binning.value = (2, 2)

        img2 = self.readoutcam.data.get()
        wl_list_bin2 = img2.metadata[model.MD_WL_LIST]

        self.assertEqual(len(wl_list_bin1) / 2, len(wl_list_bin2))

        with self.assertRaises(AssertionError):
            self.assertListEqual(
                wl_list_bin1,
                wl_list_bin2)  # there is not assertListNotEqual...
Пример #7
0
def set_attr(comp_name, attr_val_str):
    """
    set the value of vigilant attribute of the given component.
    attr_val_str (dict str->str): attribute name -> value as a string
    """
    component = get_component(comp_name)

    for attr_name, str_val in attr_val_str.items():
        try:
            attr = getattr(component, attr_name)
        except Exception:
            raise ValueError("Failed to find attribute '%s' on component '%s'" % (attr_name, comp_name))

        if not isinstance(attr, model.VigilantAttributeBase):
            raise ValueError("'%s' is not a vigilant attribute of component %s" % (attr_name, comp_name))

        new_val = convert_to_object(str_val)

        # Special case for floats, due to rounding error, it's very hard to put the
        # exact value if it's an enumerated VA. So just pick the closest one in this
        # case.
        if isinstance(new_val, float) and (
           hasattr(attr, "choices") and isinstance(attr.choices, collections.Iterable)):
            orig_val = new_val
            new_val = util.find_closest(new_val, attr.choices)
            if new_val != orig_val:
                logging.debug("Adjusting value to %s", new_val)

        try:
            attr.value = new_val
        except Exception as exc:
            raise IOError("Failed to set %s.%s = '%s': %s" % (comp_name, attr_name, str_val, exc))
Пример #8
0
    def _onUpdateTriggerDelayMD(self, evt):
        """
        Callback method for trigger delay ctrl GUI element.
        Overwrites the triggerDelay value in the MD after a new value was requested via the GUI.
        """
        evt.Skip()
        cur_timeRange = self.streak_unit.timeRange.value
        requested_triggerDelay = self.ctrl_triggerDelay.GetValue()
        # get a copy of  MD
        trigger2delay_MD = self.streak_delay.getMetadata()[model.MD_TIME_RANGE_TO_DELAY]

        # check if key already exists (prevent creating new key due to floating point issues)
        key = util.find_closest(cur_timeRange, trigger2delay_MD.keys())
        if util.almost_equal(key, cur_timeRange):
            # Replace the current delay value with the requested for an already existing timeRange in the dict.
            # This avoid duplication of keys, which are only different because of floating point issues.
            trigger2delay_MD[key] = requested_triggerDelay
        else:
            trigger2delay_MD[cur_timeRange] = requested_triggerDelay
            logging.warning("A new entry %s was added to MD_TIME_RANGE_TO_DELAY, "
                            "which is not in the device .timeRange choices.", cur_timeRange)

        # check the number of keys in the dict is same as choices for VA
        if len(trigger2delay_MD.keys()) != len(self.streak_unit.timeRange.choices):
            logging.warning("MD_TIME_RANGE_TO_DELAY has %d entries, while the device .timeRange has %d choices.",
                            len(trigger2delay_MD.keys()), len(self.streak_unit.timeRange.choices))

        self.streak_delay.updateMetadata({model.MD_TIME_RANGE_TO_DELAY: trigger2delay_MD})
        # Note: updateMetadata should here never raise an exception as the UnitFloatCtrl already
        # catches errors regarding type and out-of-range inputs

        # update txt displayed in GUI
        self._onUpdateTriggerDelayGUI("Calibration not saved yet", odemis.gui.FG_COLOUR_WARNING)
Пример #9
0
    def test_TimeRange(self):
        """Test time range VA for sweeping of streak unit."""
        for timeRange in self.streakunit.timeRange.choices:
            # change timeRange VA to value in range
            self.streakunit.timeRange.value = util.find_closest(timeRange, self.streakunit.timeRange.choices)
            self.assertAlmostEqual(self.streakunit.timeRange.value, timeRange)

            tr2d = self.delaybox._metadata.get(model.MD_TIME_RANGE_TO_DELAY)
            if tr2d:
                key = util.find_closest(timeRange, tr2d.keys())
                # check that the corresponding trigger delay is set when changing the .timeRange VA
                md_triggerDelay = tr2d[key]
                self.assertAlmostEqual(self.delaybox.triggerDelay.value, md_triggerDelay)

        # request value, which is not in choices of VA
        with self.assertRaises(IndexError):
            self.streakunit.timeRange.value = 0.000004  # 4us
Пример #10
0
    def test_TimeRange(self):
        """Test time range VA for sweeping of streak unit."""
        for timeRange in self.streakunit.timeRange.choices:
            # change timeRange VA to value in range
            self.streakunit.timeRange.value = util.find_closest(timeRange, self.streakunit.timeRange.choices)
            self.assertAlmostEqual(self.streakunit.timeRange.value, timeRange)

            tr2d = self.delaybox._metadata.get(model.MD_TIME_RANGE_TO_DELAY)
            if tr2d:
                key = util.find_closest(timeRange, tr2d.keys())
                # check that the corresponding trigger delay is set when changing the .timeRange VA
                md_triggerDelay = tr2d[key]
                self.assertAlmostEqual(self.delaybox.triggerDelay.value, md_triggerDelay)

        # request value, which is not in choices of VA
        with self.assertRaises(IndexError):
            self.streakunit.timeRange.value = 0.000004  # 4us
Пример #11
0
    def _setCCDFan(self, enable):
        """
        Turn on/off the fan of the CCD
        enable (boolean): True to turn on/restore the fan, and False to turn if off
        """
        if not self._has_fan_speed:
            return

        if self._fan_enabled == enable:
            return
        self._fan_enabled = enable

        comp = self._getComponent("ccd")

        if enable:
            if self._enabled_fan_speed is not None:
                logging.debug("Turning fan on of %s", comp.name)
                comp.fanSpeed.value = max(comp.fanSpeed.value,
                                          self._enabled_fan_speed)
        else:
            if comp.fanSpeed.value == 0:
                # Already off => don't touch it
                self._enabled_fan_speed = None
                self._enabled_fan_temp = None
            else:
                logging.debug("Turning fan off of %s", comp.name)
                self._enabled_fan_speed = comp.fanSpeed.value
                comp.fanSpeed.value = 0

        # Raise targetTemperature to max/ambient to avoid the fan from
        # automatically starting again. (Some hardware have this built-in when
        # the current temperature is too high compared to the target)
        if self._has_fan_temp:
            temp = comp.targetTemperature
            if enable:
                if self._enabled_fan_temp is not None:
                    temp.value = min(comp.targetTemperature.value,
                                     self._enabled_fan_temp)
                    try:
                        self._waitTemperatureReached(comp, timeout=60)
                    except Exception as ex:
                        logging.warning(
                            "Failed to reach target temperature of CCD: %s",
                            ex)
            else:
                # Set ~25°C == ambient temperature
                self._enabled_fan_temp = temp.value
                try:
                    try:
                        temp.value = min(comp.targetTemperature.range[1], 25)
                    except AttributeError:
                        temp.value = util.find_closest(
                            25, comp.targetTemperature.choices)
                except Exception:
                    logging.warning(
                        "Failed to change targetTemperature when disabling fan",
                        exc_info=True)
Пример #12
0
    def _setPower(self, value):
        powers = self.power.choices

        self._power = util.find_closest(value, powers)
        if self._power == 0:
            self.parent._device.HVBeamOff()
        else:
            self.parent._device.HVBeamOn()
        return self._power
Пример #13
0
    def _setPower(self, value):
        powers = self.power.choices

        self._power = util.find_closest(value, powers)
        if self._power == 0:
            self.parent._device.HVBeamOff()
        else:
            self.parent._device.HVBeamOn()
        return self._power
Пример #14
0
    def _doReference(self, axes):
        logging.debug("Referencing axis %s (-> %s)", self._axis, self._caxis)
        f = self._child.reference({self._caxis})
        f.result()

        # If we just did homing and ended up to an unsupported position, move to
        # the nearest supported position
        cp = self._child.position.value[self._caxis]
        if (cp not in self._positions):
            nearest = util.find_closest(cp, self._positions.keys())
            self._doMoveAbs({self._axis: nearest})
Пример #15
0
    def _doReference(self, axes):
        logging.debug("Referencing axis %s (-> %s)", self._axis, self._caxis)
        f = self._child.reference({self._caxis})
        f.result()

        # If we just did homing and ended up to an unsupported position, move to
        # the nearest supported position
        cp = self._child.position.value[self._caxis]
        if (cp not in self._positions):
            nearest = util.find_closest(cp, self._positions.keys())
            self._doMoveAbs({self._axis: nearest})
Пример #16
0
    def _setCCDFan(self, enable):
        """
        Turn on/off the fan of the CCD
        enable (boolean): True to turn on/restore the fan, and False to turn if off
        """
        if not self._has_fan_speed:
            return

        if self._fan_enabled == enable:
            return
        self._fan_enabled = enable

        comp = self._getComponent("ccd")

        if enable:
            if self._enabled_fan_speed is not None:
                logging.debug("Turning fan on of %s", comp.name)
                comp.fanSpeed.value = max(comp.fanSpeed.value, self._enabled_fan_speed)
        else:
            if comp.fanSpeed.value == 0:
                # Already off => don't touch it
                self._enabled_fan_speed = None
                self._enabled_fan_temp = None
            else:
                logging.debug("Turning fan off of %s", comp.name)
                self._enabled_fan_speed = comp.fanSpeed.value
                comp.fanSpeed.value = 0

        # Raise targetTemperature to max/ambient to avoid the fan from
        # automatically starting again. (Some hardware have this built-in when
        # the current temperature is too high compared to the target)
        if self._has_fan_temp:
            temp = comp.targetTemperature
            if enable:
                if self._enabled_fan_temp is not None:
                    temp.value = min(comp.targetTemperature.value, self._enabled_fan_temp)
                    try:
                        self._waitTemperatureReached(comp, timeout=60)
                    except Exception as ex:
                        logging.warning("Failed to reach target temperature of CCD: %s",
                                        ex)
            else:
                # Set ~25°C == ambient temperature
                self._enabled_fan_temp = temp.value
                try:
                    try:
                        temp.value = min(comp.targetTemperature.range[1], 25)
                    except (AttributeError, NotApplicableError):
                        temp.value = util.find_closest(25, comp.targetTemperature.choices)
                except Exception:
                    logging.warning("Failed to change targetTemperature when disabling fan",
                                    exc_info=True)
Пример #17
0
 def _updatePosition(self):
     """
     update the position VA
     """
     # if it is an unsupported position report the nearest supported one
     real_pos = self._position[self._axis]
     nearest = util.find_closest(real_pos, self._positions.keys())
     if not util.almost_equal(real_pos, nearest):
         logging.warning("Reporting axis %s @ %s (known position), while physical axis %s @ %s",
                         self._axis, nearest, self._caxis, real_pos)
     pos = {self._axis: nearest}
     logging.debug("reporting position %s", pos)
     self.position._set_value(pos, force_write=True)
Пример #18
0
    def _setPC(self, value):
        currents = self.probeCurrent.choices

        self._probeCurrent = util.find_closest(value, currents)
        self._indexCurrent = util.index_closest(value, self._list_currents)

        # Set the corresponding current index to Tescan SEM
        self.parent._device.SetPCIndex(self._indexCurrent + 1)
        # Adjust brightness and contrast
        with self.parent._acquisition_init_lock:
            self.parent._device.DtAutoSignal(self.parent._detector._channel)

        return self._probeCurrent
Пример #19
0
    def _setPC(self, value):
        currents = self.probeCurrent.choices

        self._probeCurrent = util.find_closest(value, currents)
        self._indexCurrent = util.index_closest(value, self._list_currents)

        # Set the corresponding current index to Tescan SEM
        self.parent._device.SetPCIndex(self._indexCurrent + 1)
        # Adjust brightness and contrast
        with self.parent._acquisition_init_lock:
            self.parent._device.DtAutoSignal(self.parent._detector._channel)

        return self._probeCurrent
Пример #20
0
 def _updatePosition(self):
     """
     update the position VA
     """
     # if it is an unsupported position report the nearest supported one
     real_pos = self._position[self._axis]
     nearest = util.find_closest(real_pos, self._positions.keys())
     if not util.almost_equal(real_pos, nearest):
         logging.warning("Reporting axis %s @ %s (known position), while physical axis %s @ %s",
                         self._axis, nearest, self._caxis, real_pos)
     pos = {self._axis: nearest}
     logging.debug("reporting position %s", pos)
     self.position._set_value(pos, force_write=True)
Пример #21
0
def get_time_range_to_trigger_delay(data, timeRange_choices,
                                    triggerDelay_range):
    """
    Reads the time range and trigger delay values from a csv object.
    Checks values for validity.
    :parameter data: (csv.reader object) calibration file
    :parameter timeRange_choices: (frozenset) choices possible for timeRange VA
    :parameter triggerDelay_range: (tuple) range possible for trigger delay values
    :return: (dict) new dictionary containing the loaded time range to trigger delay info
    """
    new_dict = {}

    for timeRange, delay in data:

        try:
            timeRange = float(timeRange)
            delay = float(delay)
        except ValueError:
            raise ValueError(
                "Trigger delay %s and/or time range %s is not of type float. "
                "Please check calibration file for trigger delay." %
                (delay, timeRange))

        # check delay in range allowed
        if not triggerDelay_range[0] <= delay <= triggerDelay_range[1]:
            raise ValueError(
                "Trigger delay %s corresponding to time range %s is not in range %s. "
                "Please check the calibration file for the trigger delay." %
                (delay, timeRange, triggerDelay_range))

        # check timeRange is in possible choices for timeRange on HW
        choice = find_closest(timeRange, timeRange_choices)
        if not almost_equal(timeRange, choice):
            raise ValueError(
                "Time range % s found in calibration file is not a possible choice "
                "for the time range of the streak unit. "
                "Please modify csv file so it fits the possible choices for the "
                "time range of the streak unit. "
                "Values in file must be of format timeRange:triggerDelay (per line)."
                % timeRange)

        new_dict[timeRange] = delay

    # check all time ranges are there
    if len(new_dict) == len(timeRange_choices):
        return new_dict
    else:
        raise ValueError(
            "The total number of %s time ranges in the loaded calibration file does not "
            "match the requested number of %s time ranges." %
            (len(new_dict), len(timeRange_choices)))
Пример #22
0
def read_trigger_delay_csv(filename, time_choices, trigger_delay_range):
    """
    Read the MD_TIME_RANGE_TO_DELAY from a CSV file, and check its validity based on the hardware
    filename (str): the path to file
    time_choices (set): choices possible for timeRange VA
    trigger_delay_range (float, float): min/max value of the trigger delay
    return (dict float -> float): new dictionary containing the loaded time range to trigger delay info
    raise ValueError: if the data of the CSV file cannot be parsed or doesn't fit the hardware
    raise IOError: if the file doesn't exist
    """
    tr2d = {}
    with open(filename, 'r', newline='') as csvfile:
        calibFile = csv.reader(csvfile, delimiter=':')
        for time_range, delay in calibFile:
            try:
                time_range = float(time_range)
                delay = float(delay)
            except ValueError:
                raise ValueError(
                    "Trigger delay %s and/or time range %s is not of type float. "
                    "Please check calibration file for trigger delay." %
                    (delay, time_range))

            # check delay in range allowed
            if not trigger_delay_range[0] <= delay <= trigger_delay_range[1]:
                raise ValueError(
                    "Trigger delay %s corresponding to time range %s is not in range %s. "
                    "Please check the calibration file for the trigger delay."
                    % (delay, time_range, trigger_delay_range))

            # check timeRange is in possible choices for timeRange on HW
            time_range_hw = find_closest(time_range, time_choices)
            if not almost_equal(time_range, time_range_hw):
                raise ValueError(
                    "Time range % s found in calibration file is not a possible choice "
                    "for the time range of the streak unit. "
                    "Please modify CSV file so it fits the possible choices for the "
                    "time range of the streak unit. "
                    "Values in file must be of format timeRange:triggerDelay (per line)."
                    % time_range)
            tr2d[time_range_hw] = delay

    # check all time ranges are there
    if len(tr2d) != len(time_choices):
        raise ValueError(
            "The total number of %s time ranges in the loaded calibration file does not "
            "match the requested number of %s time ranges." %
            (len(tr2d), len(time_choices)))
    return tr2d
Пример #23
0
def set_attr(comp_name, attr_name, str_val):
    """
    set the value of vigilant attribute of the given component using the type
    of the current value of the attribute.
    """
    try:
        component = get_component(comp_name)
    except LookupError:
        logging.error("Failed to find component '%s'", comp_name)
        return 127
    except:
        logging.error("Failed to contact the back-end")
        return 127

    try:
        attr = getattr(component, attr_name)
    except:
        logging.error("Failed to find attribute '%s' on component '%s'",
                      attr_name, comp_name)
        return 129

    if not isinstance(attr, model.VigilantAttributeBase):
        logging.error("'%s' is not a vigilant attribute of component '%s'",
                      attr_name, comp_name)
        return 129

    try:
        new_val = convertToObject(str_val)
    except Exception:
        return 127

    # Special case for floats, due to rounding error, it's very hard to put the
    # exact value if it's an enumerated VA. So just pick the closest one in this
    # case.
    if isinstance(new_val, float) and (hasattr(attr, "choices") and isinstance(
            attr.choices, collections.Iterable)):
        orig_val = new_val
        new_val = util.find_closest(new_val, attr.choices)
        if new_val != orig_val:
            logging.debug("Adjusting value to %s", new_val)

    try:
        attr.value = new_val
    except:
        logging.exception("Failed to set %s.%s = '%s'", comp_name, attr_name,
                          str_val)
        return 127
    return 0
Пример #24
0
def set_attr(comp_name, attr_val_str):
    """
    set the value of vigilant attribute of the given component.
    attr_val_str (dict str->str): attribute name -> value as a string
    """
    component = get_component(comp_name)

    for attr_name, str_val in attr_val_str.items():
        try:
            attr = getattr(component, attr_name)
        except Exception:
            raise ValueError(
                "Failed to find attribute '%s' on component '%s'" %
                (attr_name, comp_name))

        if not isinstance(attr, model.VigilantAttributeBase):
            raise ValueError(
                "'%s' is not a vigilant attribute of component %s" %
                (attr_name, comp_name))

        new_val = convert_to_object(str_val)

        # Special case for floats, due to rounding error, it's very hard to put the
        # exact value if it's an enumerated VA. So just pick the closest one in this
        # case.
        if (isinstance(new_val, float) and hasattr(attr, "choices")
                and isinstance(attr.choices, collections.Iterable)):
            orig_val = new_val
            choices = [
                v for v in attr.choices if isinstance(v, numbers.Number)
            ]
            new_val = util.find_closest(new_val, choices)
            if new_val != orig_val:
                logging.debug("Adjusting value to %s", new_val)

        # Special case for None being referred to as "null" in YAML, but we should
        # also accept "None"
        elif new_val == "None" and not isinstance(attr.value, basestring):
            new_val = None
            logging.debug("Adjusting value to %s (null)", new_val)

        try:
            attr.value = new_val
        except Exception as exc:
            raise IOError("Failed to set %s.%s = '%s': %s" %
                          (comp_name, attr_name, str_val, exc))
Пример #25
0
    def _onOpenCalibFile(self, event):
        """
        Loads a calibration file (*csv) containing the time range and the corresponding trigger delay
        for streak camera calibration.
        """
        logging.debug(
            "Open trigger delay calibration file for temporal acquisition.")

        dialog = wx.FileDialog(self.panel,
                               message="Choose a calibration file to load",
                               defaultDir=self._calib_path,
                               defaultFile="",
                               style=wx.FD_OPEN,
                               wildcard="csv files (*.csv)|*.csv")

        # Show the dialog and check whether is was accepted or cancelled
        if dialog.ShowModal() != wx.ID_OK:
            return

        # get selected path + filename and update default directory
        self._calib_path = dialog.GetDirectory()
        path = dialog.GetPath()
        filename = dialog.GetFilename()

        # read file
        try:
            tr2d = calibration.read_trigger_delay_csv(
                path, self.streak_unit.timeRange.choices,
                self.streak_delay.triggerDelay.range)
        except ValueError as error:
            self._onUpdateTriggerDelayGUI("Error while loading file!",
                                          odemis.gui.FG_COLOUR_HIGHLIGHT)
            logging.error("Failed loading %s: %s", filename, error)
            return

        # update the MD: overwrite the complete dict
        self.streak_delay.updateMetadata({model.MD_TIME_RANGE_TO_DELAY: tr2d})

        # update triggerDelay shown in GUI
        cur_timeRange = self.streak_unit.timeRange.value
        # find the corresponding trigger delay
        key = util.find_closest(cur_timeRange, tr2d.keys())
        # Note: no need to check almost_equal again as we do that already when loading the file
        self.streak_delay.triggerDelay.value = tr2d[key]  # set the new value

        self._onUpdateTriggerDelayGUI(filename)  # update txt displayed in GUI
Пример #26
0
def set_attr(comp_name, attr_name, str_val):
    """
    set the value of vigilant attribute of the given component using the type
    of the current value of the attribute.
    """
    try:
        component = get_component(comp_name)
    except LookupError:
        logging.error("Failed to find component '%s'", comp_name)
        return 127
    except:
        logging.error("Failed to contact the back-end")
        return 127

    try:
        attr = getattr(component, attr_name)
    except:
        logging.error("Failed to find attribute '%s' on component '%s'", attr_name, comp_name)
        return 129

    if not isinstance(attr, model.VigilantAttributeBase):
        logging.error("'%s' is not a vigilant attribute of component '%s'", attr_name, comp_name)
        return 129

    try:
        new_val = convertToObject(str_val)
    except Exception:
        return 127

    # Special case for floats, due to rounding error, it's very hard to put the
    # exact value if it's an enumerated VA. So just pick the closest one in this
    # case.
    if isinstance(new_val, float) and (
       hasattr(attr, "choices") and isinstance(attr.choices, collections.Iterable)):
        orig_val = new_val
        new_val = util.find_closest(new_val, attr.choices)
        if new_val != orig_val:
            logging.debug("Adjusting value to %s", new_val)

    try:
        attr.value = new_val
    except:
        logging.exception("Failed to set %s.%s = '%s'", comp_name, attr_name, str_val)
        return 127
    return 0
Пример #27
0
    def test_acqSync_EarlyEvents(self):
        """Test early events triggered in synchronous acquisition mode."""

        # AsyncCommandStatus() returns: pending, preparing, active
        # AsyncCommandPreparing = True  # action has not yet been started
        # AsyncCommandActive = True  # action has been started
        # AsyncCommandPending = False  # action has been ended, if True: action still going on

        # Note: async commands are: AcqStart, SeqStart, SeqSave, SeqLoad

        # choose very small exposure time to trigger for asynchronous commands are handled correctly
        self.readoutcam.exposureTime.value = 0.00001  # 10us
        self.streakunit.timeRange.value = util.find_closest(
            0.000001, self.streakunit.timeRange.choices)
        size = self.readoutcam.resolution.value

        self.readoutcam.data.synchronizedOn(self.readoutcam.softwareTrigger)

        num_images = 5
        self.camera_left = num_images

        def receive_image(dataflow, image):
            """Callback for readout camera"""
            self.assertEqual(image.shape, size[::-1])  # invert size
            self.assertIn(model.MD_EXP_TIME, image.metadata)
            self.camera_left -= 1
            logging.debug("Got image.")
            if self.camera_left <= 0:
                dataflow.unsubscribe(receive_image)

        self.readoutcam.data.subscribe(receive_image)

        # Wait for the image
        for i in range(num_images):
            # call notify quickly to trigger an early event
            self.readoutcam.softwareTrigger.notify()
            if i == num_images - 1:
                time.sleep(0.01)
                # if trigger event was fast enough we should get more than one acq waiting in queue
                self.assertGreater(len(self.readoutcam.queue_events), 1)

        time.sleep(num_images * 0.2 *
                   2)  # wait some time for acquisition to finish
        self.assertEqual(len(self.readoutcam.queue_events), 0)
Пример #28
0
def get_time_range_to_trigger_delay(data, timeRange_choices, triggerDelay_range):
    """
    Reads the time range and trigger delay values from a csv object.
    Checks values for validity.
    :parameter data: (csv.reader object) calibration file
    :parameter timeRange_choices: (frozenset) choices possible for timeRange VA
    :parameter triggerDelay_range: (tuple) range possible for trigger delay values
    :return: (dict) new dictionary containing the loaded time range to trigger delay info
    """
    new_dict = {}

    for timeRange, delay in data:

        try:
            timeRange = float(timeRange)
            delay = float(delay)
        except ValueError:
            raise ValueError("Trigger delay %s and/or time range %s is not of type float. "
                             "Please check calibration file for trigger delay." % (delay, timeRange))

        # check delay in range allowed
        if not triggerDelay_range[0] <= delay <= triggerDelay_range[1]:
            raise ValueError("Trigger delay %s corresponding to time range %s is not in range (0, 1). "
                             "Please check the calibration file for the trigger delay." % (delay, timeRange))

        # check timeRange is in possible choices for timeRange on HW
        choice = find_closest(timeRange, timeRange_choices)
        if not almost_equal(timeRange, choice):
            raise ValueError("Time range % s found in calibration file is not a possible choice "
                             "for the time range of the streak unit. "
                             "Please modify csv file so it fits the possible choices for the "
                             "time range of the streak unit. "
                             "Values in file must be of format timeRange:triggerDelay (per line)."
                             % timeRange)

        new_dict[timeRange] = delay

    # check all time ranges are there
    if len(new_dict) == len(timeRange_choices):
        return new_dict
    else:
        raise ValueError("The total number of %s time ranges in the loaded calibration file does not "
                         "match the requested number of %s time ranges."
                         % (len(new_dict), len(timeRange_choices)))
Пример #29
0
    def _on_em_view_mpp_change(self, mpp):
        """
        Set the microscope's hfw when the MicroscopeView's mpp value changes
         (or the viewport size changes)

        The canvas calculates the new hfw value.
        """
        fov = self.get_fov_from_mpp()
        self.microscope_view.fov.value = fov
        self.microscope_view.fov_buffer.value = self.get_buffer_fov_from_mpp()

        # Only change the FoV of the hardware if:
        # * this Viewport was *not* responsible for setting the mpp
        #   (by calling `self.set_mpp_from_fov`)
        # * _and_ if it's displayed on screen (so we don't interfere with
        #   viewports in other tabs that are not currently displayed)
        # This way, we prevent mpp/fov setting loops.
        if not self.self_set_mpp and self.IsShownOnScreen():
            logging.debug("View mpp changed to %s on %s", mpp, self)
            if fov is None:
                return

            fov_va = self.microscope_view.fov_hw.horizontalFoV
            shape = self.microscope_view.fov_hw.shape
            # Compute the hfov, so that the whole HW FoV just fully fit
            hfov = min(fov[0], fov[1] * shape[0] / shape[1])

            try:
                # TODO: Test with a simulated SEM that has HFW choices
                choices = fov_va.choices
                # Get the choice that matches hfw most closely
                hfov = util.find_closest(hfov, choices)
            except NotApplicableError:
                hfov = fov_va.clip(hfov)

            # Indicate that this object was responsible for updating the hardware's HFW, so it won't
            # get updated again in `_on_hw_fov_change`
            self.self_set_fov = True
            logging.debug("Setting hardware FoV to %s", hfov)
            fov_va.value = hfov
        else:
            self.self_set_mpp = False
Пример #30
0
    def test_acqSync_EarlyEvents(self):
        """Test early events triggered in synchronous acquisition mode."""

        # AsyncCommandStatus() returns: pending, preparing, active
        # AsyncCommandPreparing = True  # action has not yet been started
        # AsyncCommandActive = True  # action has been started
        # AsyncCommandPending = False  # action has been ended, if True: action still going on

        # Note: async commands are: AcqStart, SeqStart, SeqSave, SeqLoad

        # choose very small exposure time to trigger for asynchronous commands are handled correctly
        self.readoutcam.exposureTime.value = 0.00001  # 10us
        self.streakunit.timeRange.value = util.find_closest(0.000001, self.streakunit.timeRange.choices)
        size = self.readoutcam.resolution.value

        self.readoutcam.data.synchronizedOn(self.readoutcam.softwareTrigger)

        num_images = 5
        self.camera_left = num_images

        def receive_image(dataflow, image):
            """Callback for readout camera"""
            self.assertEqual(image.shape, size[::-1])  # invert size
            self.assertIn(model.MD_EXP_TIME, image.metadata)
            self.camera_left -= 1
            logging.debug("Got image.")
            if self.camera_left <= 0:
                dataflow.unsubscribe(receive_image)

        self.readoutcam.data.subscribe(receive_image)

        # Wait for the image
        for i in range(num_images):
            # call notify quickly to trigger an early event
            self.readoutcam.softwareTrigger.notify()
            if i == num_images - 1:
                time.sleep(0.01)
                # if trigger event was fast enough we should get more than one acq waiting in queue
                self.assertGreater(len(self.readoutcam.queue_events), 1)

        time.sleep(num_images * 0.2 * 2)  # wait some time for acquisition to finish
        self.assertEqual(len(self.readoutcam.queue_events), 0)
Пример #31
0
    def _setTimeRange(self, value):
        """
        Updates the timeRange VA.
        :parameter value: (float) value to be set
        :return: (float) current time range
        """
        logging.debug("Reporting time range %s for streak unit.", value)
        self._metadata[model.MD_STREAK_TIMERANGE] = value

        # set corresponding trigger delay
        tr2d = self.parent._delaybox._metadata.get(model.MD_TIME_RANGE_TO_DELAY)
        if tr2d:
            key = util.find_closest(value, tr2d.keys())
            if util.almost_equal(key, value):
                self.parent._delaybox.triggerDelay.value = tr2d[key]
            else:
                logging.warning("Time range %s is not a key in MD for time range to "
                                "trigger delay calibration" % value)

        return value
Пример #32
0
    def _setTimeRange(self, value):
        """
        Updates the timeRange VA.
        :parameter value: (float) value to be set
        :return: (float) current time range
        """
        logging.debug("Reporting time range %s for streak unit.", value)
        self._metadata[model.MD_STREAK_TIMERANGE] = value

        # set corresponding trigger delay
        tr2d = self.parent._delaybox._metadata.get(model.MD_TIME_RANGE_TO_DELAY)
        if tr2d:
            key = util.find_closest(value, tr2d.keys())
            if util.almost_equal(key, value):
                self.parent._delaybox.triggerDelay.value = tr2d[key]
            else:
                logging.warning("Time range %s is not a key in MD for time range to "
                                "trigger delay calibration" % value)

        return value
Пример #33
0
def set_attr(comp_name, attr_val_str):
    """
    set the value of vigilant attribute of the given component.
    attr_val_str (dict str->str): attribute name -> value as a string
    """
    component = get_component(comp_name)

    for attr_name, str_val in attr_val_str.items():
        try:
            attr = getattr(component, attr_name)
        except Exception:
            raise ValueError(
                "Failed to find attribute '%s' on component '%s'" %
                (attr_name, comp_name))

        if not isinstance(attr, model.VigilantAttributeBase):
            raise ValueError(
                "'%s' is not a vigilant attribute of component %s" %
                (attr_name, comp_name))

        new_val = convertToObject(str_val)

        # Special case for floats, due to rounding error, it's very hard to put the
        # exact value if it's an enumerated VA. So just pick the closest one in this
        # case.
        if isinstance(new_val,
                      float) and (hasattr(attr, "choices") and isinstance(
                          attr.choices, collections.Iterable)):
            orig_val = new_val
            new_val = util.find_closest(new_val, attr.choices)
            if new_val != orig_val:
                logging.debug("Adjusting value to %s", new_val)

        try:
            attr.value = new_val
        except Exception as exc:
            raise IOError("Failed to set %s.%s = '%s': %s" %
                          (comp_name, attr_name, str_val, exc))
Пример #34
0
    def test_acq_Live_RingBuffer_subscribe(self):
        """Acquire single image and receive it via the dataport."""

        # Note: AcqStop can be called multiple times even if the acq is already stopped without causing an error
        # However, AcqStart can be only called once and raises an error if called while an acq is running.
        # AcqStop is not an asynchronous command. But it takes time until the status of the async command
        # "AcqStart" is properly finished.
        # Changing settings is not async. RemoteEx blocks as long as the settings are changed.
        # RemoteEx also stops the "Live" mode if a settings change is requested but does not restarts the "Live" mode.

        self.streakunit.streakMode.value = True
        self.streakunit.timeRange.value = util.find_closest(
            0.000000002, self.streakunit.timeRange.choices)
        self.streakunit.MCPGain.value = 2
        self.readoutcam.exposureTime.value = 0.1  # 100ms
        time.sleep(1)

        # start Live mode
        # and subscribe to dataflow afterwards in order to request one image while acq in Live mode is already running
        # The acq should be automatically stopped and restarted, otherwise a RemoteEx error will be received.
        # error returned: ['7', 'AcqStart', 'async command pending', 'HAcq_mLive']
        self.streakcam.StartAcquisition(
            self.readoutcam.acqMode)  # acquire images

        def callback(dataflow, image):
            # self.streakcam.AcqAcqMonitor("Off")  # TODO?
            self.readoutcam.data.unsubscribe(callback)
            # Note: MCPGain set to 0 is handled by stream not by driver except when changing from
            # "Operate" mode to "Focus" mode
            size = self.readoutcam.resolution.value
            self.assertEqual(image.shape, size[::-1])  # invert size
            self.assertIn(model.MD_EXP_TIME, image.metadata)
            logging.debug("Got image.")

        self.readoutcam.data.subscribe(callback)
        time.sleep(5)
Пример #35
0
 def _setWavelength(self, value):
     return find_closest(value, self._wl_px_values)
Пример #36
0
    def test_acqSync_SingleLive_RingBuffer_subscribe(self):
        """Test to acquire synchronized images by subscribing."""

        # test sync acq in focus mode
        self.streakunit.streakMode.value = False
        size = self.readoutcam.resolution.value
        # Note: When using self.acqMode = "SingleLive" parameters regarding the readout camera
        # need to be changed via location = "Live"! For now hardcoded in driver...
        self.readoutcam.exposureTime.value = 2  # s
        exp_time = self.readoutcam.exposureTime.value

        num_images = 5
        self.images_left = num_images  # unsubscribe after receiving number of images

        self.readoutcam.data.synchronizedOn(self.readoutcam.softwareTrigger)

        def receive_image(dataflow, image):
            """Callback for readout camera"""
            self.assertEqual(image.shape, size[::-1])  # invert size
            self.assertIn(model.MD_EXP_TIME, image.metadata)
            self.assertNotIn(model.MD_TIME_LIST, image.metadata)
            self.assertFalse(image.metadata[model.MD_STREAK_MODE])
            self.images_left -= 1
            logging.debug("Got image.")
            if self.images_left == 0:
                dataflow.unsubscribe(receive_image)
                self.assertEqual(self.streakunit.MCPGain.value, 0)  # MCPGain should be zero when acq finished
                self.end_time = time.time()

        self.readoutcam.data.subscribe(receive_image)

        # Wait for the image
        for i in range(num_images):
            self.readoutcam.softwareTrigger.notify()
            time.sleep(i * 0.1)  # wait a bit to simulate some processing

        # Waiting long enough
        time.sleep(num_images * exp_time + 2)
        self.assertEqual(self.images_left, 0)
        self.readoutcam.data.synchronizedOn(None)

        # check we can still get data normally
        img = self.readoutcam.data.get()

        # test sync acq in operate mode
        self.streakunit.streakMode.value = True

        self.streakunit.timeRange.value = util.find_closest(0.001, self.streakunit.timeRange.choices)

        num_images = 5
        self.images_left = num_images  # unsubscribe after receiving number of images

        self.readoutcam.data.synchronizedOn(self.readoutcam.softwareTrigger)

        def receive_image(dataflow, image):
            """Callback for readout camera"""
            self.assertEqual(image.shape, size[::-1])  # invert size
            self.assertIn(model.MD_EXP_TIME, image.metadata)
            self.assertIn(model.MD_TIME_LIST, image.metadata)
            self.assertTrue(image.metadata[model.MD_STREAK_MODE])
            self.images_left -= 1
            logging.debug("Got image.")
            if self.images_left == 0:
                dataflow.unsubscribe(receive_image)
                self.end_time = time.time()

        self.readoutcam.data.subscribe(receive_image)

        # Wait for the image
        for i in range(num_images):
            self.readoutcam.softwareTrigger.notify()
            time.sleep(i * 0.1)  # wait a bit to simulate some processing

        # Waiting long enough
        time.sleep(num_images * exp_time + 2)
        self.assertEqual(self.images_left, 0)
        self.readoutcam.data.synchronizedOn(None)

        # check we can still get data normally
        img = self.readoutcam.data.get()
Пример #37
0
 def _setWavelength(self, value):
     return find_closest(value, self._wl_px_values)
Пример #38
0
 def _setTime(self, value):
     return find_closest(value, self._tl_px_values)
Пример #39
0
def move_abs(comp_name, moves, check_distance=True):
    """
    move (in absolute) the axis of the given component to the specified position
    comp_name (str): name of the component
    check_distance (bool): if the axis is in meters, check that the move is not
      too big.
    moves (dict str -> str): axis -> position (as text)
    """
    component = get_component(comp_name)

    act_mv = {} # axis -> value
    for axis_name, str_position in moves.items():
        try:
            if axis_name not in component.axes:
                raise ValueError("Actuator %s has no axis '%s'" % (comp_name, axis_name))
            ad = component.axes[axis_name]
        except (TypeError, AttributeError):
            raise ValueError("Component %s is not an actuator" % comp_name)

        # Allow the user to indicate the position via the user-friendly choice entry
        position = None
        if (hasattr(ad, "choices") and isinstance(ad.choices, dict)):
            for key, value in ad.choices.items():
                if value == str_position:
                    logging.info("Converting '%s' into %s", str_position, key)
                    position = key
                    # Even if it's a big distance, we don't complain as it's likely
                    # that all choices are safe
                    break

        if position is None:
            if ad.unit == "m":
                try:
                    position = float(str_position)
                except ValueError:
                    raise ValueError("Position '%s' cannot be converted to a number" % str_position)

                # compare to the current position, to see if the new position sounds reasonable
                cur_pos = component.position.value[axis_name]
                if check_distance and abs(cur_pos - position) > MAX_DISTANCE:
                    raise IOError("Distance of %f m is too big (> %f m), use '--big-distance' to allow the move." %
                                  (abs(cur_pos - position), MAX_DISTANCE))
            else:
                position = convert_to_object(str_position)

            # If only a couple of positions are possible, and asking for a float,
            # avoid the rounding error by looking for the closest possible
            if (isinstance(position, numbers.Real) and
                hasattr(ad, "choices") and
                isinstance(ad.choices, collections.Iterable) and
                position not in ad.choices):
                closest = util.find_closest(position, ad.choices)
                if util.almost_equal(closest, position, rtol=1e-3):
                    logging.debug("Adjusting value %.15g to %.15g", position, closest)
                    position = closest

        act_mv[axis_name] = position
        logging.info(u"Will move %s.%s to %s", comp_name, axis_name,
                     units.readable_str(position, ad.unit, sig=3))

    try:
        m = component.moveAbs(act_mv)
        try:
            m.result(120)
        except KeyboardInterrupt:
            logging.warning("Cancelling absolute move of component %s", comp_name)
            m.cancel()
            raise
    except Exception as exc:
        raise IOError("Failed to move component %s to %s: %s" %
                      (comp_name, act_mv, exc))
Пример #40
0
 def _setTime(self, value):
     return find_closest(value, self._tl_px_values)
Пример #41
0
    def __init__(self, name, role, children, axis_name, positions, cycle=None, **kwargs):
        """
        name (string)
        role (string)
        children (dict str -> actuator): axis name (in this actuator) -> actuator to be used for this axis
        axis_name (str): axis name in the child actuator
        positions (set or dict value -> str): positions where the actuator is allowed to move
        cycle (float): if not None, it means the actuator does a cyclic move and this value represents a full cycle
        """
        # TODO: forbid inverted
        if len(children) != 1:
            raise ValueError("FixedPositionsActuator needs precisely one child")

        self._cycle = cycle
        self._move_sum = 0
        self._position = {}
        self._referenced = {}
        axis, child = children.items()[0]
        self._axis = axis
        self._child = child
        self._caxis = axis_name
        self._positions = positions
        # Executor used to reference and move to nearest position
        self._executor = CancellableThreadPoolExecutor(max_workers=1)  # one task at a time

        if not isinstance(child, model.ComponentBase):
            raise ValueError("Child %s is not a component." % (child,))
        if not hasattr(child, "axes") or not isinstance(child.axes, dict):
            raise ValueError("Child %s is not an actuator." % child.name)

        if cycle is not None:
            # just an offset to reference switch position
            self._offset = self._cycle / len(self._positions)
            if not all(0 <= p < cycle for p in positions.keys()):
                raise ValueError("Positions must be between 0 and %s (non inclusive)" % (cycle,))

        ac = child.axes[axis_name]
        axes = {axis: model.Axis(choices=positions, unit=ac.unit)}  # TODO: allow the user to override the unit?

        model.Actuator.__init__(self, name, role, axes=axes, children=children, **kwargs)

        self._position = {}
        self.position = model.VigilantAttribute({}, readonly=True)

        logging.debug("Subscribing to position of child %s", child.name)
        child.position.subscribe(self._update_child_position, init=True)

        if model.hasVA(child, "referenced") and axis_name in child.referenced.value:
            self._referenced[axis] = child.referenced.value[axis_name]
            self.referenced = model.VigilantAttribute(self._referenced.copy(), readonly=True)
            child.referenced.subscribe(self._update_child_ref)

        # If the axis can be referenced => do it now (and move to a known position)
        # In case of cyclic move always reference
        if not self._referenced.get(axis, True) or (self._cycle and axis in self._referenced):
            # The initialisation will not fail if the referencing fails
            f = self.reference({axis})
            f.add_done_callback(self._on_referenced)
        else:
            # If not at a known position => move to the closest known position
            nearest = util.find_closest(self._child.position.value[self._caxis], self._positions.keys())
            self.moveAbs({self._axis: nearest}).result()
Пример #42
0
    def __init__(self, name, role, children, axis_name, positions, cycle=None, **kwargs):
        """
        name (string)
        role (string)
        children (dict str -> actuator): axis name (in this actuator) -> actuator to be used for this axis
        axis_name (str): axis name in the child actuator
        positions (set or dict value -> str): positions where the actuator is allowed to move
        cycle (float): if not None, it means the actuator does a cyclic move and this value represents a full cycle
        """
        # TODO: forbid inverted
        if len(children) != 1:
            raise ValueError("FixedPositionsActuator needs precisely one child")

        self._cycle = cycle
        self._move_sum = 0
        self._position = {}
        self._referenced = {}
        axis, child = children.items()[0]
        self._axis = axis
        self._child = child
        self._caxis = axis_name
        self._positions = positions
        # Executor used to reference and move to nearest position
        self._executor = CancellableThreadPoolExecutor(max_workers=1)  # one task at a time

        if not isinstance(child, model.ComponentBase):
            raise ValueError("Child %s is not a component." % (child,))
        if not hasattr(child, "axes") or not isinstance(child.axes, dict):
            raise ValueError("Child %s is not an actuator." % child.name)

        if cycle is not None:
            # just an offset to reference switch position
            self._offset = self._cycle / len(self._positions)
            if not all(0 <= p < cycle for p in positions.keys()):
                raise ValueError("Positions must be between 0 and %s (non inclusive)" % (cycle,))

        ac = child.axes[axis_name]
        axes = {axis: model.Axis(choices=positions, unit=ac.unit)}  # TODO: allow the user to override the unit?

        model.Actuator.__init__(self, name, role, axes=axes, children=children, **kwargs)

        self._position = {}
        self.position = model.VigilantAttribute({}, readonly=True)

        logging.debug("Subscribing to position of child %s", child.name)
        child.position.subscribe(self._update_child_position, init=True)

        if model.hasVA(child, "referenced") and axis_name in child.referenced.value:
            self._referenced[axis] = child.referenced.value[axis_name]
            self.referenced = model.VigilantAttribute(self._referenced.copy(), readonly=True)
            child.referenced.subscribe(self._update_child_ref)

        # If the axis can be referenced => do it now (and move to a known position)
        # In case of cyclic move always reference
        if not self._referenced.get(axis, True) or (self._cycle and axis in self._referenced):
            # The initialisation will not fail if the referencing fails
            f = self.reference({axis})
            f.add_done_callback(self._on_referenced)
        else:
            # If not at a known position => move to the closest known position
            nearest = util.find_closest(self._child.position.value[self._caxis], self._positions.keys())
            self.moveAbs({self._axis: nearest}).result()
Пример #43
0
def move_abs(comp_name, moves, check_distance=True):
    """
    move (in absolute) the axis of the given component to the specified position
    comp_name (str): name of the component
    check_distance (bool): if the axis is in meters, check that the move is not
      too big.
    moves (dict str -> str): axis -> position (as text)
    """
    component = get_component(comp_name)

    act_mv = {} # axis -> value
    for axis_name, str_position in moves.items():
        try:
            if axis_name not in component.axes:
                raise ValueError("Actuator %s has no axis '%s'" % (comp_name, axis_name))
            ad = component.axes[axis_name]
        except (TypeError, AttributeError):
            raise ValueError("Component %s is not an actuator" % comp_name)

        # Allow the user to indicate the position via the user-friendly choice entry
        position = None
        if hasattr(ad, "choices") and isinstance(ad.choices, dict):
            for key, value in ad.choices.items():
                if value == str_position:
                    logging.info("Converting '%s' into %s", str_position, key)
                    position = key
                    # Even if it's a big distance, we don't complain as it's likely
                    # that all choices are safe
                    break

        if position is None:
            if ad.unit == "m":
                try:
                    position = float(convert_to_object(str_position))
                except ValueError:
                    raise ValueError("Position '%s' cannot be converted to a number" % str_position)

                # compare to the current position, to see if the new position sounds reasonable
                cur_pos = component.position.value[axis_name]
                if check_distance and abs(cur_pos - position) > MAX_DISTANCE:
                    raise IOError("Distance of %f m is too big (> %f m), use '--big-distance' to allow the move." %
                                  (abs(cur_pos - position), MAX_DISTANCE))
            else:
                position = convert_to_object(str_position)

            # If only a couple of positions are possible, and asking for a float,
            # avoid the rounding error by looking for the closest possible
            if (isinstance(position, numbers.Real) and
                hasattr(ad, "choices") and
                isinstance(ad.choices, collections.Iterable) and
                position not in ad.choices):
                closest = util.find_closest(position, ad.choices)
                if util.almost_equal(closest, position, rtol=1e-3):
                    logging.debug("Adjusting value %.15g to %.15g", position, closest)
                    position = closest

        act_mv[axis_name] = position
        if isinstance(position, numbers.Real):
            pos_pretty = units.readable_str(position, ad.unit, sig=3)
        else:
            pos_pretty = "%s" % (position,)
        logging.info(u"Will move %s.%s to %s", comp_name, axis_name, pos_pretty)

    try:
        m = component.moveAbs(act_mv)
        try:
            m.result(120)
        except KeyboardInterrupt:
            logging.warning("Cancelling absolute move of component %s", comp_name)
            m.cancel()
            raise
    except Exception as exc:
        raise IOError("Failed to move component %s to %s: %s" %
                      (comp_name, act_mv, exc))
Пример #44
0
    def test_acqSync_SingleLive_RingBuffer_subscribe(self):
        """Test to acquire synchronized images by subscribing."""

        # test sync acq in focus mode
        self.streakunit.streakMode.value = False
        size = self.readoutcam.resolution.value
        # Note: When using self.acqMode = "SingleLive" parameters regarding the readout camera
        # need to be changed via location = "Live"! For now hardcoded in driver...
        self.readoutcam.exposureTime.value = 2  # s
        exp_time = self.readoutcam.exposureTime.value

        num_images = 5
        self.images_left = num_images  # unsubscribe after receiving number of images

        self.readoutcam.data.synchronizedOn(self.readoutcam.softwareTrigger)

        def receive_image(dataflow, image):
            """Callback for readout camera"""
            self.assertEqual(image.shape, size[::-1])  # invert size
            self.assertIn(model.MD_EXP_TIME, image.metadata)
            self.assertNotIn(model.MD_TIME_LIST, image.metadata)
            self.assertFalse(image.metadata[model.MD_STREAK_MODE])
            self.images_left -= 1
            logging.debug("Got image.")
            if self.images_left == 0:
                dataflow.unsubscribe(receive_image)
                self.assertEqual(self.streakunit.MCPGain.value,
                                 0)  # MCPGain should be zero when acq finished
                self.end_time = time.time()

        self.readoutcam.data.subscribe(receive_image)

        # Wait for the image
        for i in range(num_images):
            self.readoutcam.softwareTrigger.notify()
            time.sleep(i * 0.1)  # wait a bit to simulate some processing

        # Waiting long enough
        time.sleep(num_images * exp_time + 2)
        self.assertEqual(self.images_left, 0)
        self.readoutcam.data.synchronizedOn(None)

        # check we can still get data normally
        img = self.readoutcam.data.get()

        # test sync acq in operate mode
        self.streakunit.streakMode.value = True

        self.streakunit.timeRange.value = util.find_closest(
            0.001, self.streakunit.timeRange.choices)

        num_images = 5
        self.images_left = num_images  # unsubscribe after receiving number of images

        self.readoutcam.data.synchronizedOn(self.readoutcam.softwareTrigger)

        def receive_image(dataflow, image):
            """Callback for readout camera"""
            self.assertEqual(image.shape, size[::-1])  # invert size
            self.assertIn(model.MD_EXP_TIME, image.metadata)
            self.assertIn(model.MD_TIME_LIST, image.metadata)
            self.assertTrue(image.metadata[model.MD_STREAK_MODE])
            self.images_left -= 1
            logging.debug("Got image.")
            if self.images_left == 0:
                dataflow.unsubscribe(receive_image)
                self.end_time = time.time()

        self.readoutcam.data.subscribe(receive_image)

        # Wait for the image
        for i in range(num_images):
            self.readoutcam.softwareTrigger.notify()
            time.sleep(i * 0.1)  # wait a bit to simulate some processing

        # Waiting long enough
        time.sleep(num_images * exp_time + 2)
        self.assertEqual(self.images_left, 0)
        self.readoutcam.data.synchronizedOn(None)

        # check we can still get data normally
        img = self.readoutcam.data.get()