Example #1
0
def test_mutator_exceptions():
    handled = False

    def base_plan():
        yield Msg('foo')
        yield Msg('bar')

    def failing_plan():
        nonlocal handled
        handled = False
        yield Msg('pre')
        try:
            yield Msg('FAIL')
        except EchoException:
            handled = True
            raise

    def test_mutator(msg):
        if msg.command == 'bar':
            return (failing_plan(), single_message_gen(Msg('foo')))
        return None, None

    # check generator exit behavior
    plan = plan_mutator(base_plan(), test_mutator)
    next(plan)
    plan.close()

    # check exception fall through
    plan = plan_mutator(base_plan(), test_mutator)
    with pytest.raises(EchoException):
        EchoRE(plan, debug=True)
    assert handled
Example #2
0
def test_mutator_exceptions():
    handled = False

    def base_plan():
        yield Msg('foo')
        yield Msg('bar')

    def failing_plan():
        nonlocal handled
        handled = False
        yield Msg('pre')
        try:
            yield Msg('FAIL')
        except EchoException:
            handled = True
            raise

    def test_mutator(msg):
        if msg.command == 'bar':
            return (
                failing_plan(),
                single_message_gen(Msg('foo'))
            )
        return None, None

    # check generator exit behavior
    plan = plan_mutator(base_plan(), test_mutator)
    next(plan)
    plan.close()

    # check exception fall through
    plan = plan_mutator(base_plan(), test_mutator)
    with pytest.raises(EchoException):
        EchoRE(plan, debug=True)
    assert handled
Example #3
0
def test_simple_mutator():
    _mut_active = True
    pre_count = 3
    post_count = 5
    pre_cmd = 'pre'
    post_cmd = 'post'

    def test_mutator(msg):
        nonlocal _mut_active
        if _mut_active:
            _mut_active = False

            return (pchain(echo_plan(num=pre_count, command=pre_cmd),
                           single_message_gen(msg)),
                    echo_plan(num=post_count, command=post_cmd))
        return None, None

    num = 5
    cmd = 'echo'
    plan = plan_mutator(echo_plan(command=cmd, num=num), test_mutator)
    msgs = EchoRE(plan)
    total = num + pre_count + post_count
    cmd_sq = ([pre_cmd] * pre_count + [cmd] + [post_cmd] * post_count + [cmd] *
              (num - 1))
    _verify_msg_seq(msgs,
                    m_len=total,
                    cmd_sq=cmd_sq,
                    args_sq=[()] * total,
                    kwargs_sq=[{}] * total)
Example #4
0
def test_plan_mutator_exception_propogation():
    class ExpectedException(Exception):
        pass

    num = 5
    cmd1 = 'echo1'
    cmd2 = 'echo2'

    def bad_tail():
        yield Msg('one_tail', None)
        raise ExpectedException('this is a test')

    def sarfing_plan():
        try:
            yield from echo_plan(command=cmd1, num=num)
        except ExpectedException:
            print('CAUGHT IT')

    _mut_active = True

    def test_mutator(msg):
        nonlocal _mut_active
        if _mut_active:
            _mut_active = False

            return (pchain(echo_plan(num=2, command=cmd2),
                           single_message_gen(msg)), bad_tail())
        return None, None

    plan = plan_mutator(sarfing_plan(), test_mutator)
    EchoRE(plan, debug=True)
Example #5
0
def test_exception_in_pre_with_tail():
    class SnowFlake(Exception):
        ...

    def bad_pre():
        yield Msg('pre_bad', None)
        raise SnowFlake('this one')

    def good_post():
        yield Msg('good_post', None)

    def test_mutator(msg):
        if msg.command == 'TARGET':
            return bad_pre(), good_post()

        return None, None

    def testing_plan():
        yield Msg('a', None)
        yield Msg('b', None)
        try:
            yield Msg('TARGET', None)
        except SnowFlake:
            pass
        yield Msg('b', None)
        yield Msg('a', None)

    plan = plan_mutator(testing_plan(), test_mutator)
    msgs = EchoRE(plan, debug=True)
    _verify_msg_seq(msgs,
                    m_len=5,
                    cmd_sq=['a', 'b', 'pre_bad', 'b', 'a'],
                    args_sq=[()] * 5,
                    kwargs_sq=[{}] * 5)
Example #6
0
def test_plan_mutator_returns():
    def testing_plan():
        yield Msg('a', None)
        yield Msg('TARGET', None)
        yield Msg('b', None)

        return 'foobar'

    def outer_plan(pln):
        ret = (yield from pln)
        assert ret == 'foobar'
        return ret

    def tail_plan():
        yield Msg('A', None)
        return 'baz'

    def test_mutator(msg):
        def pre_plan():
            yield Msg('pre', None)
            yield msg

        if msg.command == 'TARGET':
            return pre_plan(), tail_plan()

        return None, None

    plan = plan_mutator(testing_plan(), test_mutator)
    msgs = EchoRE(plan)
    _verify_msg_seq(msgs,
                    m_len=5,
                    cmd_sq=['a', 'pre', 'TARGET', 'A', 'b'],
                    args_sq=[()] * 5,
                    kwargs_sq=[{}] * 5)
Example #7
0
def test_plan_mutator_returns():

    def testing_plan():
        yield Msg('a', None)
        yield Msg('TARGET', None)
        yield Msg('b', None)

        return 'foobar'

    def outer_plan(pln):
        ret = (yield from pln)
        assert ret == 'foobar'
        return ret

    def tail_plan():
        yield Msg('A', None)
        return 'baz'

    def test_mutator(msg):
        def pre_plan():
            yield Msg('pre', None)
            yield msg

        if msg.command == 'TARGET':
            return pre_plan(), tail_plan()

        return None, None

    plan = plan_mutator(testing_plan(), test_mutator)
    msgs = EchoRE(plan)
    _verify_msg_seq(msgs, m_len=5,
                    cmd_sq=['a', 'pre', 'TARGET', 'A', 'b'],
                    args_sq=[()]*5,
                    kwargs_sq=[{}]*5)
Example #8
0
def test_exception_in_pre_with_tail():
    class SnowFlake(Exception):
        ...

    def bad_pre():
        yield Msg('pre_bad', None)
        raise SnowFlake('this one')

    def good_post():
        yield Msg('good_post', None)

    def test_mutator(msg):
        if msg.command == 'TARGET':
            return bad_pre(), good_post()

        return None, None

    def testing_plan():
        yield Msg('a', None)
        yield Msg('b', None)
        try:
            yield Msg('TARGET', None)
        except SnowFlake:
            pass
        yield Msg('b', None)
        yield Msg('a', None)

    plan = plan_mutator(testing_plan(), test_mutator)
    msgs = EchoRE(plan, debug=True)
    _verify_msg_seq(msgs, m_len=5,
                    cmd_sq=['a', 'b', 'pre_bad', 'b', 'a'],
                    args_sq=[()]*5,
                    kwargs_sq=[{}]*5)
Example #9
0
def test_plan_mutator_exception_propogation():
    class ExpectedException(Exception):
        pass

    num = 5
    cmd1 = 'echo1'
    cmd2 = 'echo2'

    def bad_tail():
        yield Msg('one_tail', None)
        raise ExpectedException('this is a test')

    def sarfing_plan():
        try:
            yield from echo_plan(command=cmd1, num=num)
        except ExpectedException:
            print('CAUGHT IT')

    _mut_active = True

    def test_mutator(msg):
        nonlocal _mut_active
        if _mut_active:
            _mut_active = False

            return (pchain(echo_plan(num=2, command=cmd2),
                           single_message_gen(msg)),
                    bad_tail())
        return None, None

    plan = plan_mutator(sarfing_plan(), test_mutator)
    EchoRE(plan, debug=True)
Example #10
0
def test_simple_mutator():
    _mut_active = True
    pre_count = 3
    post_count = 5
    pre_cmd = 'pre'
    post_cmd = 'post'

    def test_mutator(msg):
        nonlocal _mut_active
        if _mut_active:
            _mut_active = False

            return (pchain(echo_plan(num=pre_count, command=pre_cmd),
                           single_message_gen(msg)),
                    echo_plan(num=post_count, command=post_cmd))
        return None, None

    num = 5
    cmd = 'echo'
    plan = plan_mutator(echo_plan(command=cmd, num=num), test_mutator)
    msgs = EchoRE(plan)
    total = num + pre_count + post_count
    cmd_sq = ([pre_cmd]*pre_count +
              [cmd] +
              [post_cmd]*post_count +
              [cmd]*(num-1))
    _verify_msg_seq(msgs, m_len=total,
                    cmd_sq=cmd_sq,
                    args_sq=[()]*total,
                    kwargs_sq=[{}]*total)
Example #11
0
def periodic_dark(plan):
    """
    a plan wrapper that takes a plan and inserts `take_dark`

    The `take_dark` plan is inserted on the fly before the beginning of
    any new run after a period of time defined by glbl['dk_window'] has passed.
    """
    need_dark = True

    def insert_take_dark(msg):
        now = time.time()
        nonlocal need_dark
        qualified_dark_uid = _validate_dark(expire_time=glbl["dk_window"])
        area_det = xpd_configuration["area_det"]

        if (not need_dark) and (not qualified_dark_uid):
            need_dark = True
        if (need_dark and (not qualified_dark_uid)
                and msg.command == "open_run"
                and ("dark_frame" not in msg.kwargs)):
            # We are about to start a new 'run' (e.g., a count or a scan).
            # Insert a dark frame run first.
            need_dark = False
            # Annoying detail: the detector was probably already staged.
            # Unstage it (if it wasn't staged, nothing will happen) and
            # then take_dark() and then re-stage it.
            return (
                bpp.pchain(
                    bps.unstage(area_det),
                    take_dark(),
                    bps.stage(area_det),
                    bpp.single_gen(msg),
                    bps.abs_set(
                        xpd_configuration.get("shutter"),
                        XPD_SHUTTER_CONF["open"],
                        wait=True,
                    ),
                    bps.sleep(glbl['shutter_sleep']),
                ),
                None,
            )
        elif msg.command == "open_run" and "dark_frame" not in msg.kwargs:
            return (
                bpp.pchain(
                    bpp.single_gen(msg),
                    bps.abs_set(
                        xpd_configuration.get("shutter"),
                        XPD_SHUTTER_CONF["open"],
                        wait=True,
                    ), bps.sleep(glbl['shutter_sleep'])),
                None,
            )
        else:
            # do nothing if (not need_dark)
            return None, None

    return (yield from bpp.plan_mutator(plan, insert_take_dark))
Example #12
0
def test_base_exception():
    class SnowFlake(Exception):
        ...

    def null_mutator(msg):
        return None, None

    def test_plan():
        yield Msg('a', None)
        raise SnowFlake('this one')

    pln = plan_mutator(test_plan(), null_mutator)

    try:
        EchoRE(pln)
    except SnowFlake as ex:
        assert ex.args[0] == 'this one'
Example #13
0
def test_base_exception():
    class SnowFlake(Exception):
        ...

    def null_mutator(msg):
        return None, None

    def test_plan():
        yield Msg('a', None)
        raise SnowFlake('this one')

    pln = plan_mutator(test_plan(), null_mutator)

    try:
        EchoRE(pln)
    except SnowFlake as ex:
        assert ex.args[0] == 'this one'
Example #14
0
def test_insert_after():
    def target():
        yield Msg('a', None)
        ret = yield Msg('TARGET', None)
        yield Msg('b', None)
        assert ret is not None
        assert ret.command == 'TARGET'
        return ret

    def insert_after(msg):
        if msg.command == 'TARGET':

            def post():
                yield Msg('post', None)

            return None, post()
        else:
            return None, None

    EchoRE(plan_mutator(target(), insert_after))
Example #15
0
def test_insert_after():

    def target():
        yield Msg('a', None)
        ret = yield Msg('TARGET', None)
        yield Msg('b', None)
        assert ret is not None
        assert ret.command == 'TARGET'
        return ret

    def insert_after(msg):
        if msg.command == 'TARGET':
            def post():
                yield Msg('post', None)

            return None, post()
        else:
            return None, None

    ret = EchoRE(plan_mutator(target(), insert_after))
Example #16
0
def test_insert_before():
    def target():
        yield Msg('a', None)
        ret = yield Msg('TARGET', None)
        yield Msg('b', None)
        assert ret.command == 'TARGET'
        return ret
        return ret

    def insert_before(msg):
        if msg.command == 'TARGET':

            def pre():
                yield Msg('pre', None)
                ret = yield msg
                assert ret is not None
                assert ret.command == 'TARGET'
                return ret

            return pre(), None
        else:
            return None, None

    EchoRE(plan_mutator(target(), insert_before))
Example #17
0
def test_insert_before():

    def target():
        yield Msg('a', None)
        ret = yield Msg('TARGET', None)
        yield Msg('b', None)
        assert ret.command == 'TARGET'
        return ret
        return ret

    def insert_before(msg):
        if msg.command == 'TARGET':
            def pre():
                yield Msg('pre', None)
                ret = yield msg
                assert ret is not None
                assert ret.command == 'TARGET'
                return ret

            return pre(), None
        else:
            return None, None

    ret = EchoRE(plan_mutator(target(), insert_before))
Example #18
0
def tseries_gas_plan(detectors,
                     gas_in,
                     exp_time,
                     delay=1,
                     num_exp=1,
                     rga_masses=default_mass_list):
    """
    tseries-type scan with rga gas reading

    Parameters
    ----------
    detectors: list
        List of detectors will be triggered and recored.
    gas_in : string
        e.g., 'He', default is 'He'
        These gas must be in `gas.gas_list` but they may be in any order.
    exp_time : float, optional
        exposure time in seconds
    num_exp : integer, optional
        number of exposures
    delay : float, optional
        delay between exposures in seconds
    rga_masses: list, optional
        a list of rga masses appearing in a live table

    Example:
    >>> plan = tseries_gas_plan([pe1c, rga], 'He', 5, 10, 2)
    >>> xrun(<sample ind>, plan)
    """
    ## configure hints on gas device
    configure_gas_mass_hint(rga_masses)

    ## switch gas
    yield from set_gas(gas_in)

    # configure the exposure time first
    (num_frame, acq_time,
     computed_exposure) = yield from _configure_area_det(exp_time)
    real_delay = max(0, delay - computed_exposure)
    period = max(computed_exposure, real_delay + computed_exposure)
    print('INFO: requested delay = {}s  -> computed delay = {}s'.format(
        delay, real_delay))
    print('INFO: nominal period (neglecting readout overheads) of {} s'.format(
        period))
    xpdacq_md = {
        'sp_time_per_frame': acq_time,
        'sp_num_frames': num_frame,
        'sp_requested_exposure': exp_time,
        'sp_computed_exposure': computed_exposure,
        'sp_plan_name': 'tseries'
    }

    plan = bp.count(detectors, num_exp, delay, md=xpdacq_md)
    plan = bpp.subs_wrapper(plan, LiveTable(detectors))

    def inner_shutter_control(msg):
        if msg.command == 'trigger':

            def inner():
                yield from open_shutter_stub()
                yield msg

            return inner(), None
        elif msg.command == 'save':
            return None, close_shutter_stub()
        else:
            return None, None

    plan = bpp.plan_mutator(plan, inner_shutter_control)
    yield from plan
Example #19
0
def flash_step(VIT_table,
               total_exposure,
               md,
               *,
               dets=None,
               delay=1,
               mm_mode='Current',
               per_step=bps.trigger_and_read,
               control_shutter=True):
    """
    Run a step-series of current/voltage.

    The current/voltage profile will look something like: ┌┐_┌┐_┌┐_

    Parameters
    ----------
    VIT_table : pd.DataFrame
       The required columns are {"I", "V", "t"} which are
       the current, voltage, and hold time respectively.

    total_exposure : float
        The total exposure time for the detector.

        This is set via _configure_area_detector which is
        implicitly coupled to xpd_configuration['area_det']

    md : dict
        The metadata to put into the runstart.  Will have
        some defaults added

    dets : List[OphydObj], optional
        The detectors to trigger at each point.

        If None, defaults to::

          [xpd_configuration['area_det']]

    delay : float, optional
        The time lag between subsequent data acquisition

    mm_mode : {'Current', 'Voltage'}, optional
        The thing to measure from the Keithly multimeter.

    per_step : Callable[List[OphydObj], Optional[str]] -> Generator[Msg], optional
        The inner-most data acquisition loop.

        This plan will be repeated as many times as possible (with
        the target *delay* between starting the plan.

        If the plan take longer than *delay* to run it will
        immediately be restarted.

    control_shutter : bool, optional
        If the plan should try to open and close the shutter

        defaults to True
    """
    if total_exposure > delay:
        raise RuntimeError(f"You asked for total_exposure={total_exposure} "
                           f"with a delay of delay={delay} which is less ")
    if dets is None:
        dets = [xpd_configuration['area_det']]

    all_dets = dets + [flash_power, eurotherm]
    req_cols = ['I', 'V', 't']
    if not all(k in VIT_table for k in req_cols):
        raise ValueError(f"input table must have {req_cols}")

    monitor_during = yield from _setup_mm(mm_mode)

    VIT_table = pd.DataFrame(VIT_table)
    # TODO put in default meta-data
    md.setdefault('hints', {})
    md['hints'].setdefault('dimensions', [(('time', ), 'primary')])
    md['plan_name'] = 'flash_step'
    md['plan_args'] = {
        'VIT_table': {k: v.values
                      for k, v in VIT_table.items()},
        'delay': delay,
        'total_exposure': total_exposure
    }
    md['detectors'] = [det.name for det in dets]

    @subs_decorator(bec)
    # paranoia to be very sure we turn the PSU off
    @finalize_decorator(lambda: bps.mov(flash_power.enabled, 0))
    # this arms the detectors
    @stage_decorator(all_dets)
    # this sets up the monitors of the multi-meter
    @monitor_during_decorator(monitor_during)
    # this opens the run and puts the meta-data in it
    @run_decorator(md=md)
    def flash_step_field_inner():
        # set everything to zero at the top
        yield from bps.mv(flash_power.current_sp, 0, flash_power.voltage_sp, 0)
        # put in "Duty Cycle" mode so current changes immediately
        yield from bps.mv(flash_power.mode, 'Duty-Cycle')

        # take a measurement on the way in
        yield from per_step(all_dets, 'primary')

        # turn it on!
        yield from bps.mv(flash_power.enabled, 1)

        last_I = last_V = np.nan
        for _, row in VIT_table.iterrows():
            tau = row['t']
            exposure_count = int(max(1, tau // delay))
            print('initial per step call')
            yield from per_step(all_dets, 'primary')
            next_I = row['I']
            next_V = row['V']
            print('set IV')
            if next_I != last_I:
                yield from bps.mv(flash_power.current_sp, next_I)
                last_I = next_I
            if next_V != last_V:
                yield from bps.mv(flash_power.voltage_sp, next_V)
                last_V = next_V
            print('finsh setting IV')
            deadline = time.monotonic() + tau
            yield from _inner_loop(all_dets, exposure_count, delay, deadline,
                                   per_step, 'primary')
            print('finished inner loop call')

        print('final shot')
        # take a measurement on the way out
        yield from per_step(all_dets, 'primary')

        print('turning off')
        # turn it off!
        # there are several other places we turn this off, but better safe
        yield from bps.mv(flash_power.enabled, 0)

    # HACK to get at global state
    yield from _configure_area_det(total_exposure)
    plan = flash_step_field_inner()
    if control_shutter:
        return (yield from bpp.plan_mutator(plan, inner_shutter_control))
    else:
        return (yield from plan)
Example #20
0
def ttseries(dets, temp_setpoint, exposure, delay, num, auto_shutter=True, manual_set=False):
    """
    Set a target temperature. Make time series scan with area detector during the ramping and holding. Since
    abs_set is used, please do not set the temperature through CSstudio when the plan is running.

    Parameters
    ----------
    dets : list
        list of 'readable' objects. default to area detector
        linked to xpdAcq.
    temp_setpoint: float
        A temperature set point. If None, do not set the temperature.
    exposure : float
        The exposure time at each reading from area detector in seconds
    delay : float
        The period of time between the starting points of two consecutive readings from area detector in seconds
    num : int
        The total number of readings
    auto_shutter: bool
        Option on whether delegates shutter control to ``xpdAcq``. If True,
        following behavior will take place:

        `` open shutter - collect data - close shutter ``

        To make shutter stay open during ``tseries`` scan,
        pass ``False`` to this argument. See ``Notes`` below for more
        detailed information.
    manual_set : bool
        Option on whether to manual set the temperature set point outside the plan. If True, no temperature
        will be set in plan.

    Examples
    --------
    To see which area detector and shutter will be used, type the
    following commands:

        >>> xpd_configuration['area_det']
        >>> xpd_configuration['shutter']
        >>> xpd_configuration['temp_controller']

    To override default behavior and keep the shutter open throughout
    scan, create ScanPlan with following syntax:

        >>> ScanPlan(bt, ttseries, 300, 10, 20, 10, False)
    """
    area_det = xpd_configuration["area_det"]
    temp_controller = xpd_configuration["temp_controller"]
    md = {
        "sp_type": "ttseries",
        "sp_uid": str(uuid.uuid4()),
        "sp_plan_name": "ttseries",
        "temp_setpoint": temp_setpoint
    }
    # calculate number of frames
    exposure_md = tl.calc_exposure(area_det, exposure)
    md.update(exposure_md)
    # calculate the real delay and period
    computed_exposure = exposure_md.get("sp_computed_exposure")
    delay_md = tl.calc_delay(delay, computed_exposure, num)
    md.update(delay_md)
    # make the count plan
    real_delay = delay_md.get('sp_computed_delay')
    plan = count([area_det, temp_controller], num, real_delay, md=md)
    plan = subs_wrapper(plan, LiveTable([temp_controller]))
    # open and close the shutter for each count
    if auto_shutter:
        plan = plan_mutator(plan, tl.inner_shutter_control)
    # yield messages
    yield from tl.configure_area_det(area_det, md)
    if not manual_set:
        yield from abs_set(temp_controller, temp_setpoint, wait=False)
    yield from plan
Example #21
0
 def daq_step_plan():
     return (yield from bpp.plan_mutator(plan, daq_mutator))
Example #22
0
def tseries(dets, exposure, delay, num, auto_shutter=True):
    """
    time series scan with area detector.

    Parameters
    ----------
    dets : list
        list of 'readable' objects. default to area detector
        linked to xpdAcq.
    exposure : float
        exposure time at each reading from area detector in seconds
    delay : float
        delay between two consecutive readings from area detector in seconds
    num : int
        total number of readings
    auto_shutter: bool, optional
        Option on whether delegates shutter control to ``xpdAcq``. If True,
        following behavior will take place:

        `` open shutter - collect data - close shutter ``

        To make shutter stay open during ``tseries`` scan,
        pass ``False`` to this argument. See ``Notes`` below for more
        detailed information.

    Notes
    -----
    To see which area detector and shutter will be used, type the
    following commands:

        >>> xpd_configuration['area_det']
        >>> xpd_configuration['shutter']

    To override default behavior and keep the shutter open throughout
    scan , create ScanPlan with following syntax:

        >>> ScanPlan(bt, tseries, 10, 5, 10, False)
    """

    pe1c, = dets
    md = {}
    # setting up area_detector
    area_det = xpd_configuration["area_det"]
    (num_frame, acq_time, computed_exposure) = yield from _configure_area_det(
        exposure
    )
    real_delay = max(0, delay - computed_exposure)
    period = max(computed_exposure, real_delay + computed_exposure)
    print(
        "INFO: requested delay = {}s  -> computed delay = {}s".format(
            delay, real_delay
        )
    )
    print(
        "INFO: nominal period (neglecting readout overheads) of {} s".format(
            period
        )
    )
    # update md
    _md = ChainMap(
        md,
        {
            "sp_time_per_frame": acq_time,
            "sp_num_frames": num_frame,
            "sp_requested_exposure": exposure,
            "sp_computed_exposure": computed_exposure,
            "sp_requested_delay": delay,
            "sp_requested_num": num,
            "sp_type": "tseries",
            # need a name that shows all parameters values
            # 'sp_name': 'tseries_<exposure_time>',
            "sp_uid": str(uuid.uuid4()),
            "sp_plan_name": "tseries",
        },
    )
    plan = bp.count([area_det], num, delay, md=_md)
    plan = bpp.subs_wrapper(plan, LiveTable([]))

    def inner_shutter_control(msg):
        if msg.command == "trigger":

            def inner():
                yield from open_shutter_stub()
                yield msg

            return inner(), None
        elif msg.command == "save":
            return None, close_shutter_stub()
        else:
            return None, None

    if auto_shutter:
        plan = bpp.plan_mutator(plan, inner_shutter_control)
    yield from plan
Example #23
0
def flash_ramp(start_I,
               stop_I,
               ramp_rate,
               voltage,
               total_exposure,
               md,
               *,
               dets=None,
               delay=1,
               mm_mode='Current',
               hold_time=0,
               per_step=bps.trigger_and_read,
               control_shutter=True):
    """
    Run a current ramp

    The current profile will look something like: /|

    Parameters
    ----------
    start_I, stop_I : float
        The start and end points of the current ramp

        In mA

    ramp_rate : float
        The rate of current change.

        In mA/min

    voltage : float
        The voltage limit through the current ramp.

    total_exposure : float
        The total exposure time for the detector.

        This is set via _configure_area_detector which is
        implicitly coupled to xpd_configuration['area_det']

    md : dict
        The metadata to put into the runstart.  Will have
        some defaults added

    dets : List[OphydObj], optional
        The detectors to trigger at each point.

        If None, defaults to::

          [xpd_configuration['area_det']]

    delay : float, optional
        The time lag between subsequent data acquisition

    mm_mode : {'Current', 'Voltage'}, optional
        The thing to measure from the Keithly multimeter.

    hold_time : float, optional
       How long to hold at the top of the ramp, defalts to 0

    per_step : Callable[List[OphydObj], Optional[str]] -> Generator[Msg], optional
        The inner-most data acquisition loop.

        This plan will be repeated as many times as possible (with
        the target *delay* between starting the plan.

        If the plan take longer than *delay* to run it will
        immediately be restarted.

    control_shutter : bool, optional
        If the plan should try to open and close the shutter

        defaults to True
    """
    if dets is None:
        dets = [xpd_configuration['area_det']]
    if total_exposure > delay:
        raise RuntimeError(f"You asked for total_exposure={total_exposure} "
                           f"with a delay of delay={delay} which is less ")
    # mA -> A
    start_I = start_I / 1000
    stop_I = stop_I / 1000
    if stop_I < start_I:
        raise ValueError("IOC can not ramp backwards")
    fudge_factor = 1
    ramp_rate *= fudge_factor
    all_dets = dets + [flash_power, eurotherm]
    monitor_during = yield from _setup_mm(mm_mode)

    expected_time = abs(
        (stop_I - start_I) / (ramp_rate / (fudge_factor * 60 * 1000)))
    exposure_count = int(max(1, expected_time // delay))

    md.setdefault('hints', {})
    md['hints'].setdefault('dimensions', [(('time', ), 'primary')])
    md['plan_name'] = 'flash_ramp'
    md['plan_args'] = {
        'start_I': start_I,
        'stop_I': stop_I,
        'ramp_rate': ramp_rate,
        'voltage': voltage,
        'delay': delay,
        'total_exposure': total_exposure
    }
    md['detectors'] = [det.name for det in dets]

    @subs_decorator(bec)
    # paranoia to be very sure we turn the PSU off
    @finalize_decorator(lambda: bps.mov(flash_power.enabled, 0))
    # this arms the detectors
    @stage_decorator(all_dets)
    # this sets up the monitors of the multi-meter
    @monitor_during_decorator(monitor_during)
    # this opens the run and puts the meta-data in it
    @run_decorator(md=md)
    def flash_ramp_inner():
        # set everything to zero at the top
        yield from bps.mv(flash_power.current_sp, 0, flash_power.voltage_sp, 0,
                          flash_power.ramp_rate, ramp_rate)
        # put in "Duty Cycle" mode so current changes immediately
        yield from bps.mv(flash_power.mode, 'Duty-Cycle')
        # take one shot on the way in
        yield from per_step(all_dets, 'primary')
        # turn it on!
        yield from bps.mv(flash_power.enabled, 1)

        # TODO
        # what voltage limit to start with ?!
        yield from bps.mv(flash_power.current_sp, start_I,
                          flash_power.voltage_sp, voltage)
        # put in "Current Ramp" to start the ramp
        yield from bps.mv(flash_power.mode, 'Current Ramping')
        # set the target to let it go
        gid = short_uid()
        yield from bps.abs_set(flash_power.current_sp, stop_I, group=gid)
        yield from bps.mv(flash_power.voltage_sp, voltage)

        yield from _inner_loop(all_dets,
                               exposure_count,
                               delay,
                               time.monotonic() + expected_time * 1.1,
                               per_step,
                               'primary',
                               done_signal=flash_power.ramp_done)

        if hold_time > 0:
            yield from _inner_loop(all_dets, int(max(1, hold_time // delay)),
                                   delay,
                                   time.monotonic() + hold_time, per_step,
                                   'primary')

        # take one shot on the way out
        yield from per_step(all_dets, 'primary')
        yield from bps.wait(gid)
        # turn it off!
        # there are several other places we turn this off, but better safe
        yield from bps.mv(flash_power.enabled, 0)

    # HACK to get at global state
    yield from _configure_area_det(total_exposure)
    plan = flash_ramp_inner()
    if control_shutter:
        return (yield from bpp.plan_mutator(plan, inner_shutter_control))
    else:
        return (yield from plan)
Example #24
0
def autoplan(bt: Beamtime,
             sample_index,
             plan_index,
             wait_time=30.,
             auto_shutter=False):
    """
    Yield messages to count the predefined measurement plan on the a list of samples on a sample rack. It requires
    the following information to be added for each sample.
        position_x The x position of the sample in mm.
        position_y The y position of the sample in mm.
        wait_time The waiting time between the end of the former plan and the start of the latter plan in second.

    Parameters
    ----------
    bt: Beamtime
        The Beamtime instance that contains the sample information.
    sample_index : List[int]
        A list of the sample index in the BeamTime instance.
    plan_index: List[int]
        A list of the plan index in the BeamTime instance.
    wait_time : float
        Waiting time before conduct plan for each sample in second.
    auto_shutter : bool
        Whether to mutate the plan with inner_shutter_control.

    Yields
    ------
    Msg
        Messages of the plan.

    Examples
    --------
    Add position controller to the xpd_configuration.
        >>> xpd_configuration["posx_controller"] = Grid_X
        >>> xpd_configuration["posy_controller"] = Grid_Y
    Register the scan plan to the beamtime.
        >>> ScanPlan(bt, ct, 30)
    Add the information of 'position_x', 'position_y' and 'wait_time' to the excel and import.
    Automatically conduct the scan plan for sample No.0 and No.1
        >>> plan = autoplan(bt, [0, 1])
        >>> xrun({}, plan)
    """
    posx_controller = xpd_configuration["posx_controller"]
    posy_controller = xpd_configuration["posy_controller"]

    for sample_ind, plan_ind in zip(sample_index, plan_index):
        sample = translate_to_sample(bt, int(sample_ind))
        posx = mg.get_from_sample(sample, "position_x")
        posy = mg.get_from_sample(sample, "position_y")
        count_plan = mg.translate_to_plan(bt, int(plan_ind), sample)
        if auto_shutter:
            count_plan = plan_mutator(count_plan, inner_shutter_control)
        if posx and posy and count_plan:
            yield from checkpoint()
            print(f"INFO: Move to x: {posx}")
            yield from mv(posx_controller, float(posx))
            yield from checkpoint()
            print(f"INFO: Move to y: {posy}")
            yield from mv(posy_controller, float(posy))
            yield from checkpoint()
            yield from wait()
            print(f"INFO: Wait for {wait_time} s")
            yield from sleep(float(wait_time))
            yield from checkpoint()
            yield from count_plan