def __init__(self, name: parts.APartName, ioc: AIoc) -> None: super().__init__(name) self.dls_ver_pv = ca.util.CAAttribute( StringMeta("IOC version"), ca.util.catools.DBR_STRING, "", ioc + ":DLSVER", throw=False, callback=self.version_updated, ) self.dir1_pv = ca.util.CAAttribute( StringMeta("IOC directory pt1"), ca.util.catools.DBR_STRING, "", ioc + ":APP_DIR1", widget=Widget.NONE, throw=False, callback=self.set_dir1, ) self.dir2_pv = ca.util.CAAttribute( StringMeta("IOC directory pt2"), ca.util.catools.DBR_STRING, "", ioc + ":APP_DIR2", widget=Widget.NONE, throw=False, callback=self.set_dir2, ) self.autosave_status_pv = ca.util.CAAttribute( StringMeta("IOC Status"), ca.util.catools.DBR_STRING, "", ioc + ":STATUS", throw=False, callback=self.set_procserv_state, ) self.dir1 = None self.dir2 = None self.dir = "" self.has_procserv = False elements = OrderedDict() elements["module"] = StringArrayMeta("Module", tags=[Widget.TEXTUPDATE.tag()]) elements["path"] = StringArrayMeta("Path", tags=[Widget.TEXTUPDATE.tag()]) self.dependencies = TableMeta( "Modules which this IOC depends on", tags=[Widget.TABLE.tag()], writeable=False, elements=elements, ).create_attribute_model({"module": [], "path": []})
def test_to_dict(self): elements = OrderedDict() elements["foo"] = StringArrayMeta(label="Foo") elements["bar"] = StringArrayMeta() meta = TableMeta(description="desc", tags=[], writeable=True, label="my label", elements=elements) value = meta.table_cls( foo=["foo1", "foo2"], bar=["bar1", "bar2"]) o = meta.create_attribute_model(value) o.set_ts(self.serialized["timeStamp"]) assert o.meta.elements["foo"].to_dict() == self.serialized["meta"]["elements"]["foo"] assert o.to_dict() == self.serialized
def setup(self, registrar: PartRegistrar) -> None: attr = TableMeta.from_table( SequencerTable, "Sequencer Table", writeable=list( SequencerTable.call_types)).create_attribute_model() self.table_set = MagicMock(side_effect=attr.set_value) registrar.add_attribute_model("table", attr, self.table_set) for suff, val in (("a", "INENC1.VAL"), ("b", "INENC2.VAL"), ("c", "ZERO")): attr = StringMeta("Input").create_attribute_model(val) registrar.add_attribute_model(f"pos{suff}", attr) attr = StringMeta("Input").create_attribute_model("ZERO") registrar.add_attribute_model("bita", attr) attr = BooleanMeta("Active", (), True).create_attribute_model(False) registrar.add_attribute_model("active", attr, attr.set_value) attr = NumberMeta("int16", "repeats", writeable=True).create_attribute_model(1) registrar.add_attribute_model("repeats", attr, writeable_func=attr.set_value) attr = NumberMeta("int16", "prescale", writeable=True).create_attribute_model(0) registrar.add_attribute_model("prescale", attr, writeable_func=attr.set_value)
def _make_table(self, field_name, field_data): group = self._make_group("parameters") tags = [Widget.TABLE.tag(), group, config_tag()] meta = TableMeta(field_data.description, tags, writeable=True) part = PandABlocksTablePart(self.client, meta, self.block_name, field_name) self._add_part(field_name, part)
def _make_table(self, field_name: str, field_data: FieldData) -> None: group = self._make_group("parameters") tags = [Widget.TABLE.tag(), group, config_tag()] meta = TableMeta(field_data.description, tags, writeable=True) part = PandATablePart(self.client, meta, self.block_name, field_name) self.add_part(part) self.field_parts[field_name] = part
def test_init(self): tm = TableMeta("desc") assert "desc" == tm.description assert "malcolm:core/TableMeta:1.0" == tm.typeid assert [] == tm.tags assert False is tm.writeable assert "" == tm.label
def setUp(self): self.client = Mock() fields = OrderedDict() fields["NREPEATS"] = TableFieldData(15, 0, "Num Repeats", None, False) fields["TRIGGER"] = TableFieldData(19, 16, "Choices", ["A", "b", "CC"], False) fields["POSITION"] = TableFieldData(63, 32, "Position", None, True) fields["TIME1"] = TableFieldData(95, 64, "Time Phase A", None, False) fields["OUTA1"] = TableFieldData(20, 20, "Out1", None, False) fields["TIME2"] = TableFieldData(127, 96, "Time Phase B", None, False) fields["OUTA2"] = TableFieldData(26, 26, "Out2", None, False) self.client.get_table_fields.return_value = fields self.meta = TableMeta("Seq table", writeable=True) self.o = PandATablePart(self.client, self.meta, block_name="SEQ1", field_name="TABLE")
def __init__(self, name): # type: (APartName) -> None super(DatasetTablePart, self).__init__(name) self.datasets = TableMeta.from_table( DatasetTable, "Datasets produced in HDF file").create_attribute_model() self.register_hooked(scanning.hooks.PostConfigureHook, self.post_configure)
def setup(self, registrar: PartRegistrar) -> None: self.datasets = TableMeta.from_table( DatasetTable, "Datasets produced in HDF file").create_attribute_model() registrar.add_attribute_model("datasets", self.datasets) # Hooks registrar.hook(PostConfigureHook, self.on_post_configure) registrar.hook(builtin.hooks.ResetHook, self.on_reset)
def test_from_dict(self): tm = TableMeta.from_dict(self.serialized) assert tm.description == "desc" assert len(tm.elements) == 1 assert tm.elements["c1"].to_dict() == self.sam.to_dict() assert tm.tags == [] assert tm.writeable is True assert tm.label == "Name"
def setUp(self): tm = TableMeta("desc") self.tm = tm self.tm.set_elements(dict(c1=StringArrayMeta())) self.sam = StringArrayMeta() self.serialized = OrderedDict() self.serialized["typeid"] = "malcolm:core/TableMeta:1.0" self.serialized["description"] = "desc" self.serialized["tags"] = [] self.serialized["writeable"] = True self.serialized["label"] = "Name" self.serialized["elements"] = dict(c1=self.sam.to_dict())
def setup(self, registrar: PartRegistrar) -> None: self.bits = TableMeta.from_table( self.bits_table_cls, "Current values and capture status of Bit fields", writeable=[ x for x in self.bits_table_cls.call_types if x not in ("name", "value") ], extra_tags=[config_tag()], ).create_attribute_model() self.positions = TableMeta.from_table( self.positions_table_cls, "Current values, scaling, and capture status of Position fields", writeable=[ x for x in self.positions_table_cls.call_types if x not in ("name", "value") ], extra_tags=[config_tag()], ).create_attribute_model() registrar.add_attribute_model("bits", self.bits, self.set_bits) registrar.add_attribute_model("positions", self.positions, self.set_positions)
def setUp(self): self.client = Mock() fields = OrderedDict() fields["NREPEATS"] = TableFieldData(7, 0, "Num Repeats", None) fields["SWITCH"] = TableFieldData(34, 32, "Choices", ["A", "b", "CC"]) fields["TRIGGER_MASK"] = TableFieldData(48, 48, "Trigger Mask", None) fields["TIME_PH_A"] = TableFieldData(95, 64, "Time Phase A", None) self.client.get_table_fields.return_value = fields self.meta = TableMeta("Seq table", writeable=True) self.o = PandABlocksTablePart(self.client, self.meta, block_name="SEQ1", field_name="TABLE")
def setup(self, registrar: PartRegistrar) -> None: attr = TableMeta.from_table( SequencerTable, "Sequencer Table", writeable=list( SequencerTable.call_types)).create_attribute_model() self.table_set = MagicMock(side_effect=attr.set_value) registrar.add_attribute_model("table", attr, self.table_set) for suff, val in (("a", "INENC1.VAL"), ("b", "INENC2.VAL"), ("c", "ZERO")): attr = StringMeta("Input").create_attribute_model(val) registrar.add_attribute_model("pos%s" % suff, attr) attr = StringMeta("Input").create_attribute_model("ZERO") registrar.add_attribute_model("bita", attr)
def __init__( self, name: APartName, mri: AMri, soft_trigger_modes: USoftTriggerModes = None, multiple_image_mode: AMultipleImageMode = "Multiple", main_dataset_useful: AMainDatasetUseful = True, runs_on_windows: APartRunsOnWindows = False, required_version: AVersionRequirement = None, min_acquire_period: AMinAcquirePeriod = 0.0, ) -> None: super().__init__(name, mri) self.required_version = required_version self.min_acquire_period = min_acquire_period self.soft_trigger_modes = soft_trigger_modes self.multiple_image_mode = multiple_image_mode self.is_hardware_triggered = True self.main_dataset_useful = main_dataset_useful self.attributes_filename = "" self.extra_attributes = TableMeta.from_table( ExtraAttributesTable, "Extra attributes to be added to the dataset", writeable=[ "name", "pv", "description", "sourceId", "sourceType", "dataType", "datasetType", ], extra_tags=[config_tag()], ).create_attribute_model() self.runs_on_windows = runs_on_windows # How long to wait between frame updates before error self.frame_timeout = 0.0 # When arrayCounter gets to here we are done self.done_when_reaches = 0 # CompletedSteps = arrayCounter + self.uniqueid_offset self.uniqueid_offset = 0 # A future that completes when detector start calls back self.start_future: Optional[Future] = None
def __init__( self, mri: builtin.controllers.AMri, hostname: AHostname = "localhost", port: APort = 8008, connect_timeout: AConnectTimeout = DEFAULT_TIMEOUT, ) -> None: super().__init__(mri) self.hostname = hostname self.port = port self.connect_timeout = connect_timeout self._connected_queue = Queue() # {new_id: request} self._request_lookup: Dict[int, Request] = {} self._next_id = 1 self._conn: Optional[WebSocketClientConnection] = None # Create read-only attribute for the remotely reachable blocks self.remote_blocks = TableMeta.from_table( BlockTable, "Remotely reachable blocks").create_attribute_model() self.field_registry.add_attribute_model("remoteBlocks", self.remote_blocks)
def setup(self, registrar: PartRegistrar) -> None: pos_table = DatasetPositionsTable( name=["COUNTER1.VALUE", "INENC1.VAL", "INENC2.VAL"], value=[0.0] * 3, units=[""] * 3, # NOTE: x inverted from MRES below to simulate inversion of # encoder in the geobrick layer scale=[1.0, -0.001, 0.001], offset=[0.0, 0.0, 0.0], capture=[PositionCapture.MIN_MAX_MEAN] * 3, datasetName=["I0", "x", "y"], datasetType=[ AttributeDatasetType.MONITOR, AttributeDatasetType.POSITION, AttributeDatasetType.POSITION, ], ) attr = TableMeta.from_table( DatasetPositionsTable, "Sequencer Table", writeable=list(SequencerTable.call_types), ).create_attribute_model(pos_table) registrar.add_attribute_model("positions", attr)
def __init__( self, name: util.APartName, description: util.AMetaDescription, pv_list: util.APvList = (), name_list: util.ANameList = (), min_delta: util.AMinDelta = 0.05, timeout: util.ATimeout = DEFAULT_TIMEOUT, widget: util.AWidget = Widget.PLOT, group: util.AGroup = None, config: util.AConfig = True, display_from_pv: util.AGetLimits = True, ) -> None: if len(pv_list) != len(name_list): raise BadValueError( "List of PVs must be same length as list of names!") super().__init__(name) self.display_from_pv = display_from_pv elements = {} for name in name_list: elements[name] = NumberArrayMeta("float64", name, tags=[Widget.TEXTUPDATE.tag()]) self.name_list = name_list self.pv_list = pv_list self.caa = util.WaveformTableAttribute( TableMeta(description, writeable=False, elements=elements), util.catools.DBR_DOUBLE, pv_list, name_list, min_delta, timeout, widget, group, config, on_connect=self._update_display, )
def __init__( self, mri: AMri, config_dir: AConfigDir, template_designs: ATemplateDesigns = "", initial_design: AInitialDesign = "", use_git: AUseGit = True, description: ADescription = "", ) -> None: super().__init__(mri=mri, description=description) assert os.path.isdir(config_dir), "%s is not a directory" % config_dir self.config_dir = config_dir self.initial_design = initial_design self.use_git = use_git self.template_designs = template_designs self.git_config: Tuple[str, ...] if use_git: if check_git_version("1.7.2"): self.git_email = os.environ["USER"] + "@" + socket.gethostname( ) self.git_name = "Malcolm" self.git_config = ( "-c", "user.name=%s" % self.git_name, "-c", 'user.email="%s"' % self.git_email, ) else: self.git_config = () # last saved layout and exports self.saved_visibility = None self.saved_exports = None # ((name, AttributeModel/MethodModel, setter, needs_context)) self._current_part_fields = () self._subscriptions: List[Subscribe] = [] self.port_info: Dict[APartName, List[PortInfo]] = {} self.part_exportable: Dict[Part, List[AAttributeName]] = {} # TODO: turn this into "exported attribute modified" self.context_modified: Dict[Part, Set[str]] = {} self.part_modified: Dict[Part, PartModifiedInfo] = {} # The attributes our part has published self.our_config_attributes: Dict[str, AttributeModel] = {} # The reportable infos we are listening for self.info_registry.add_reportable(PartModifiedInfo, self.update_modified) # Update queue of exportable fields self.info_registry.add_reportable(PartExportableInfo, self.update_exportable) # Create a port for ourself self.field_registry.add_attribute_model( "mri", StringMeta( "A port for giving our MRI to things that might use us", tags=[Port.BLOCK.source_port_tag(self.mri)], ).create_attribute_model(self.mri), ) # Create a layout table attribute for setting block positions self.layout = TableMeta.from_table( LayoutTable, "Layout of child blocks", Widget.FLOWGRAPH, writeable=["x", "y", "visible"], ).create_attribute_model() self.set_writeable_in(self.layout, ss.READY) self.field_registry.add_attribute_model("layout", self.layout, self.set_layout) # Create a design attribute for loading an existing layout self.design = ChoiceMeta("Design name to load", tags=[config_tag(), Widget.COMBO.tag() ]).create_attribute_model() self.field_registry.add_attribute_model("design", self.design, self.set_design) self.set_writeable_in(self.design, ss.READY) # Create an export table for mirroring exported fields self.exports = TableMeta.from_table( ExportTable, "Exported fields of child blocks", writeable=list(ExportTable.call_types), ).create_attribute_model() # Overwrite the sources meta to be a ChoiceArrayMeta self.exports.meta.elements["source"] = ChoiceArrayMeta( "Name of the block.field to export", writeable=True, tags=[Widget.COMBO.tag()], ) self.set_writeable_in(self.exports, ss.READY) self.field_registry.add_attribute_model("exports", self.exports, self.set_exports) # Create read-only indicator for when things are modified self.modified = BooleanMeta("Whether the design is modified", tags=[Widget.LED.tag() ]).create_attribute_model() self.field_registry.add_attribute_model("modified", self.modified) # Create the save method self.set_writeable_in(self.field_registry.add_method_model(self.save), ss.READY)
class DirParsePart(Part): registrar = None ioc_prod_root = "" dls_version = None def __init__(self, name: parts.APartName, ioc: AIoc) -> None: super().__init__(name) self.dls_ver_pv = ca.util.CAAttribute( StringMeta("IOC version"), ca.util.catools.DBR_STRING, "", ioc + ":DLSVER", throw=False, callback=self.version_updated, ) self.dir1_pv = ca.util.CAAttribute( StringMeta("IOC directory pt1"), ca.util.catools.DBR_STRING, "", ioc + ":APP_DIR1", widget=Widget.NONE, throw=False, callback=self.set_dir1, ) self.dir2_pv = ca.util.CAAttribute( StringMeta("IOC directory pt2"), ca.util.catools.DBR_STRING, "", ioc + ":APP_DIR2", widget=Widget.NONE, throw=False, callback=self.set_dir2, ) self.autosave_status_pv = ca.util.CAAttribute( StringMeta("IOC Status"), ca.util.catools.DBR_STRING, "", ioc + ":STATUS", throw=False, callback=self.set_procserv_state, ) self.dir1 = None self.dir2 = None self.dir = "" self.has_procserv = False elements = OrderedDict() elements["module"] = StringArrayMeta("Module", tags=[Widget.TEXTUPDATE.tag()]) elements["path"] = StringArrayMeta("Path", tags=[Widget.TEXTUPDATE.tag()]) self.dependencies = TableMeta( "Modules which this IOC depends on", tags=[Widget.TABLE.tag()], writeable=False, elements=elements, ).create_attribute_model({"module": [], "path": []}) def setup(self, registrar: PartRegistrar) -> None: super().setup(registrar) registrar.add_attribute_model("dlsVersion", self.dls_ver_pv.attr) registrar.add_attribute_model("dir1", self.dir1_pv.attr) registrar.add_attribute_model("dir2", self.dir2_pv.attr) registrar.add_attribute_model("autosaveStatus", self.autosave_status_pv.attr) registrar.add_attribute_model("dependencies", self.dependencies) self.register_hooked(hooks.DisableHook, self.disconnect) self.register_hooked((hooks.InitHook, hooks.ResetHook), self.reconnect) def reconnect(self): self.dls_ver_pv.reconnect() self.dir1_pv.reconnect() self.dir2_pv.reconnect() self.autosave_status_pv.reconnect() def disconnect(self): self.dls_ver_pv.disconnect() self.dir1_pv.disconnect() self.dir2_pv.disconnect() self.autosave_status_pv.disconnect() def set_procserv_state(self, value): if value.ok: self.has_procserv = True self.version_updated(self.dls_version) def version_updated(self, value): if value is not None and value.ok: self.dls_version = value if isinstance(value, str): if value.lower() == "work" or value.lower() == "other": message = "IOC running from non-prod area" stat = alarm.Alarm( message=message, severity=alarm.AlarmSeverity.MINOR_ALARM ) self.registrar.report(infos.HealthInfo(stat)) else: message = "OK" stat = alarm.Alarm( message=message, severity=alarm.AlarmSeverity.NO_ALARM ) self.registrar.report(infos.HealthInfo(stat)) else: if self.has_procserv: message = "IOC not running (procServ enabled)" stat = alarm.Alarm( message=message, severity=alarm.AlarmSeverity.UNDEFINED_ALARM ) self.registrar.report(infos.HealthInfo(stat)) else: message = "neither IOC nor procServ are running" stat = alarm.Alarm( message=message, severity=alarm.AlarmSeverity.INVALID_ALARM ) self.registrar.report(infos.HealthInfo(stat)) def set_dir1(self, value): if value.ok: self.dir1 = value if self.dir1 is not None and self.dir2 is not None: self.dir = self.dir1 + self.dir2 self.parse_release() def set_dir2(self, value): if value.ok: self.dir2 = value if self.dir1 is not None and self.dir2 is not None: self.dir = self.dir1 + self.dir2 self.parse_release() def parse_release(self): release_file = os.path.join(self.dir, "configure", "RELEASE") dependencies = OrderedDict() dependency_table = OrderedDict() if os.path.isdir(self.dir) and os.path.isfile(release_file): with open(release_file, "r") as release: dep_list = release.readlines() dep_list = [ dep.strip("\n") for dep in dep_list if not dep.startswith("#") ] for dep in dep_list: dep_split = dep.replace(" ", "").split("=") if len(dep_split) == 2: dependencies[dep_split[0]] = dep_split[1] dependency_table["module"] = [] dependency_table["path"] = [] for k1, v1 in dependencies.items(): for k2, v2 in dependencies.items(): dependencies[k2] = v2.replace(f"$({k1})", v1) for k1, v1 in dependencies.items(): dependency_table["module"] += [k1] dependency_table["path"] += [v1] if len(dep_list) > 0: self.dependencies.set_value(dependency_table) else: status = alarm.Alarm( message="reported IOC directory not found", severity=alarm.AlarmSeverity.MINOR_ALARM, ) self.dependencies.set_alarm(status)
def test_to_dict(self): tm = TableMeta("desc") tm.set_label("Name") tm.set_writeable(True) tm.set_elements(dict(c1=self.sam)) assert tm.to_dict() == self.serialized
class PandABoxTablePartTest(unittest.TestCase): def setUp(self): self.client = Mock() fields = OrderedDict() fields["NREPEATS"] = TableFieldData(15, 0, "Num Repeats", None, False) fields["TRIGGER"] = TableFieldData(19, 16, "Choices", ["A", "b", "CC"], False) fields["POSITION"] = TableFieldData(63, 32, "Position", None, True) fields["TIME1"] = TableFieldData(95, 64, "Time Phase A", None, False) fields["OUTA1"] = TableFieldData(20, 20, "Out1", None, False) fields["TIME2"] = TableFieldData(127, 96, "Time Phase B", None, False) fields["OUTA2"] = TableFieldData(26, 26, "Out2", None, False) self.client.get_table_fields.return_value = fields self.meta = TableMeta("Seq table", writeable=True) self.o = PandATablePart(self.client, self.meta, block_name="SEQ1", field_name="TABLE") def assert_meta(self, meta, cls, **attrs): self.assertIsInstance(meta, cls) for k, v in attrs.items(): assert meta[k] == v def test_init(self): assert list(self.meta.elements) == [ "nrepeats", "trigger", "position", "time1", "outa1", "time2", "outa2", ] self.assert_meta( self.meta.elements["nrepeats"], NumberArrayMeta, dtype="uint16", tags=["widget:textinput"], description="Num Repeats", ) self.assert_meta( self.meta.elements["trigger"], ChoiceArrayMeta, tags=["widget:combo"], description="Choices", choices=["A", "b", "CC"], ) self.assert_meta( self.meta.elements["position"], NumberArrayMeta, dtype="int32", tags=["widget:textinput"], description="Position", ) self.assert_meta( self.meta.elements["time1"], NumberArrayMeta, dtype="uint32", tags=["widget:textinput"], description="Time Phase A", ) self.assert_meta( self.meta.elements["outa1"], BooleanArrayMeta, tags=["widget:checkbox"], description="Out1", ) self.assert_meta( self.meta.elements["time2"], NumberArrayMeta, dtype="uint32", tags=["widget:textinput"], description="Time Phase B", ) self.assert_meta( self.meta.elements["outa2"], BooleanArrayMeta, tags=["widget:checkbox"], description="Out2", ) def test_list_from_table(self): table = self.meta.validate( self.meta.table_cls.from_rows([ [32, "b", -1, 4096, True, 4097, False], [0, "b", 1, 0, False, 200, True], [0, "CC", 0, 6, True, 200, False], ])) li = self.o.list_from_table(table) assert all(li == ([ 0x00110020, 4294967295, 4096, 4097, 0x04010000, 1, 0, 200, 0x00120000, 0, 6, 200, ])) def test_table_from_list(self): li = [ 0x00110020, 4294967295, 4096, 4097, 0x04010000, 1, 0, 200, 0x00120000, 0, 6, 200, ] table = self.o.table_from_list([str(x) for x in li]) assert table.nrepeats == [32, 0, 0] assert table.trigger == ["b", "b", "CC"] assert table.position == [-1, 1, 0] assert table.time1 == [4096, 0, 6] assert table.outa1 == [True, False, True] assert table.time2 == [4097, 200, 200] assert table.outa2 == [False, True, False]