Beispiel #1
0
    def check_value(self, value):
        """
        Raise an exception if the motor cannot move to value.

        The full list of checks done in this method:

            - Check if the value is within the soft limits of the motor.
            - Check if the motor is disabled ('.DISP' field).

        Raises
        ------
        MotorDisabledError
            If the motor is passed any motion command when disabled.

        ~ophyd.utils.errors.LimitError
            When the provided value is outside the range of the low
            and high limits.
        """

        # First check that the user has returned a valid EPICS value. It will
        # not consult the limits of the PV itself because limits=False
        super().check_value(value)

        # Find the soft limit values from EPICS records and check that this
        # command will be accepted by the motor
        if any(self.limits):
            if not (self.low_limit <= value <= self.high_limit):
                raise LimitError("Value {} outside of range: [{}, {}]".format(
                    value, self.low_limit, self.high_limit))

        # Find the value for the disabled attribute
        if self.disabled.get() == 1:
            raise MotorDisabledError("Motor is not enabled. Motion requests "
                                     "ignored")
Beispiel #2
0
    def check_value(self, position):
        """
        Checks to make sure the inputted value is valid.

        Parameters
        ----------
        position : float
            Position to check for validity

        Raises
        ------
        ValueError
            If position is None, NaN or Inf
        LimitError
            If the position is outside the soft limits.
        """
        # Check for invalid positions
        if position is None or np.isnan(position) or np.isinf(position):
            raise ValueError("Invalid value inputted: '{0}'".format(position))

        # Check if it is within the soft limits
        if not (self.low_limit <= position <= self.high_limit):
            err_str = "Requested value {0} outside of range: [{1}, {2}]"
            logger.warn(
                err_str.format(position, self.low_limit, self.high_limit))
            raise LimitError(err_str)
Beispiel #3
0
def _validate_motor_limits(motor, start, stop, k):
    # blow up on inverted values
    assert start < stop, (f"start ({start}) must be smaller than "
                          f"stop ({stop}) for {k}")
    limits = motor.limits
    if any(not (limits[0] < v < limits[1]) for v in (start, stop)):
        raise LimitError(f"your requested {k} values are out of limits for "
                         "the motor "
                         f"{limits[0]} < ({start}, {stop}) < {limits[1]}")
Beispiel #4
0
    def check_value(self, value):
        """
        Check if the value is within the soft limits of the motor.

        Raises
        ------
        ValueError
        """
        # First check that the user has returned a valid EPICS value. It will
        # not consult the limits of the PV itself because limits=False
        super().check_value(value)
        # Find the soft limit values from EPICS records and check that this
        # command will be accepted by the motor
        low_limit, high_limit = self.limits

        if not (low_limit <= value <= high_limit):
            raise LimitError("Value {} outside of range: [{}, {}]"
                             .format(value, low_limit, high_limit))
Beispiel #5
0
    def check_value(self, value, retries=5):
        """
        Check if the value is within the soft limits of the motor.

        Raises
        ------
        ValueError
        """
        if value is None:
            raise ValueError('Cannot write None to epics PVs')

        for i in range(retries):
            try:
                low_limit, high_limit = self.limits
                if not (low_limit <= value <= high_limit):
                    raise LimitError(
                        "Value {} outside of range: [{}, {}]".format(
                            value, low_limit, high_limit))
                return
            except TypeError:
                logger.warning("Failed to get limits, retrying...")
                if i == retries - 1:
                    raise
Beispiel #6
0
    def check_value(self, value):
        """
        Check if the motor is disabled
        Check if the value is within the soft limits of the motor.

        Raises
        ------
        Exception
            If the motor is passed any motion command when disabled
        ValueError
            When the provided value is outside the range of the low
            and high limits
        """
        # First check that the user has returned a valid EPICS value. It will
        # not consult the limits of the PV itself because limits=False
        super().check_value(value)

        # Find the value for the disabled attribute
        if self.disabled.value == 1:
            raise Exception("Motor is not enabled. Motion requests " "ignored")

        if self.motor_spg.value in [0, 'Stop']:
            raise Exception("Motor is stopped.  Motion requests "
                            "ignored until motor is set to 'Go'")

        if self.motor_spg.value in [1, 'Pause']:
            raise Exception("Motor is paused.  If a move is set, motion "
                            "will resume when motor is set to 'Go'")

        # Find the soft limit values from EPICS records and check that this
        # command will be accepted by the motor
        low_limit, high_limit = self.limits

        if not (low_limit <= value <= high_limit):
            raise LimitError("Value {} outside of range: [{}, {}]".format(
                value, low_limit, high_limit))
Beispiel #7
0
def xy_fly(
    scan_title,
    *,
    beamline_operator,
    dwell_time,
    xstart,
    xstop,
    xstep_size,
    ystart,
    ystop,
    ystep_size=None,
    xspress3=None,
):
    """Do a x-y fly scan.

    The x-motor is the 'fast' direction.

    Parameters
    ----------
    scan_title : str
       A name for the scan.

    beamline_operator : str
       The individual responsible for this scan. Appears in output directory path.

    dwell_time : float
       Target time is s on each pixel

    xstart, xstop : float
       The start and stop values in the fast direction in mm

    xstep_size :
        xstep_size is step of x movement

    ystart, ystop : float
       The start and stop values in the slow direction in mm

    ystep_size :
        ystep_size use xstep_size if it isn't passed in

    scan_title : str
       Title of scan, required.
    """
    xy_fly_stage = xy_stage
    _validate_motor_limits(xy_fly_stage.x, xstart, xstop, "x")
    _validate_motor_limits(xy_fly_stage.y, ystart, ystop, "y")
    ystep_size = ystep_size if ystep_size is not None else xstep_size
    assert dwell_time > 0, f"dwell_time ({dwell_time}) must be more than 0"
    assert xstep_size > 0, f"xstep_size ({xstep_size}) must be more than 0"
    assert ystep_size > 0, f"ystep_size ({ystep_size}) must be more than 0"
    ret = yield from bps.read(xy_fly_stage.x.mres)  # (in mm)
    xmres = ret[
        xy_fly_stage.x.mres.name]["value"] if ret is not None else 0.0003125

    ret = yield from bps.read(xy_fly_stage.y.mres)  # (in mm)
    ymres = ret[
        xy_fly_stage.y.mres.name]["value"] if ret is not None else 0.0003125

    prescale = int(np.floor((xstep_size / (5 * xmres))))
    a_xstep_size = prescale * (5 * xmres)

    a_ystep_size = int(np.floor((ystep_size / (ymres)))) * ymres

    num_xpixels = int(np.floor((xstop - xstart) / a_xstep_size))
    num_ypixels = int(np.floor((ystop - ystart) / a_ystep_size))

    yield from bps.mv(
        x_centers,
        a_xstep_size / 2 + xstart + np.arange(num_xpixels) * a_xstep_size)

    # SRX original roi_key = getattr(xs.channel1.rois, roi_name).value.name
    roi_livegrid_key = xs.channel1.rois.roi01.value.name
    fig = plt.figure("xs")
    fig.clf()
    roi_livegrid = LiveGrid(
        (num_ypixels + 1, num_xpixels + 1),
        roi_livegrid_key,
        clim=None,
        cmap="inferno",
        xlabel="x (mm)",
        ylabel="y (mm)",
        extent=[xstart, xstop, ystart, ystop],
        x_positive="right",
        y_positive="down",
        ax=fig.gca(),
    )

    flyspeed = a_xstep_size / dwell_time  # this is in mm/s

    try:
        xy_fly_stage.x.velocity.check_value(flyspeed)
    except LimitError as e:
        raise LimitError(f"You requested a range of {xstop - xstart} with "
                         f"{num_xpixels} pixels and a dwell time of "
                         f"{dwell_time}.  This requires a "
                         f"motor velocity of {flyspeed} which "
                         "is out of range.") from e

    # set up delta-tau trigger to fast motor
    for v in ["p1600=0", "p1607=1", "p1600=1"]:
        yield from bps.mv(dtt, v)
        yield from bps.sleep(0.1)

    # TODO make this a message?
    sclr.set_mode("flying")
    # poke the struck settings
    yield from bps.mv(sclr.mcas.prescale, prescale)
    yield from bps.mv(sclr.mcas.nuse, num_xpixels)
    if xspress3 is not None:
        yield from bps.mv(xs.external_trig, True)
        yield from bps.mv(xspress3.total_points, num_xpixels)
        yield from bps.mv(xspress3.hdf5.num_capture, num_xpixels)
        yield from bps.mv(xspress3.settings.num_images, num_xpixels)

    @bpp.reset_positions_decorator([xy_fly_stage.x, xy_fly_stage.y])
    @bpp.subs_decorator({"all": [roi_livegrid]})
    @bpp.monitor_during_decorator([xs.channel1.rois.roi01.value])
    @bpp.stage_decorator([sclr])
    @bpp.baseline_decorator([mono, xy_fly_stage])
    # TODO put is other meta data
    @bpp.run_decorator(
        md={
            "scan_title": scan_title,
            "operator": beamline_operator,
            "user_input": {
                "dwell_time": dwell_time,
                "xstart": xstart,
                "xstop": xstop,
                "xstep_size": xstep_size,
                "ystart": ystart,
                "ystep_size": ystep_size,
            },
            "derived_input": {
                "actual_ystep_size": a_ystep_size,
                "actual_xstep_size": a_xstep_size,
                "fly_velocity": flyspeed,
                "xpixels": num_xpixels,
                "ypixels": num_ypixels,
                "prescale": prescale,
            },
        })
    def fly_body():

        yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, ystart)

        @bpp.stage_decorator([x for x in [xspress3] if x is not None])
        def fly_row():
            # go to start of row
            target_y = ystart + y * a_ystep_size
            yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, target_y)
            yield from bps.mv(y_centers,
                              np.ones(num_xpixels) *
                              target_y)  # set the fly speed

            ret = yield from bps.read(xy_fly_stage.z.user_readback)  # (in mm)
            zpos = (ret[xy_fly_stage.z.user_readback.name]["value"]
                    if ret is not None else 0)
            yield from bps.mov(z_centers, np.ones(num_xpixels) * zpos)

            yield from bps.mv(xy_fly_stage.x.velocity, flyspeed)

            yield from bps.trigger_and_read([xy_fly_stage], name="row_ends")

            for v in ["p1600=0", "p1600=1"]:
                yield from bps.mv(dtt, v)
                yield from bps.sleep(0.1)

            # arm the struck
            yield from bps.trigger(sclr, group=f"fly_row_{y}")
            # maybe start the xspress3
            if xspress3 is not None:
                yield from bps.trigger(xspress3, group=f"fly_row_{y}")
            yield from bps.sleep(0.1)
            # fly the motor
            yield from bps.abs_set(xy_fly_stage.x,
                                   xstop + a_xstep_size,
                                   group=f"fly_row_{y}")
            yield from bps.wait(group=f"fly_row_{y}")

            yield from bps.trigger_and_read([xy_fly_stage], name="row_ends")
            yield from bps.mv(xy_fly_stage.x.velocity, 5.0)
            yield from bps.sleep(0.1)
            # read and save the struck
            yield from bps.create(name="primary")
            #
            yield from bps.read(sclr)
            yield from bps.read(mono)
            yield from bps.read(x_centers)
            yield from bps.read(y_centers)
            yield from bps.read(z_centers)
            yield from bps.read(xy_fly_stage.y)
            yield from bps.read(xy_fly_stage.z)
            # and maybe the xspress3
            if xspress3 is not None:
                yield from bps.read(xspress3)
            yield from bps.save()

        for y in range(num_ypixels):
            if xspress3 is not None:
                yield from bps.mv(xspress3.fly_next, True)

            yield from fly_row()

    yield from fly_body()

    # save the start document to a file for the benefit of the user
    start = db[-1].start
    dt = datetime.datetime.fromtimestamp(start["time"])
    filepath = os.path.expanduser(
        f"~/Users/Data/{start['operator']}/{dt.date().isoformat()}/xy_fly/"
        f"{start['scan_title']}-{start['scan_id']}-{start['operator']}-{dt.time().isoformat()}.log"
    )
    os.makedirs(os.path.dirname(filepath), exist_ok=True)
    with open(filepath, "wt") as output_file:
        output_file.write(pprint.pformat(start))
def xy_fly(scan_title,
           *,
           dwell_time,
           xstart,
           xstop,
           xstep_size,
           ystart,
           ystop,
           ystep_size=None,
           xspress3=None):
    """Do a x-y fly scan.

    The x-motor is the 'fast' direction.

    Parameters
    ----------
    dwell_time : float
       Target time is s on each pixel

    xstart, xstop : float
       The start and stop values in the fast direction in mm

    xstep_size :
        xstep_size is step of x movement

    ystart, ystop : float
       The start and stop values in the slow direction in mm

    ystep_size :
        ystep_size use xstep_size if it isn't passed in

    scan_title : str
       Title of scan, required.
    """
    xy_fly_stage = xy_stage
    _validate_motor_limits(xy_fly_stage.x, xstart, xstop, 'x')
    _validate_motor_limits(xy_fly_stage.y, ystart, ystop, 'y')
    ystep_size = ystep_size if ystep_size is not None else xstep_size
    assert dwell_time > 0, f'dwell_time ({dwell_time}) must be more than 0'
    assert xstep_size > 0, f'xstep_size ({xstep_size}) must be more than 0'
    assert ystep_size > 0, f'ystep_size ({ystep_size}) must be more than 0'
    ret = yield from bps.read(xy_fly_stage.x.mres)  # (in mm)
    xmres = (ret[xy_fly_stage.x.mres.name]['value']
             if ret is not None else .0003125)

    ret = yield from bps.read(xy_fly_stage.y.mres)  # (in mm)
    ymres = (ret[xy_fly_stage.y.mres.name]['value']
             if ret is not None else .0003125)

    prescale = int(np.floor((xstep_size / (5 * xmres))))
    a_xstep_size = prescale * (5 * xmres)

    a_ystep_size = int(np.floor((ystep_size / (ymres)))) * ymres

    num_xpixels = int(np.floor((xstop - xstart) / a_xstep_size))
    num_ypixels = int(np.floor((ystop - ystart) / a_ystep_size))

    flyspeed = a_xstep_size / dwell_time  # this is in mm/s

    try:
        xy_fly_stage.x.velocity.check_value(flyspeed)
    except LimitError as e:
        raise LimitError(f'You requested a range of {xstop - xstart} with '
                         f'{num_xpixels} pixels and a dwell time of '
                         f'{dwell_time}.  This requires a '
                         f'motor velocity of {flyspeed} which '
                         'is out of range.') from e

    # set up delta-tau trigger to fast motor
    for v in ['p1600=0', 'p1607=1', 'p1600=1']:
        yield from bps.mv(dtt, v)
        yield from bps.sleep(0.1)

    # TODO make this a message?
    sclr.set_mode('flying')

    # poke the struck settings
    yield from bps.mv(sclr.mcas.prescale, prescale)
    yield from bps.mv(sclr.mcas.nuse, num_xpixels)

    if xspress3 is not None:
        yield from bps.mov(xs.external_trig, True)
        yield from mov(xspress3.total_points, num_xpixels)
        yield from mov(xspress3.hdf5.num_capture, num_xpixels)
        yield from mov(xspress3.settings.num_images, num_xpixels)

    @bpp.reset_positions_decorator([xy_fly_stage.x, xy_fly_stage.y])
    @bpp.stage_decorator([sclr])
    @bpp.baseline_decorator([mono, xy_fly_stage])
    # TODO put is other meta data
    @bpp.run_decorator(md={'scan_title': scan_title})
    def fly_body():

        yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, ystart)

        @bpp.stage_decorator([x for x in [xspress3] if x is not None])
        def fly_row():
            # go to start of row
            yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y,
                              ystart + y * ystep_size)

            # set the fly speed
            yield from bps.mv(xy_fly_stage.x.velocity, flyspeed)

            yield from bps.trigger_and_read([xy_fly_stage], name='row_ends')

            for v in ['p1600=0', 'p1600=1']:
                yield from bps.mv(dtt, v)
                yield from bps.sleep(0.1)

            # arm the struck
            yield from bps.trigger(sclr, group=f'fly_row_{y}')
            # maybe start the xspress3
            if xspress3 is not None:
                yield from bps.trigger(xspress3, group=f'fly_row_{y}')
            yield from bps.sleep(0.1)
            # fly the motor
            yield from bps.abs_set(xy_fly_stage.x,
                                   xstop + a_xstep_size,
                                   group=f'fly_row_{y}')
            yield from bps.wait(group=f'fly_row_{y}')

            yield from bps.trigger_and_read([xy_fly_stage], name='row_ends')

            yield from bps.mv(xy_fly_stage.x.velocity, 5.0)
            yield from bps.sleep(.1)
            # read and save the struck
            yield from bps.create(name='primary')
            yield from bps.read(sclr)
            # and maybe the xspress3
            if xspress3 is not None:
                yield from bps.read(xspress3)
            yield from bps.save()

        for y in range(num_ypixels):
            if xspress3 is not None:
                yield from bps.mov(xspress3.fly_next, True)

            yield from fly_row()

    yield from fly_body()