def create_attributes(self): tags = [widget("group")] meta = ChoiceMeta("All %s attributes" % self.attr_name, choices=["expanded", "collapsed"], tags=tags, label=self.attr_name.title()) self.attr = meta.make_attribute("expanded") yield self.attr_name, self.attr, self.attr.set_value
def create_attributes(self): tags = ["widget:group"] meta = ChoiceMeta("All %s attributes" % self.attr_name, choices=["expanded", "collapsed"], tags=tags, label=self.attr_name.title()) self.attr = meta.make_attribute("expanded") yield self.attr_name, self.attr, self.attr.set_value
def __init__(self, process, attr_name): params = Part.MethodMeta.prepare_input_map(name=attr_name) super(PandABoxGroupPart, self).__init__(process, params) self.attr_name = attr_name tags = [widget("group"), config()] self.meta = ChoiceMeta("All %s attributes" % self.attr_name, choices=["expanded", "collapsed"], tags=tags, label=self.attr_name.title())
def test_init(self): self.choice_meta = ChoiceMeta("test description", ["a", "b"]) self.assertEqual("test description", self.choice_meta.description) self.assertEqual(self.choice_meta.typeid, "malcolm:core/ChoiceMeta:1.0") self.assertEqual(self.choice_meta.label, "") self.assertEqual(self.choice_meta.choices, ("a", "b"))
def make_meta(subtyp, description, tags, writeable=True, labels=None): if subtyp == "enum": if writeable: widget_type = "combo" else: widget_type = "textupdate" tags.append(widget(widget_type)) meta = ChoiceMeta(description, labels, tags) elif subtyp == "bit": if writeable: widget_type = "checkbox" else: widget_type = "led" tags.append(widget(widget_type)) meta = BooleanMeta(description, tags) else: if writeable: widget_type = "textinput" else: widget_type = "textupdate" tags.append(widget(widget_type)) if subtyp == "uint": meta = NumberMeta("uint32", description, tags) elif subtyp == "int": meta = NumberMeta("int32", description, tags) elif subtyp == "scalar": meta = NumberMeta("float64", description, tags) elif subtyp == "lut": meta = StringMeta(description, tags) elif subtyp in ("pos", "relative_pos"): meta = NumberMeta("float64", description, tags) else: raise ValueError("Unknown subtype %r" % subtyp) return meta
def test_from_dict(self): bm = ChoiceMeta.from_dict(self.serialized) self.assertEqual(type(bm), ChoiceMeta) self.assertEquals(bm.description, "desc") self.assertEquals(bm.choices, ["a", "b"]) self.assertEqual(bm.tags, []) self.assertFalse(bm.writeable) self.assertEqual(bm.label, "name")
class TestValidate(unittest.TestCase): def setUp(self): self.choice_meta = ChoiceMeta( "test description", ["a", "b"]) def test_given_valid_value_then_return(self): response = self.choice_meta.validate("a") self.assertEqual("a", response) def test_int_validate(self): response = self.choice_meta.validate(1) self.assertEqual("b", response) def test_None_valid(self): response = self.choice_meta.validate(None) self.assertEqual(None, response) def test_given_invalid_value_then_raises(self): with self.assertRaises(ValueError): self.choice_meta.validate('badname') def test_set_choices(self): self.choice_meta.set_choices(["4"]) self.assertEqual(["4"], self.choice_meta.choices)
class TestValidate(unittest.TestCase): def setUp(self): self.choice_meta = ChoiceMeta( "test description", ["a", "b"]) def test_given_valid_value_then_return(self): response = self.choice_meta.validate("a") self.assertEqual("a", response) def test_int_validate(self): response = self.choice_meta.validate(1) self.assertEqual("b", response) def test_None_valid(self): response = self.choice_meta.validate(None) self.assertEqual("a", response) def test_given_invalid_value_then_raises(self): with self.assertRaises(ValueError): self.choice_meta.validate('badname') def test_set_choices(self): self.choice_meta.set_choices(["4"]) self.assertEqual(["4"], self.choice_meta.choices)
def create_attributes(self): for data in super(DetectorDriverPart, self).create_attributes(): yield data meta = NumberMeta("float64", "Time taken to readout detector") self.readout_time = meta.make_attribute(self.params.readoutTime) yield "readoutTime", self.readout_time, self.readout_time.set_value meta = ChoiceMeta("Whether detector is software or hardware triggered", ["Software", "Hardware"]) self.trigger_mode = meta.make_attribute("Hardware") yield "triggerMode", self.trigger_mode, None
def _create_default_attributes(self): # Add the state, status and busy attributes self.state = ChoiceMeta("State of Block", self.stateMachine.possible_states, label="State").make_attribute() yield "state", self.state, None self.status = StringMeta("Status of Block", label="Status").make_attribute() yield "status", self.status, None self.busy = BooleanMeta("Whether Block busy or not", label="Busy").make_attribute() yield "busy", self.busy, None
def create_attributes(self): for data in super(ManagerController, self).create_attributes(): yield data # Make a table for the layout info we need columns = OrderedDict() columns["name"] = StringArrayMeta("Name of layout part") columns["mri"] = StringArrayMeta("Malcolm full name of child block") columns["x"] = NumberArrayMeta("float64", "X Coordinate of child block") columns["y"] = NumberArrayMeta("float64", "Y Coordinate of child block") columns["visible"] = BooleanArrayMeta("Whether child block is visible") layout_table_meta = TableMeta("Layout of child blocks", columns=columns) layout_table_meta.set_writeable_in(sm.EDITABLE) self.layout = layout_table_meta.make_attribute() yield "layout", self.layout, self.set_layout self.layout_name = ChoiceMeta( "Saved layout name to load", []).make_attribute() self.layout_name.meta.set_writeable_in( self.stateMachine.AFTER_RESETTING) yield "layoutName", self.layout_name, self.load_layout assert os.path.isdir(self.params.configDir), \ "%s is not a directory" % self.params.configDir
def _make_time_parts(self, field_name, field_data, writeable): description = field_data.description if writeable: widget_tag = widget("textupdate") group_tag = self._make_group("parameters") else: widget_tag = widget("textinput") group_tag = self._make_group("readbacks") meta = NumberMeta("float64", description, [group_tag, widget_tag]) self._make_field_part(field_name, meta, writeable) meta = ChoiceMeta(description + " time units", ["s", "ms", "us"], tags=[group_tag, widget("combo")]) self._make_field_part(field_name + ".UNITS", meta, writeable=True)
class PandABoxGroupPart(Part): """This will normally be instantiated by the PandABox assembly, not created in yaml""" def __init__(self, process, attr_name): params = Part.MethodMeta.prepare_input_map(name=attr_name) super(PandABoxGroupPart, self).__init__(process, params) self.attr_name = attr_name tags = [widget("group"), config()] self.meta = ChoiceMeta("All %s attributes" % self.attr_name, choices=["expanded", "collapsed"], tags=tags, label=self.attr_name.title()) def create_attributes(self): attr = self.meta.make_attribute("expanded") yield self.attr_name, attr, attr.set_value
def create_attributes(self): """Method that should provide Attribute instances for Block Yields: tuple: (string name, Attribute, callable put_function). """ # Add the state, status and busy attributes self.state = ChoiceMeta("State of Block", self.stateMachine.possible_states, label="State").make_attribute() yield "state", self.state, None self.status = StringMeta("Status of Block", label="Status").make_attribute() yield "status", self.status, None self.busy = BooleanMeta("Whether Block busy or not", label="Busy").make_attribute() yield "busy", self.busy, None
def _make_mux(self, field_name, field_data, typ): group_tag = self._make_group("inputs") if typ == "bit": inport_type = "bool" else: inport_type = "int32" meta = ChoiceMeta( field_data.description, field_data.labels, tags=[group_tag, inport(inport_type, "ZERO"), widget("combo")]) self._make_field_part(field_name, meta, writeable=True) meta = make_meta(typ, "%s current value" % field_name, tags=[group_tag], writeable=False) self._make_field_part(field_name + ".VAL", meta, writeable=False)
def _make_out_capture(self, field_name, field_data): group_tag = self._make_group("outputs") meta = ChoiceMeta("Capture %s in PCAP?" % field_name, field_data.labels, tags=[group_tag, widget("combo")]) self._make_field_part(field_name + ".CAPTURE", meta, writeable=True) if self.area_detector: from malcolm.parts.ADCore.hdfwriterpart import \ attribute_dataset_types # Make a string part to hold the name of the dataset part_name = field_name + ".DATASET_NAME" label, attr_name = make_label_attr_name(part_name) params = StringPart.MethodMeta.prepare_input_map( name=attr_name, widget="textinput", description="Name of the captured dataset in HDF file", writeable=True, config=True) part = StringPart(self.process, params) self._add_part(part_name, part) # Make a choice part to hold the type of the dataset part_name = field_name + ".DATASET_TYPE" label, attr_name = make_label_attr_name(part_name) if "INENC" in self.block_name: initial = "position" else: initial = "monitor" params = ChoicePart.MethodMeta.prepare_input_map( name=attr_name, widget="textinput", description="Type of the captured dataset in HDF file", writeable=True, choices=attribute_dataset_types, initialValue=initial) part = StringPart(self.process, params) self._add_part(part_name, part)
def setUp(self): self.choice_meta = ChoiceMeta( "test description", ["a", "b"])
def test_to_dict(self): bm = ChoiceMeta("desc", ["a", "b"], label="name") self.assertEqual(bm.to_dict(), self.serialized)
class ManagerController(DefaultController): """RunnableDevice implementer that also exposes GUI for child parts""" # The stateMachine that this controller implements stateMachine = sm() ReportOutports = Hook() """Called before Layout to get outport info from children Args: task (Task): The task used to perform operations on child blocks Returns: [`OutportInfo`] - the type and value of each outport of the child """ Layout = Hook() """Called when layout table set and at init to update child layout Args: task (Task): The task used to perform operations on child blocks part_info (dict): {part_name: [Info]} returned from Layout hook layout_table (Table): A possibly partial set of changes to the layout table that should be acted on Returns: [`LayoutInfo`] - the child layout resulting from this change """ Load = Hook() """Called at load() or revert() to load child settings from a structure Args: task (Task): The task used to perform operations on child blocks structure (dict): {part_name: part_structure} where part_structure is the return from Save hook """ Save = Hook() """Called at save() to serialize child settings into a dict structure Args: task (Task): The task used to perform operations on child blocks Returns: dict: serialized version of the child that could be loaded from """ # attributes layout = None layout_name = None # {part_name: part_structure} of currently loaded settings load_structure = None def create_attributes(self): for data in super(ManagerController, self).create_attributes(): yield data # Make a table for the layout info we need columns = OrderedDict() columns["name"] = StringArrayMeta("Name of layout part") columns["mri"] = StringArrayMeta("Malcolm full name of child block") columns["x"] = NumberArrayMeta("float64", "X Coordinate of child block") columns["y"] = NumberArrayMeta("float64", "Y Coordinate of child block") columns["visible"] = BooleanArrayMeta("Whether child block is visible") layout_table_meta = TableMeta("Layout of child blocks", columns=columns) layout_table_meta.set_writeable_in(sm.EDITABLE) self.layout = layout_table_meta.make_attribute() yield "layout", self.layout, self.set_layout self.layout_name = ChoiceMeta( "Saved layout name to load", []).make_attribute() self.layout_name.meta.set_writeable_in( self.stateMachine.AFTER_RESETTING) yield "layoutName", self.layout_name, self.load_layout assert os.path.isdir(self.params.configDir), \ "%s is not a directory" % self.params.configDir def set_layout(self, value): # If it isn't a table, make it one if not isinstance(value, Table): value = Table(self.layout.meta, value) part_info = self.run_hook(self.ReportOutports, self.create_part_tasks()) part_info = self.run_hook( self.Layout, self.create_part_tasks(), part_info, value) layout_table = Table(self.layout.meta) for name, layout_infos in LayoutInfo.filter_parts(part_info).items(): assert len(layout_infos) == 1, \ "%s returned more than 1 layout infos" % name layout_info = layout_infos[0] row = [name, layout_info.mri, layout_info.x, layout_info.y, layout_info.visible] layout_table.append(row) self.layout.set_value(layout_table) def do_reset(self): super(ManagerController, self).do_reset() # This will trigger all parts to report their layout, making sure the # layout table has a valid value self.set_layout(Table(self.layout.meta)) # List the configDir and add to choices self._set_layout_names() # If we have no load_structure (initial reset) define one if self.load_structure is None: if self.params.defaultConfig: self.load_layout(self.params.defaultConfig) else: self.load_structure = self._save_to_structure() @method_writeable_in(sm.READY) def edit(self): self.transition(sm.EDITABLE, "Layout editable") def go_to_error_state(self, exception): if self.state.value == sm.EDITABLE: # If we got a save or revert exception, don't go to fault self.log_exception("Fault occurred while trying to save/revert") else: super(ManagerController, self).go_to_error_state(exception) @method_writeable_in(sm.EDITABLE) @method_takes( "layoutName", StringMeta( "Name of layout to save to, if different from current layoutName"), None) def save(self, params): self.try_stateful_function( sm.SAVING, self.stateMachine.AFTER_RESETTING, self.do_save, params.layoutName) def do_save(self, layout_name=None): if not layout_name: layout_name = self.layout_name.value structure = self._save_to_structure() text = json_encode(structure, indent=2) filename = self._validated_config_filename(layout_name) open(filename, "w").write(text) self._set_layout_names(layout_name) self.layout_name.set_value(layout_name) self.load_structure = structure def _set_layout_names(self, extra_name=None): names = [] if extra_name: names.append(extra_name) dir_name = self._make_config_dir() for f in os.listdir(dir_name): if os.path.isfile( os.path.join(dir_name, f)) and f.endswith(".json"): names.append(f.split(".json")[0]) self.layout_name.meta.set_choices(names) @method_writeable_in(sm.EDITABLE) def revert(self): self.try_stateful_function( sm.REVERTING, self.stateMachine.AFTER_RESETTING, self.do_revert) def do_revert(self): self._load_from_structure(self.load_structure) def _validated_config_filename(self, name): """Make config dir and return full file path and extension Args: name (str): Filename without dir or extension Returns: str: Full path including extensio """ dir_name = self._make_config_dir() filename = os.path.join(dir_name, name.split(".json")[0] + ".json") return filename def _make_config_dir(self): dir_name = os.path.join(self.params.configDir, self.mri) try: os.mkdir(dir_name) except OSError: # OK if already exists, if not then it will fail on write anyway pass return dir_name def load_layout(self, value): # TODO: race condition if we get 2 loads at once... # Do we need a Loading state? filename = self._validated_config_filename(value) text = open(filename, "r").read() structure = json_decode(text) self._load_from_structure(structure) self.layout_name.set_value(value) def _save_to_structure(self): structure = OrderedDict() structure["layout"] = OrderedDict() for name, x, y, visible in sorted( zip(self.layout.value.name, self.layout.value.x, self.layout.value.y, self.layout.value.visible)): layout_structure = OrderedDict() layout_structure["x"] = x layout_structure["y"] = y layout_structure["visible"] = visible structure["layout"][name] = layout_structure for part_name, part_structure in sorted(self.run_hook( self.Save, self.create_part_tasks()).items()): structure[part_name] = part_structure return structure def _load_from_structure(self, structure): table = Table(self.layout.meta) for part_name, part_structure in structure["layout"].items(): table.append([part_name, "", part_structure["x"], part_structure["y"], part_structure["visible"]]) self.set_layout(table) self.run_hook(self.Load, self.create_part_tasks(), structure)
from malcolm.parts.ca.castringpart import CAStringPart from malcolm.controllers.defaultcontroller import DefaultController from malcolm.core import method_takes, REQUIRED from malcolm.core.vmetas import StringMeta, ChoiceMeta from malcolm.tags import port_types, outport, widget @method_takes("name", StringMeta("Name of the created attribute"), REQUIRED, "description", StringMeta("Desc of created attribute"), REQUIRED, "rbv", StringMeta("Full pv of demand and default for rbv"), REQUIRED, "outport", ChoiceMeta("Outport type", port_types), REQUIRED) class AsynOutportPart(CAStringPart): def __init__(self, process, params): self.outport_type = params.outport params = CAStringPart.MethodMeta.prepare_input_map( name=params.name, description=params.description, rbv=params.rbv) super(AsynOutportPart, self).__init__(process, params) def create_tags(self): tags = super(AsynOutportPart, self).create_tags() tags.append(widget("textupdate")) return tags @DefaultController.Reset def reset(self, task=None): super(AsynOutportPart, self).reset(task) # Add the outport tags tags = [t for t in self.attr.meta.tags if not t.startswith("outport:")] tags.append(outport(self.outport_type, self.attr.value))
from malcolm.core import Part, method_takes, REQUIRED from malcolm.core.vmetas import StringMeta, BooleanMeta, ChoiceMeta from malcolm.tags import widget_types, widget, config @method_takes( "name", StringMeta("Name of the created attribute"), REQUIRED, "description", StringMeta("Desc of created attribute"), REQUIRED, "widget", ChoiceMeta("Widget type", [""] + widget_types), "", "writeable", BooleanMeta("Is the attribute writeable?"), False, "config", BooleanMeta("Should this field be loaded/saved?"), False) class AttributePart(Part): # Attribute instance attr = None def create_attributes(self): # Find the tags tags = self.create_tags() # Make a meta object for our attribute meta = self.create_meta(self.params.description, tags) # The attribute we will be publishing initial_value = self.get_initial_value() self.attr = meta.make_attribute(initial_value) writeable_func = self.get_writeable_func() yield self.params.name, self.attr, writeable_func def create_meta(self, description, tags): raise NotImplementedError() def get_writeable_func(self): if self.params.writeable:
from malcolm.core import method_takes, REQUIRED from malcolm.parts.builtin.attributepart import AttributePart from malcolm.core.vmetas import StringMeta, ChoiceMeta, BooleanMeta from malcolm.controllers.defaultcontroller import DefaultController from malcolm.parts.ca.cothreadimporter import CothreadImporter from malcolm.tags import widget_types, inport, port_types @method_takes( "name", StringMeta("Name of the created attribute"), REQUIRED, "description", StringMeta("Desc of created attribute"), REQUIRED, "pv", StringMeta("Full pv of demand and default for rbv"), "", "rbv", StringMeta("Override for rbv"), "", "rbvSuff", StringMeta("Set rbv ro pv + rbv_suff"), "", "widget", ChoiceMeta("Widget type", [""] + widget_types), "", "inport", ChoiceMeta("Inport type", [""] + port_types), "", "config", BooleanMeta("Should this field be loaded/saved?"), False) class CAPart(AttributePart): # Camonitor subscription monitor = None def __init__(self, process, params): self.cothread, self.catools = CothreadImporter.get_cothread(process) # Format for all caputs self.ca_format = self.catools.FORMAT_TIME super(CAPart, self).__init__(process, params) def store_params(self, params): if not params.rbv and not params.pv: raise ValueError('Must pass pv or rbv') if not params.rbv:
def create_meta(self, description, tags): return ChoiceMeta(choices=self.params.choices, description=description, tags=tags)
def create_meta(self, description): return ChoiceMeta(description)
def create_meta(self, description, tags): return ChoiceMeta(description=description, tags=tags)