Ejemplo n.º 1
0
    def transition(self, state, message=""):
        """Change to a new state if the transition is allowed

        Args:
            state (str): State to transition to
            message (str): Message if the transition is to a fault state
        """
        with self.changes_squashed:
            initial_state = self.state.value
            if self.state_set.transition_allowed(initial_state=initial_state,
                                                 target_state=state):
                self.log.debug("%s: Transitioning from %s to %s", self.mri,
                               initial_state, state)
                if state == ss.DISABLED:
                    alarm = Alarm.invalid("Disabled")
                elif state == ss.FAULT:
                    alarm = Alarm.major(message)
                else:
                    alarm = Alarm()
                self.update_health(self, HealthInfo(alarm))
                self.state.set_value(state)
                self.state.set_alarm(alarm)
                for child, writeable in self._children_writeable[state].items(
                ):
                    child.meta.set_writeable(writeable)
            else:
                raise TypeError("Cannot transition from %s to %s" %
                                (initial_state, state))
Ejemplo n.º 2
0
    def transition(self, state, message=""):
        """Change to a new state if the transition is allowed

        Args:
            state (str): State to transition to
            message (str): Message if the transition is to a fault state
        """
        with self.changes_squashed:
            initial_state = self.state.value
            if self.stateSet.transition_allowed(
                    initial_state=initial_state, target_state=state):
                self.log.debug(
                    "Transitioning from %s to %s", initial_state, state)
                if state == ss.DISABLED:
                    alarm = Alarm.invalid("Disabled")
                elif state == ss.FAULT:
                    alarm = Alarm.major(message)
                else:
                    alarm = Alarm()
                self.update_health(self, alarm)
                self.state.set_value(state)
                self.state.set_alarm(alarm)
                for child, writeable in self._children_writeable[state].items():
                    if isinstance(child, AttributeModel):
                        child.meta.set_writeable(writeable)
                    elif isinstance(child, MethodModel):
                        child.set_writeable(writeable)
                        for element in child.takes.elements.values():
                            element.set_writeable(writeable)
            else:
                raise TypeError("Cannot transition from %s to %s" %
                                (initial_state, state))
Ejemplo n.º 3
0
    def test_set_health(self):
        self.update_health(1, Alarm(severity=AlarmSeverity.MINOR_ALARM))
        self.update_health(2, Alarm(severity=AlarmSeverity.MAJOR_ALARM))
        assert self.b.health.alarm.severity == AlarmSeverity.MAJOR_ALARM

        self.update_health(1, Alarm(severity=AlarmSeverity.UNDEFINED_ALARM))
        self.update_health(2, Alarm(severity=AlarmSeverity.INVALID_ALARM))
        assert self.b.health.alarm.severity == AlarmSeverity.UNDEFINED_ALARM

        self.update_health(1)
        self.update_health(2)
        assert self.o.health.value == "OK"
Ejemplo n.º 4
0
 def _update_value(self, value):
     if not value.ok:
         self.attr.set_value(None, alarm=Alarm.invalid("PV disconnected"))
     else:
         if value.severity:
             alarm = Alarm(severity=value.severity,
                           status=AlarmStatus.RECORD_STATUS,
                           message="PV in alarm state")
         else:
             alarm = Alarm.ok
         # We only have a raw_stamp attr on monitor, the initial
         # caget with CTRL doesn't give us a timestamp
         ts = TimeStamp(*getattr(value, "raw_stamp", (None, None)))
         value = self.attr.meta.validate(value)
         self.attr.set_value_alarm_ts(value, alarm, ts)
Ejemplo n.º 5
0
 def setUp(self):
     self.serialized = OrderedDict()
     self.serialized["typeid"] = "epics:nt/NTScalar:1.0"
     self.serialized["value"] = "some string"
     self.serialized["alarm"] = Alarm().to_dict()
     self.serialized["timeStamp"] = TimeStamp().to_dict()
     self.serialized["meta"] = StringMeta("desc").to_dict()
Ejemplo n.º 6
0
 def update_part_modified(self, response):
     subscribe = self.config_subscriptions[response.id]
     name = subscribe.path[-2]
     original_value = self.saved_structure[name]
     try:
         np.testing.assert_equal(original_value, response.value)
     except AssertionError:
         message = "%s.%s.value = %r not %r" % (
             self.name, name, response.value, original_value)
         if name in self.we_modified:
             message = "(We modified) " + message
         self.modified_messages[name] = message
     else:
         self.modified_messages.pop(name, None)
     message_list = []
     only_modified_by_us = True
     # Tell the controller what has changed
     for name, message in self.modified_messages.items():
         if name not in self.we_modified:
             only_modified_by_us = False
         message_list.append(message)
     if message_list:
         if only_modified_by_us:
             severity = AlarmSeverity.NO_ALARM
         else:
             severity = AlarmSeverity.MINOR_ALARM
         alarm = Alarm(severity, AlarmStatus.CONF_STATUS,
                       "\n".join(message_list))
     else:
         alarm = None
     # Put data on the queue, so if spawns are handled out of order we
     # still get the most up to date data
     self.modified_update_queue.put(alarm)
     self.spawn(self._update_part_modified).wait()
Ejemplo n.º 7
0
 def __init__(self, mri: AMri, comms: AComms, publish: APublish = False) -> None:
     super().__init__(mri)
     self.comms = comms
     self.publish = publish
     self.client_comms: Optional[ClientComms] = None
     self.health.set_value("Uninitialized", alarm=Alarm.invalid("Uninitialized"))
     # Hooks
     self.register_hooked(ProcessStartHook, self.init)
Ejemplo n.º 8
0
 def __init__(self, process, parts, params):
     self.params = params
     super(ProxyController, self).__init__(process, params.mri, parts)
     self.client_comms = process.get_controller(params.comms)
     self.update_health(self, Alarm.invalid("Uninitialized"))
     self._response_queue = Queue()
     self._notify_response = True
     self._first_response_queue = Queue()
Ejemplo n.º 9
0
 def _update_value(self, value):
     # type: (Any) -> None
     if not value.ok or value.severity != 0:
         self.attr.set_value(None, alarm=Alarm.invalid("Bad PV value"))
     else:
         # Split "@asyn(PORT,num)" into ["PORT", "num"]
         split = value.split("(")[1].rstrip(")").split(",")
         cs_port = split[0].strip()
         cs_axis = cs_axis_names[int(split[1].strip()) - 1]
         self.attr.set_value("%s,%s" % (cs_port, cs_axis))
Ejemplo n.º 10
0
 def _update_value(self, value: Any) -> None:
     if not value.ok:
         self.attr.set_value(None,
                             alarm=Alarm.disconnected("PV Disconnected"))
     else:
         # Split "@asyn(PORT,num)" into ["PORT", "num"]
         split = value.split("(")[1].rstrip(")").split(",")
         cs_port = split[0].strip()
         cs_axis = CS_AXIS_NAMES[int(split[1].strip()) - 1]
         self.attr.set_value(f"{cs_port},{cs_axis}")
Ejemplo n.º 11
0
 def __init__(self, mri, comms, publish=False):
     # type: (AMri, AComms, APublish) -> None
     super(ProxyController, self).__init__(mri)
     self.comms = comms
     self.publish = publish
     self.client_comms = None
     self.health.set_value("Uninitialized",
                           alarm=Alarm.invalid("Uninitialized"))
     # Hooks
     self.register_hooked(ProcessStartHook, self.init)
Ejemplo n.º 12
0
 def _update_value(self, value):
     # Attribute value might not be raw PV, PV which triggered update is
     # passed as status
     if self._user_callback is not None:
         self._user_callback(value)
     if not value.ok:
         self.attr.set_value(self.attr.value,
                             alarm=Alarm.disconnected("PV disconnected"))
     else:
         if value.severity:
             alarm = Alarm(
                 severity=value.severity,
                 status=AlarmStatus.RECORD_STATUS,
                 message="PV in alarm state",
             )
         else:
             alarm = Alarm.ok
         # We only have a raw_stamp attr on monitor, the initial
         # caget with CTRL doesn't give us a timestamp
         ts = TimeStamp(*getattr(value, "raw_stamp", (None, None)))
         value = self.attr.meta.validate(value)
         self.attr.set_value_alarm_ts(value, alarm, ts)
Ejemplo n.º 13
0
 def _update_value(self, value, index):
     if index == 0:
         # Got CS Port
         if not value.ok:
             self.port = None
         elif value == 0:
             self.port = ""
         else:
             self.port = self.port_choices[value]
     elif index == 1:
         # Got CS Axis
         if value.ok and str(value) in CS_AXIS_NAMES + ["I"]:
             self.axis = value
         else:
             self.axis = None
     else:
         # Got PMAC Port name
         if value.ok:
             # Split "@asyn(PORT,num)" into ["PORT", "num"]
             split = value.split("(")[1].rstrip(")").split(",")
             self.pmac_attr.set_value(split[0].strip())
             self.axis_num_attr.set_value(split[1].strip())
         else:
             self.pmac_attr.set_value(None,
                                      alarm=Alarm.invalid("Bad PV value"))
             self.axis_num_attr.set_value(
                 None, alarm=Alarm.invalid("Bad PV value"))
     if self.port is None or self.axis is None:
         # Bad value or PV disconnected
         self.cs_attr.set_value(None, alarm=Alarm.invalid("Bad PV value"))
     elif self.port and self.axis:
         # Connected to a port
         self.cs_attr.set_value("%s,%s" % (self.port, self.axis))
     else:
         # Not connected to a port
         self.cs_attr.set_value("")
Ejemplo n.º 14
0
 def callback(value=None):
     if isinstance(value, Exception):
         # Disconnect or Cancelled or RemoteError
         if isinstance(value, Disconnected):
             # We will get a reconnect with a whole new structure
             update_fields.clear()
             block.health.set_value(
                 value="pvAccess disconnected",
                 alarm=Alarm.disconnected("pvAccess disconnected"))
     else:
         with block.notifier.changes_squashed:
             if not update_fields:
                 self.log.debug("Regenerating from %s", list(value))
                 self._regenerate_block(block, value, update_fields)
                 done_queue.put(None)
             else:
                 self._update_block(block, value, update_fields)
Ejemplo n.º 15
0
 def handle_changes(self, changes: Dict[str, Any], ts: TimeStamp) -> None:
     with self.changes_squashed:
         icon_needs_update = False
         if isinstance(changes, Dict):
             for k, v in changes.items():
                 # Health changes are for us
                 if k.upper() == "HEALTH":
                     if v.upper() == "OK":
                         alarm = Alarm.ok
                     else:
                         alarm = Alarm.major(v)
                     self.update_health(
                         self, builtin.infos.HealthInfo(cast(Alarm, alarm), ts)
                     )
                     continue
                 # Work out if there is a part we need to notify
                 try:
                     part = self.field_parts[k]
                 except KeyError:
                     self.log.exception(f"Can't handle field {self.block_name}.{k}")
                     part = None
                 if part is None:
                     continue
                 part.handle_change(v, ts)
                 if not icon_needs_update:
                     icon_needs_update = k in self.icon_part.update_fields
                 try:
                     mux_meta = self.mux_metas[k]
                 except KeyError:
                     pass
                 else:
                     self._handle_mux_update(mux_meta, v)
         if icon_needs_update:
             d = {}
             for key in self.icon_part.update_fields:
                 if key in self.field_parts:
                     field_part = self.field_parts[key]
                     if field_part:
                         d[key] = field_part.attr.value
             icon = builtin.util.SVGIcon(self.icon_part.svg_text)
             self.icon_part.update_icon(icon, d)
             self.icon_part.attr.set_value(str(icon), ts=ts)
Ejemplo n.º 16
0
 def update_modified(self,
                     part: Part = None,
                     info: PartModifiedInfo = None) -> None:
     with self.changes_squashed:
         if part:
             assert info, "No info to update part"
             # Update the alarm for the given part
             self.part_modified[part] = info
         # Find the modified alarms for each visible part
         message_list = []
         only_modified_by_us = True
         for part_name, visible in zip(self.layout.value.name,
                                       self.layout.value.visible):
             part = self.parts[part_name]
             info = self.part_modified.get(part, None)
             if visible and info:
                 for name, message in sorted(info.modified.items()):
                     # Attribute flagged as been modified, is it by the
                     # context we passed to the part?
                     if name in self.context_modified.get(part, {}):
                         message = "(We modified) %s" % (message, )
                     else:
                         only_modified_by_us = False
                     message_list.append(message)
         # Add in any modification messages from the layout and export tables
         if self.layout.value.visible != self.saved_visibility:
             message_list.append("layout changed")
             only_modified_by_us = False
         if self.exports.value != self.saved_exports:
             message_list.append("exports changed")
             only_modified_by_us = False
         if message_list:
             if only_modified_by_us:
                 severity = AlarmSeverity.NO_ALARM
             else:
                 severity = AlarmSeverity.MINOR_ALARM
             alarm = Alarm(severity, AlarmStatus.CONF_STATUS,
                           "\n".join(message_list))
             self.modified.set_value(True, alarm=alarm)
         else:
             self.modified.set_value(False)
Ejemplo n.º 17
0
 def update_modified(self, part=None, alarm=None):
     with self.changes_squashed:
         # Update the alarm for the given part
         if part:
             self.part_modified[part] = alarm
         # Find the modified alarms for each visible part
         message_list = []
         only_modified_by_us = True
         for part_name, visible in zip(
                 self.layout.value.name, self.layout.value.visible):
             if visible:
                 alarm = self.part_modified.get(self.parts[part_name], None)
                 if alarm:
                     # Part flagged as been modified, is it by us?
                     if alarm.severity:
                         only_modified_by_us = False
                     message_list.append(alarm.message)
         # Add in any modification messages from the layout and export tables
         try:
             np.testing.assert_equal(
                 self.layout.value.visible, self.saved_visibility)
         except AssertionError:
             message_list.append("layout changed")
             only_modified_by_us = False
         try:
             np.testing.assert_equal(
                 self.exports.value.to_dict(), self.saved_exports)
         except AssertionError:
             message_list.append("exports changed")
             only_modified_by_us = False
         if message_list:
             if only_modified_by_us:
                 severity = AlarmSeverity.NO_ALARM
             else:
                 severity = AlarmSeverity.MINOR_ALARM
             alarm = Alarm(
                 severity, AlarmStatus.CONF_STATUS, "\n".join(message_list))
             self.modified.set_value(True, alarm=alarm)
         else:
             self.modified.set_value(False)
Ejemplo n.º 18
0
 def _update_value(self, value, index):
     if index == 0:
         if not value.ok:
             self.port = None
         elif value == 0:
             self.port = ""
         else:
             self.port = self.port_choices[value]
     else:
         if value.ok and str(value) in cs_axis_names + ["I"]:
             self.axis = value
         else:
             self.axis = None
     if self.port is None or self.axis is None:
         # Bad value or PV disconnected
         self.attr.set_value(None, alarm=Alarm.invalid("Bad PV value"))
     elif self.port and self.axis:
         # Connected to a port
         self.attr.set_value("%s,%s" % (self.port, self.axis))
     else:
         # Not connected to a port
         self.attr.set_value("")
Ejemplo n.º 19
0
 def setUp(self):
     elements = OrderedDict()
     elements["foo"] = StringArrayMeta(label="Foo").to_dict()
     elements["bar"] = StringArrayMeta().to_dict()
     meta = OrderedDict()
     meta["typeid"] = "malcolm:core/TableMeta:1.0"
     meta["description"] = "desc"
     meta["tags"] = []
     meta["writeable"] = True
     meta["label"] = "my label"
     meta["elements"] = elements
     value = OrderedDict()
     value["typeid"] = "malcolm:core/Table:1.0"
     value["foo"] = ["foo1", "foo2"]
     value["bar"] = ["bar1", "bar2"]
     self.serialized = OrderedDict()
     self.serialized["typeid"] = "epics:nt/NTTable:1.0"
     self.serialized["labels"] = ["Foo", "bar"]
     self.serialized["value"] = value
     self.serialized["alarm"] = Alarm().to_dict()
     self.serialized["timeStamp"] = TimeStamp().to_dict()
     self.serialized["meta"] = meta
Ejemplo n.º 20
0
    def test_block_fields_adder(self):
        fields = OrderedDict()
        block_data = BlockData(2, "Adder description", fields)
        fields["INPA"] = FieldData("pos_mux", "", "Input A", ["A.OUT", "B.OUT"])
        fields["INPB"] = FieldData("pos_mux", "", "Input B", ["A.OUT", "B.OUT"])
        fields["DIVIDE"] = FieldData(
            "param", "enum", "Divide output", ["/1", "/2", "/4"]
        )
        fields["OUT"] = FieldData("pos_out", "", "Output", ["No", "Capture"])
        fields["HEALTH"] = FieldData("read", "enum", "What's wrong", ["OK", "Very Bad"])

        o = PandABlockController(self.client, "MRI", "ADDER1", block_data, "/docs")
        self.process.add_controller(o)
        b = self.process.block_view("MRI:ADDER1")

        assert list(b) == [
            "meta",
            "health",
            "icon",
            "label",
            "help",
            "inputs",
            "inpa",
            "inpb",
            "parameters",
            "divide",
            "outputs",
            "out",
        ]

        group = b.inputs
        assert group.meta.tags == ["widget:group", "config:1"]

        inpa = b.inpa
        assert inpa.meta.writeable is True
        assert inpa.meta.typeid == ChoiceMeta.typeid
        assert inpa.meta.tags == [
            "group:inputs",
            "sinkPort:int32:ZERO",
            "widget:combo",
            "config:1",
        ]
        assert inpa.meta.choices == ["A.OUT", "B.OUT"]
        inpa.put_value("A.OUT")
        self.client.set_field.assert_called_once_with("ADDER1", "INPA", "A.OUT")
        self.client.reset_mock()

        divide = b.divide
        assert divide.meta.writeable is True
        assert divide.meta.typeid == ChoiceMeta.typeid
        assert divide.meta.tags == ["group:parameters", "widget:combo", "config:1"]
        assert divide.meta.choices == ["/1", "/2", "/4"]

        out = b.out
        assert out.meta.writeable is False
        assert out.meta.typeid == NumberMeta.typeid
        assert out.meta.dtype == "int32"
        assert out.meta.tags == [
            "group:outputs",
            "sourcePort:int32:ADDER1.OUT",
            "widget:textupdate",
        ]

        queue = Queue()
        subscribe = Subscribe(path=["MRI:ADDER1", "out"], delta=True)
        subscribe.set_callback(queue.put)
        o.handle_request(subscribe)
        delta = queue.get(timeout=1)
        assert delta.changes[0][1]["value"] == 0

        ts = TimeStamp()
        o.handle_changes({"OUT": "145"}, ts)
        delta = queue.get(timeout=1)
        assert delta.changes == [
            [["value"], 145],
            [["timeStamp"], ts],
        ]

        subscribe = Subscribe(path=["MRI:ADDER1", "health"], delta=True)
        subscribe.set_callback(queue.put)
        o.handle_request(subscribe)
        delta = queue.get(timeout=1)
        assert delta.changes[0][1]["value"] == "OK"

        ts = TimeStamp()
        o.handle_changes({"HEALTH": "Very Bad"}, ts)
        delta = queue.get(timeout=1)
        assert delta.changes == [
            [["value"], "Very Bad"],
            [["alarm"], Alarm.major("Very Bad")],
            [["timeStamp"], ts],
        ]
        o.handle_changes({"HEALTH": "OK"}, ts)
        delta = queue.get(timeout=1)
        assert delta.changes == [
            [["value"], "OK"],
            [["alarm"], Alarm.ok],
            [["timeStamp"], ts],
        ]
Ejemplo n.º 21
0
 def test_set_alarm(self):
     alarm = Alarm(AlarmSeverity.MAJOR_ALARM, AlarmStatus.DEVICE_STATUS,
                   "bad")
     self.o.set_alarm(alarm)
     assert self.o.alarm == alarm
Ejemplo n.º 22
0
    def __init__(
        self,
        mri: builtin.controllers.AMri,
        prefix: APvPrefix,
        config_dir: builtin.controllers.AConfigDir,
        ioc_list: AIocList = "",
    ) -> None:
        super().__init__(mri, config_dir)
        self.ioc = None
        self.ioc_blocks: OrderedDict = OrderedDict()
        self.prefix = prefix
        self.bl_iocs = ioc_list.split(" ")
        if self.bl_iocs[-1] == "":
            self.bl_iocs = self.bl_iocs[:-1]
        self.stats = dict()
        # TODO: the following stuff is all Linux-specific....
        sys_call_bytes = (
            open("/proc/%s/cmdline" % os.getpid(), "rb").read().split(b"\0")
        )
        sys_call = [el.decode("utf-8") for el in sys_call_bytes]
        self.stats["pymalcolm_path"] = os.path.abspath(sys_call[1])
        self.stats["yaml_path"] = os.path.abspath(sys_call[2])

        self.stats["yaml_ver"] = self.parse_yaml_version(
            self.stats["yaml_path"], "/dls_sw/work", "/dls_sw/prod"
        )

        self.stats["pymalcolm_ver"] = __version__
        hostname = os.uname()[1]
        self.stats["kernel"] = "%s %s" % (os.uname()[0], os.uname()[2])
        self.stats["hostname"] = (
            hostname if len(hostname) < 39 else hostname[:35] + "..."
        )
        self.stats["pid"] = str(os.getpid())

        self.pymalcolm_path = StringMeta(
            "Path to pymalcolm executable", tags=[Widget.MULTILINETEXTUPDATE.tag()]
        ).create_attribute_model(self.stats["pymalcolm_path"])
        self.pymalcolm_ver = StringMeta(
            "Version of pymalcolm executable", tags=[Widget.TEXTUPDATE.tag()]
        ).create_attribute_model(self.stats["pymalcolm_ver"])
        self.yaml_path = StringMeta(
            "Path to yaml configuration file", tags=[Widget.MULTILINETEXTUPDATE.tag()]
        ).create_attribute_model(self.stats["yaml_path"])
        self.yaml_ver = StringMeta(
            "version of yaml configuration file", tags=[Widget.TEXTUPDATE.tag()]
        ).create_attribute_model(self.stats["yaml_ver"])
        self.hostname = StringMeta(
            "Name of host machine", tags=[Widget.TEXTUPDATE.tag()]
        ).create_attribute_model(self.stats["hostname"])
        self.kernel = StringMeta(
            "Kernel of host machine", tags=[Widget.TEXTUPDATE.tag()]
        ).create_attribute_model(self.stats["kernel"])
        self.pid = StringMeta(
            "process ID of pymalcolm instance", tags=[Widget.TEXTUPDATE.tag()]
        ).create_attribute_model(self.stats["pid"])

        self.field_registry.add_attribute_model("pymalcolmPath", self.pymalcolm_path)
        self.field_registry.add_attribute_model("pymalcolmVer", self.pymalcolm_ver)
        self.field_registry.add_attribute_model("yamlPath", self.yaml_path)
        self.field_registry.add_attribute_model("yamlVer", self.yaml_ver)
        self.field_registry.add_attribute_model("hostname", self.hostname)
        self.field_registry.add_attribute_model("kernel", self.kernel)
        self.field_registry.add_attribute_model("pid", self.pid)

        if self.stats["yaml_ver"] in ["work", "unknown"]:
            message = "Non-prod YAML config"
            alarm = Alarm(message=message, severity=AlarmSeverity.MINOR_ALARM)
            self.update_health("", builtin.infos.HealthInfo(alarm))

        self.register_hooked(ProcessStartHook, self.init)

        self.register_hooked(ProcessStopHook, self.stop_ioc)