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))
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))
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)
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], ]