Beispiel #1
0
 def __init__(
         self,
         name,  # type: APartName
         mri,  # type: AMri
         initial_visibility=False,  # type: AInitialVisibility
         ignore_configure_args=(),  # type: UIgnoreConfigureArgs
 ):
     # type: (...) -> None
     super(RunnableChildPart, self).__init__(name, mri, initial_visibility)
     self.ignore_configure_args = AIgnoreConfigureArgs(
         ignore_configure_args)
     # Stored between runs
     self.run_future = None  # type: Future
     # The configure method model of our child
     self.configure_model = MethodModel()
     # The registrar object we get at setup
     self.registrar = None  # type: PartRegistrar
     # The design we last loaded/saved
     self.design = None
     # Hooks
     self.register_hooked(ValidateHook, self.validate, self.configure_args)
     self.register_hooked(ConfigureHook, self.configure,
                          self.configure_args)
     self.register_hooked((RunHook, ResumeHook), self.run)
     self.register_hooked((PostRunArmedHook, PostRunReadyHook),
                          self.post_run)
     self.register_hooked(SeekHook, self.seek)
     self.register_hooked(AbortHook, self.abort)
Beispiel #2
0
 def test_from_dict(self):
     m = MethodModel.from_dict(self.serialized)
     assert m.takes.to_dict() == self.takes.to_dict()
     assert m.defaults == self.serialized["defaults"]
     assert m.tags == []
     assert m.writeable is False
     assert m.label == ""
     assert m.returns.to_dict() == MapMeta().to_dict()
Beispiel #3
0
 def setUp(self):
     self.data = BlockModel()
     self.data.set_endpoint_data("attr",
                                 StringMeta().create_attribute_model())
     self.data.set_endpoint_data("method", MethodModel())
     self.data.set_notifier_path(Mock(), ["block"])
     self.controller = Mock()
     self.context = Mock()
     self.o = make_view(self.controller, self.context, self.data)
Beispiel #4
0
    def create_method_models(self):
        method_name = snake_to_camel(self.field_name)
        if self.arg_meta:
            self.arg_name = method_name

            # Decorate set_field with a Method
            @method_takes(self.arg_name, self.arg_meta, REQUIRED)
            def set_field(params):
                self.set_field(params)

            self.method = set_field.MethodModel
            writeable_func = set_field
        else:
            self.method = MethodModel()
            writeable_func = None
        self.method.set_description(self.description)
        self.method.set_tags(self.tags)
        yield method_name, self.method, writeable_func
Beispiel #5
0
 def init(self, context):
     self.configure_args_update_queue = Queue()
     super(RunnableChildPart, self).init(context)
     # Monitor the child configure Method for changes
     self.serialized_configure = MethodModel().to_dict()
     subscription = Subscribe(
         path=[self.params.mri, "configure"], delta=True,
         callback=self.update_part_configure_args)
     # Wait for the first update to come in
     self.child_controller.handle_request(subscription).wait()
Beispiel #6
0
class PandABlocksActionPart(Part):
    """This will normally be instantiated by the PandABox assembly, not created
    in yaml"""

    def __init__(self, client, block_name, field_name, description, tags,
                 arg_meta=None):
        super(PandABlocksActionPart, self).__init__(field_name)
        self.client = client
        self.block_name = block_name
        self.field_name = field_name
        self.description = description
        self.tags = tags
        self.arg_name = None
        self.arg_meta = arg_meta
        self.method = None

    def create_method_models(self):
        method_name = snake_to_camel(self.field_name)
        if self.arg_meta:
            self.arg_name = method_name

            # Decorate set_field with a Method
            @method_takes(self.arg_name, self.arg_meta, REQUIRED)
            def set_field(params):
                self.set_field(params)

            self.method = set_field.MethodModel
            writeable_func = set_field
        else:
            self.method = MethodModel()
            writeable_func = None
        self.method.set_description(self.description)
        self.method.set_tags(self.tags)
        yield method_name, self.method, writeable_func

    def set_field(self, params=None):
        if params is None:
            value = 0
        else:
            value = params[self.arg_name]
        self.client.set_field(self.block_name, self.field_name, value)
    def update_configure_args(self, part, configure_model):
        """Tell controller part needs different things passed to Configure"""
        with self.changes_squashed:
            # Update the dict
            self.configure_method_models[part] = configure_model
            method_models = list(self.configure_method_models.values())

            # Update takes with the things we need
            default_configure = MethodModel.from_dict(
                RunnableController.configure.MethodModel.to_dict())
            default_configure.defaults["axesToMove"] = self.axes_to_move.value
            method_models.append(default_configure)

            # Decorate validate and configure with the sum of its parts
            self._block.validate.recreate_from_others(method_models)
            self._block.validate.set_returns(self._block.validate.takes)
            self._block.configure.recreate_from_others(method_models)
Beispiel #8
0
    def instantiate(self, substitutions):
        """Keep recursing down from base using dotted name, then call it with
        self.params and args

        Args:
            substitutions (dict): Substitutions to make to self.param_dict

        Returns:
            The found object called with (*args, map_from_d)

        E.g. if ob is malcolm.parts, and name is "ca.CADoublePart", then the
        object will be malcolm.parts.ca.CADoublePart
        """
        param_dict = self.substitute_params(substitutions)
        pkg, ident = self.name.rsplit(".", 1)
        pkg = "malcolm.modules.%s" % pkg
        try:
            ob = importlib.import_module(pkg)
        except ImportError as e:
            raise_with_traceback(
                ImportError("\n%s:%d:\n%s" % (self.filename, self.lineno, e)))
        try:
            ob = getattr(ob, ident)
        except AttributeError:
            raise_with_traceback(
                ImportError("\n%s:%d:\nPackage %r has no ident %r" %
                            (self.filename, self.lineno, pkg, ident)))
        try:
            model = MethodModel.from_callable(ob, returns=False)
            args = model.validate(param_dict)
            ret = ob(**args)
        except Exception as e:
            sourcefile = inspect.getsourcefile(ob)
            lineno = inspect.getsourcelines(ob)[1]
            raise_with_traceback(
                YamlError("\n%s:%d:\n%s:%d:\n%s" %
                          (self.filename, self.lineno, sourcefile, lineno, e)))
        else:
            return ret
Beispiel #9
0
 def create_method_models(self):
     # Method instance
     self.method = MethodModel(self.params.description)
     # TODO: set widget tag?
     yield self.params.name, self.method, self.caput
Beispiel #10
0
 def setUp(self):
     self.attr = StringMeta().create_attribute_model()
     self.method = MethodModel()
     self.o = BlockModel()
     self.o.set_endpoint_data("attr", self.attr)
     self.o.set_endpoint_data("method", self.method)
Beispiel #11
0
 def test_to_dict(self):
     m = MethodModel(description="test_description")
     m.set_takes(self.takes)
     m.set_defaults(self.serialized["defaults"])
     assert m.to_dict() == self.serialized
Beispiel #12
0
 def test_set_label(self):
     m = MethodModel(description="test_description")
     m.set_label("new_label")
     assert "new_label" == m.label
Beispiel #13
0
 def test_init(self):
     m = MethodModel(description="test_description")
     assert "test_description" == m.description
     assert "malcolm:core/Method:1.0" == m.typeid
     assert "" == m.label
Beispiel #14
0
class RunnableChildPart(ChildPart):
    """Part controlling a child Block that exposes a configure/run interface"""
    def __init__(
            self,
            name,  # type: APartName
            mri,  # type: AMri
            initial_visibility=False,  # type: AInitialVisibility
            ignore_configure_args=(),  # type: UIgnoreConfigureArgs
    ):
        # type: (...) -> None
        super(RunnableChildPart, self).__init__(name, mri, initial_visibility)
        self.ignore_configure_args = AIgnoreConfigureArgs(
            ignore_configure_args)
        # Stored between runs
        self.run_future = None  # type: Future
        # The configure method model of our child
        self.configure_model = MethodModel()
        # The registrar object we get at setup
        self.registrar = None  # type: PartRegistrar
        # The design we last loaded/saved
        self.design = None
        # Hooks
        self.register_hooked(ValidateHook, self.validate, self.configure_args)
        self.register_hooked(ConfigureHook, self.configure,
                             self.configure_args)
        self.register_hooked((RunHook, ResumeHook), self.run)
        self.register_hooked((PostRunArmedHook, PostRunReadyHook),
                             self.post_run)
        self.register_hooked(SeekHook, self.seek)
        self.register_hooked(AbortHook, self.abort)

    def configure_args(self):
        args = ["context"]
        for x in self.configure_model.takes.elements:
            if x not in self.ignore_configure_args:
                args.append(x)
        return args

    @add_call_types
    def init(self, context):
        # type: (AContext) -> None
        super(RunnableChildPart, self).init(context)
        # Monitor the child configure Method for changes
        subscription = Subscribe(path=[self.mri, "configure"], delta=True)
        subscription.set_callback(self.update_part_configure_args)
        # Wait for the first update to come in
        self.child_controller.handle_request(subscription).wait()

    @add_call_types
    def halt(self):
        # type: () -> None
        super(RunnableChildPart, self).halt()
        unsubscribe = Unsubscribe()
        unsubscribe.set_callback(self.update_part_configure_args)
        self.child_controller.handle_request(unsubscribe)

    @add_call_types
    def reset(self, context):
        # type: (AContext) -> None
        child = context.block_view(self.mri)
        if child.abort.writeable:
            child.abort()
        super(RunnableChildPart, self).reset(context)

    @add_call_types
    def load(self, context, structure, init=False):
        # type: (AContext, AStructure, AInit) -> None
        if init:
            # At init pop out the design so it doesn't get restored here
            # This stops child devices (like a detector) getting told to
            # go to multiple conflicting designs at startup
            design = structure.pop("design", "")
            super(RunnableChildPart, self).load(context, structure)
            # Now put it back in the saved structure
            self.saved_structure["design"] = design
            # We might not have cleared the changes so report here
            child = context.block_view(self.mri)
            self.send_modified_info_if_not_equal("design", child.design.value)
        else:
            super(RunnableChildPart, self).load(context, structure)

    @add_call_types
    def validate(self, context, **kwargs):
        # type: (AContext, **Any) -> UParameterTweakInfos
        child = context.block_view(self.mri)
        returns = child.validate(**kwargs)
        ret = []
        for k, v in serialize_object(returns).items():
            if serialize_object(kwargs[k]) != v:
                ret.append(ParameterTweakInfo(k, v))
        return ret

    @add_call_types
    def configure(self, context, **kwargs):
        # type: (AContext, **Any) -> None
        child = context.block_view(self.mri)
        # If we have done a save or load with the child having a particular
        # design then make sure the child now has that design
        design = self.saved_structure.get("design", "")
        if design:
            child.design.put_value(design)
        child.configure(**kwargs)

    @add_call_types
    def run(self, context):
        # type: (AContext) -> None
        context.unsubscribe_all()
        child = context.block_view(self.mri)
        child.completedSteps.subscribe_value(self.update_completed_steps)
        bad_states = [ss.DISABLING, ss.ABORTING, ss.FAULT]
        match_future = child.when_value_matches_async("state", ss.POSTRUN,
                                                      bad_states)
        if child.state.value == ss.ARMED:
            self.run_future = child.run_async()
        else:
            child.resume()
        try:
            context.wait_all_futures(match_future)
        except BadValueError:
            # If child went into Fault state, raise the friendlier run_future
            # exception
            if child.state.value == ss.FAULT:
                raise self.run_future.exception()
            else:
                raise

    @add_call_types
    def post_run(self, context):
        # type: (AContext) -> None
        context.wait_all_futures(self.run_future)

    @add_call_types
    def seek(self, context, completed_steps):
        # type: (AContext, ACompletedSteps) -> None
        # Clear out the update_completed_steps and match_future subscriptions
        context.unsubscribe_all()
        child = context.block_view(self.mri)
        child.pause(completedSteps=completed_steps)

    @add_call_types
    def abort(self, context):
        # type: (AContext) -> None
        child = context.block_view(self.mri)
        child.abort()

    def update_completed_steps(self, value):
        # type: (int) -> None
        self.registrar.report(RunProgressInfo(value))

    def update_part_configure_args(self, response):
        # Decorate validate and configure with the sum of its parts
        if isinstance(response, Delta):
            # Check if the changes contain more than just writeable change
            writeable_path = [
                c[0] and c[0][-1] == "writeable" for c in response.changes
            ]
            if all(writeable_path):
                return
        else:
            # Return or Error is the end of our subscription, log and ignore
            self.log.debug("update_part_configure_args got response %r",
                           response)
            return

        for change in response.changes:
            if change[0]:
                # Update
                self.configure_model.apply_change(*change)
            else:
                # Replace
                # TODO: should we make apply_change handle this?
                self.configure_model = deserialize_object(change[1])
        # Extract the bits we need
        metas = OrderedDict()
        defaults = OrderedDict()
        required = []
        for k, meta in self.configure_model.takes.elements.items():
            if k not in self.ignore_configure_args:
                # Copy the meta so it belongs to the Controller we report to
                # TODO: should we pass the serialized version?
                metas[k] = deserialize_object(meta.to_dict())
                if k in self.configure_model.defaults:
                    defaults[k] = self.configure_model.defaults[k]
                if k in self.configure_model.takes.required:
                    required.append(k)

        # Notify the controller that we have some new parameters to take
        self.registrar.report(ConfigureParamsInfo(metas, required, defaults))