def test_finalize():
    def plan():
        yield from [Msg('null')]

    def cleanup_plan():
        yield from [Msg('read', det)]

    # wrapper accepts list
    processed_plan = list(finalize_wrapper(plan(), [Msg('read', det)]))
    expected = [Msg('null'), Msg('read', det)]
    assert processed_plan == expected

    # or func that returns list
    def plan():
        yield from [Msg('null')]

    processed_plan = list(finalize_wrapper(plan(), lambda: [Msg('read', det)]))
    expected = [Msg('null'), Msg('read', det)]
    assert processed_plan == expected

    # or generator instance
    def plan():
        yield from [Msg('null')]

    processed_plan = list(finalize_wrapper(plan(), cleanup_plan()))
    expected = [Msg('null'), Msg('read', det)]
    assert processed_plan == expected

    # or generator func
    def plan():
        yield from [Msg('null')]

    processed_plan = list(finalize_wrapper(plan(), cleanup_plan))
    expected = [Msg('null'), Msg('read', det)]
    assert processed_plan == expected

    # decorator accepts generator func
    processed_plan = list(finalize_decorator(cleanup_plan)(plan)())
    expected = [Msg('null'), Msg('read', det)]
    assert processed_plan == expected

    # or func that returns list
    processed_plan = list(
        finalize_decorator(lambda: [Msg('read', det)])(plan)())
    expected = [Msg('null'), Msg('read', det)]
    assert processed_plan == expected

    # decorator does NOT accept list
    with pytest.raises(TypeError):
        list(finalize_decorator([Msg('read', det)])(plan)())

    # nor generator instance
    with pytest.raises(TypeError):
        list(finalize_decorator(cleanup_plan())(plan)())
def test_finalize():
    def plan():
        yield from [Msg("null")]

    def cleanup_plan():
        yield from [Msg("read", det)]

    # wrapper accepts list
    processed_plan = list(finalize_wrapper(plan(), [Msg("read", det)]))
    expected = [Msg("null"), Msg("read", det)]
    assert processed_plan == expected

    # or func that returns list
    def plan():
        yield from [Msg("null")]

    processed_plan = list(finalize_wrapper(plan(), lambda: [Msg("read", det)]))
    expected = [Msg("null"), Msg("read", det)]
    assert processed_plan == expected

    # or generator instance
    def plan():
        yield from [Msg("null")]

    processed_plan = list(finalize_wrapper(plan(), cleanup_plan()))
    expected = [Msg("null"), Msg("read", det)]
    assert processed_plan == expected

    # or generator func
    def plan():
        yield from [Msg("null")]

    processed_plan = list(finalize_wrapper(plan(), cleanup_plan))
    expected = [Msg("null"), Msg("read", det)]
    assert processed_plan == expected

    # decorator accepts generator func
    processed_plan = list(finalize_decorator(cleanup_plan)(plan)())
    expected = [Msg("null"), Msg("read", det)]
    assert processed_plan == expected

    # or func that returns list
    processed_plan = list(finalize_decorator(lambda: [Msg("read", det)])(plan)())
    expected = [Msg("null"), Msg("read", det)]
    assert processed_plan == expected

    # decorator does NOT accept list
    with pytest.raises(TypeError):
        list(finalize_decorator([Msg("read", det)])(plan)())

    # nor generator instance
    with pytest.raises(TypeError):
        list(finalize_decorator(cleanup_plan())(plan)())
Exemple #3
0
def test_sigint_three_hits(fresh_RE, motor_det):
    motor, det = motor_det
    motor._fake_sleep = 0.3
    RE = fresh_RE

    pid = os.getpid()

    def sim_kill(n=1):
        for j in range(n):
            print('KILL')
            os.kill(pid, signal.SIGINT)

    lp = RE.loop
    motor.loop = lp
    lp.call_later(.02, sim_kill, 3)
    lp.call_later(.02, sim_kill, 3)
    lp.call_later(.02, sim_kill, 3)
    start_time = ttime.time()
    RE(
        bp.finalize_wrapper(bp.abs_set(motor, 1, wait=True),
                            bp.abs_set(motor, 0, wait=True)))
    end_time = ttime.time()
    assert end_time - start_time < 0.2  # not enough time for motor to cleanup
    RE.abort()  # now cleanup
    done_cleanup_time = ttime.time()
    assert done_cleanup_time - end_time > 0.3
def test_sigint_manyhits(fresh_RE, motor_det):
    motor, det = motor_det
    motor._fake_sleep = 0.3
    RE = fresh_RE

    pid = os.getpid()

    def sim_kill(n=1):
        for j in range(n):
            print('KILL')
            os.kill(pid, signal.SIGINT)

    lp = RE.loop
    motor.loop = lp
    lp.call_later(.02, sim_kill, 3)
    lp.call_later(.02, sim_kill, 3)
    lp.call_later(.02, sim_kill, 3)
    start_time = ttime.time()
    RE(bp.finalize_wrapper(bp.abs_set(motor, 1, wait=True),
                           bp.abs_set(motor, 0, wait=True)))
    end_time = ttime.time()
    assert end_time - start_time < 0.2  # not enough time for motor to cleanup
    RE.abort()  # now cleanup
    done_cleanup_time = ttime.time()
    assert done_cleanup_time - end_time > 0.3
Exemple #5
0
def test_finialize_pause():
    fail_cmd = 'fail_next'

    def erroring_plan():
        yield Msg(fail_cmd, None)
        raise RuntimeError('saw this coming')

    num = 5
    cmd = 'echo'
    plan = finalize_wrapper(erroring_plan(),
                            echo_plan(command=cmd, num=num),
                            pause_for_debug=True)
    msgs = list()
    try:
        EchoRE(plan, msg_list=msgs)
    except RuntimeError:
        pass

    total = num + 2
    _verify_msg_seq(msgs,
                    m_len=total,
                    cmd_sq=[fail_cmd, 'pause'] + [cmd] * num,
                    args_sq=[()] * total,
                    kwargs_sq=[{}, {
                        'defer': False
                    }] + [{}] * num)
def test_finalizer_closeable():
    pre = (j for j in range(18))
    post = (j for j in range(18))

    plan = bp.finalize_wrapper(pre, post)

    for j in range(3):
        next(plan)
    plan.close()
Exemple #7
0
def test_finalizer_closeable():
    pre = (j for j in range(18))
    post = (j for j in range(18))

    plan = bp.finalize_wrapper(pre, post)

    for j in range(3):
        next(plan)
    plan.close()
Exemple #8
0
def test_finialize_success():
    suc_cmd = "it_works"

    num = 5
    cmd = "echo"
    plan = finalize_wrapper(single_message_gen(Msg(suc_cmd, None)), echo_plan(command=cmd, num=num))
    msgs = list()
    try:
        EchoRE(plan, msg_list=msgs)
    except RuntimeError:
        pass

    total = num + 1
    _verify_msg_seq(msgs, m_len=total, cmd_sq=[suc_cmd] + [cmd] * num, args_sq=[()] * total, kwargs_sq=[{}] * total)
Exemple #9
0
def configure_count_time_wrapper(plan, time):
    """
    Preprocessor that sets all devices with a `count_time` to the same time.

    The original setting is stashed and restored at the end.

    Parameters
    ----------
    plan : iterable or iterator
        a generator, list, or similar containing `Msg` objects
    time : float or None
        If None, the plan passes through unchanged.

    Yields
    ------
    msg : Msg
        messages from plan, with 'set' messages inserted
    """
    devices_seen = set()
    original_times = {}

    def insert_set(msg):
        obj = msg.obj
        if obj is not None and obj not in devices_seen:
            devices_seen.add(obj)
            if hasattr(obj, 'count_time'):
                # TODO Do this with a 'read' Msg once reads can be
                # marked as belonging to a different event stream (or no
                # event stream.
                original_times[obj] = obj.count_time.get()
                # TODO do this with configure
                return pchain(mv(obj.count_time, time),
                              single_gen(msg)), None
        return None, None

    def reset():
        for obj, time in original_times.items():
            yield from mv(obj.count_time, time)

    if time is None:
        # no-op
        return (yield from plan)
    else:
        return (yield from finalize_wrapper(plan_mutator(plan, insert_set),
                                            reset()))
Exemple #10
0
def test_finialize_fail():
    fail_cmd = "fail_next"

    def erroring_plan():
        yield Msg(fail_cmd, None)
        raise RuntimeError("saw this coming")

    num = 5
    cmd = "echo"
    plan = finalize_wrapper(erroring_plan(), echo_plan(command=cmd, num=num))
    msgs = list()
    try:
        EchoRE(plan, msg_list=msgs)
    except RuntimeError:
        pass

    total = num + 1
    _verify_msg_seq(msgs, m_len=total, cmd_sq=[fail_cmd] + [cmd] * num, args_sq=[()] * total, kwargs_sq=[{}] * total)
def test_finalize_runs_after_error(fresh_RE):
    def plan():
        yield from [Msg('null')]
        raise Exception

    msgs = []

    def accumulator(msg):
        msgs.append(msg)

    fresh_RE.msg_hook = accumulator
    try:
        fresh_RE(finalize_wrapper(plan(), [Msg('read', det)]))
    except Exception:
        pass  # swallow the Exception; we are interested in msgs below

    expected = [Msg('null'), Msg('read', det)]

    assert msgs == expected
def test_finalize_runs_after_error(fresh_RE):
    def plan():
        yield from [Msg("null")]
        raise Exception

    msgs = []

    def accumulator(msg):
        msgs.append(msg)

    fresh_RE.msg_hook = accumulator
    try:
        fresh_RE(finalize_wrapper(plan(), [Msg("read", det)]))
    except Exception:
        pass  # swallow the Exception; we are interested in msgs below

    expected = [Msg("null"), Msg("read", det)]

    assert msgs == expected
Exemple #13
0
def test_finialize_success():
    suc_cmd = 'it_works'

    num = 5
    cmd = 'echo'
    plan = finalize_wrapper(single_message_gen(Msg(suc_cmd, None)),
                            echo_plan(command=cmd, num=num))

    msgs = list()
    try:
        EchoRE(plan, msg_list=msgs)
    except RuntimeError:
        pass

    total = num + 1
    _verify_msg_seq(msgs,
                    m_len=total,
                    cmd_sq=[suc_cmd] + [cmd] * num,
                    args_sq=[()] * total,
                    kwargs_sq=[{}] * total)
def test_finialize_pause():
    fail_cmd = 'fail_next'

    def erroring_plan():
        yield Msg(fail_cmd, None)
        raise RuntimeError('saw this coming')

    num = 5
    cmd = 'echo'
    plan = finalize_wrapper(erroring_plan(),
                            echo_plan(command=cmd, num=num),
                            pause_for_debug=True)
    msgs = list()
    try:
        EchoRE(plan, msg_list=msgs)
    except RuntimeError:
        pass

    total = num + 2
    _verify_msg_seq(msgs, m_len=total,
                    cmd_sq=[fail_cmd, 'pause'] + [cmd]*num,
                    args_sq=[()]*total,
                    kwargs_sq=[{}, {'defer': False}] + [{}]*num)
def fast_shutter_wrapper(plan):
    if gs.USE_FAST_SHUTTER:
        plan = bsp.pchain(bsp.abs_set(fast_shutter.output, FastShutter.OPEN_SHUTTER, settle_time=FastShutter.SETTLE_TIME), plan)
        plan = bsp.finalize_wrapper(plan, bsp.abs_set(fast_shutter.output, FastShutter.CLOSE_SHUTTER, settle_time=FastShutter.SETTLE_TIME))
    return (yield from plan)
Exemple #16
0
    def __call__(self, sample, plan, subs=None, *,
                 verify_write=False, dark_strategy=periodic_dark,
                 raise_if_interrupted=False, **metadata_kw):
        # The CustomizedRunEngine knows about a Beamtime object, and it
        # interprets integers for 'sample' as indexes into the Beamtime's
        # lists of Samples from all its Experiments.

        # deprecated from v0.5 release
        #if getattr(glbl, 'collection', None) is None:
        #    raise RuntimeError("No collection has been linked to current "
        #                       "experiment yet.\nPlease do\n"
        #                       ">>> open_collection(<collection_name>)\n"
        #                       "before you run any xrun")

        if isinstance(sample, int):
            try:
                sample = self.beamtime.samples[sample]
            except IndexError:
                print("WARNING: hmm, there is no sample with index `{}`"
                      ", please do `bt.list()` to check if it exists yet"
                      .format(sample))
                return
        # If a plan is given as a string, look in up in the global registry.
        if isinstance(plan, int):
            try:
                plan = self.beamtime.scanplans[plan]
            except IndexError:
                print("WARNING: hmm, there is no scanplan with index `{}`"
                      ", please do `bt.list()` to check if it exists yet"
                      .format(plan))
                return
        # If the plan is an xpdAcq 'ScanPlan', make the actual plan.
        if isinstance(plan, ScanPlan):
            plan = plan.factory()
        _subs = normalize_subs_input(subs)
        if verify_write:
            _subs.update({'stop': verify_files_saved})
        # No keys in metadata_kw are allows to collide with sample keys.
        if set(sample) & set(metadata_kw):
            raise ValueError("These keys in metadata_kw are illegal "
                             "because they are always in sample: "
                             "{}".format(set(sample) & set(metadata_kw)))
        if self._beamtime.get('bt_wavelength') is None:
            print("WARNING: there is no wavelength information in current"
                  "beamtime object, scan will keep going....")
        metadata_kw.update(sample)
        sh = glbl.shutter

        if glbl.shutter_control:
            # Alter the plan to incorporate dark frames.
            # only works if user allows shutter control
            if glbl.auto_dark:
                plan = dark_strategy(plan)
                plan = bp.msg_mutator(plan, _inject_qualified_dark_frame_uid)
            # force to close shutter after scan
            plan = bp.finalize_wrapper(plan, bp.abs_set(sh, 0, wait=True))

        # Load calibration file
        if glbl.auto_load_calib:
            plan = bp.msg_mutator(plan, _inject_calibration_md)

        # Insert glbl mask
        plan = bp.msg_mutator(plan, _inject_mask)

        # Execute
        return super().__call__(plan, subs,
                                raise_if_interrupted=raise_if_interrupted,
                                **metadata_kw)
Exemple #17
0
    def __call__(self,
                 sample,
                 plan,
                 subs=None,
                 *,
                 verify_write=False,
                 dark_strategy=periodic_dark,
                 raise_if_interrupted=False,
                 **metadata_kw):
        # The CustomizedRunEngine knows about a Beamtime object, and it
        # interprets integers for 'sample' as indexes into the Beamtime's
        # lists of Samples from all its Experiments.

        # deprecated from v0.5 release
        #if getattr(glbl, 'collection', None) is None:
        #    raise RuntimeError("No collection has been linked to current "
        #                       "experiment yet.\nPlease do\n"
        #                       ">>> open_collection(<collection_name>)\n"
        #                       "before you run any xrun")

        if isinstance(sample, int):
            try:
                sample = self.beamtime.samples[sample]
            except IndexError:
                print(
                    "WARNING: hmm, there is no sample with index `{}`"
                    ", please do `bt.list()` to check if it exists yet".format(
                        sample))
                return
        # If a plan is given as a string, look in up in the global registry.
        if isinstance(plan, int):
            try:
                plan = self.beamtime.scanplans[plan]
            except IndexError:
                print(
                    "WARNING: hmm, there is no scanplan with index `{}`"
                    ", please do `bt.list()` to check if it exists yet".format(
                        plan))
                return
        # If the plan is an xpdAcq 'ScanPlan', make the actual plan.
        if isinstance(plan, ScanPlan):
            plan = plan.factory()
        _subs = normalize_subs_input(subs)
        if verify_write:
            _subs.update({'stop': verify_files_saved})
        # No keys in metadata_kw are allows to collide with sample keys.
        if set(sample) & set(metadata_kw):
            raise ValueError("These keys in metadata_kw are illegal "
                             "because they are always in sample: "
                             "{}".format(set(sample) & set(metadata_kw)))
        if self._beamtime.get('bt_wavelength') is None:
            print("WARNING: there is no wavelength information in current"
                  "beamtime object, scan will keep going....")
        metadata_kw.update(sample)
        sh = glbl.shutter

        if glbl.shutter_control:
            # Alter the plan to incorporate dark frames.
            # only works if user allows shutter control
            if glbl.auto_dark:
                plan = dark_strategy(plan)
                plan = bp.msg_mutator(plan, _inject_qualified_dark_frame_uid)
            # force to close shutter after scan
            plan = bp.finalize_wrapper(plan, bp.abs_set(sh, 0, wait=True))

        # Load calibration file
        if glbl.auto_load_calib:
            plan = bp.msg_mutator(plan, _inject_calibration_md)

        # Insert glbl mask
        plan = bp.msg_mutator(plan, _inject_mask)

        # Execute
        return super().__call__(plan,
                                subs,
                                raise_if_interrupted=raise_if_interrupted,
                                **metadata_kw)