class StatefulStates(object): """The most basic Malcolm state machine""" RESETTING = "Resetting" DISABLED = "Disabled" DISABLING = "Disabling" FAULT = "Fault" READY = "Ready" def __init__(self): self._allowed = OrderedDict() # These are all the states we can possibly be in self.possible_states = [] self.create_block_transitions() self.create_error_disable_transitions() def create_block_transitions(self): self.set_allowed(self.RESETTING, self.READY) def create_error_disable_transitions(self): block_states = self.possible_states[:] # Set transitions for standard states for state in block_states: self.set_allowed(state, self.FAULT) self.set_allowed(state, self.DISABLING) self.set_allowed(self.FAULT, [self.RESETTING, self.DISABLING]) self.set_allowed(self.DISABLING, [self.FAULT, self.DISABLED]) self.set_allowed(self.DISABLED, self.RESETTING) def transition_allowed(self, initial_state, target_state): """ Check if a transition between two states is allowed Args: initial_state(str): Initial state target_state(str): Target state Returns: bool: True if allowed, False if not """ assert initial_state in self._allowed, \ "%s is not in %s" % (initial_state, list(self._allowed)) return target_state in self._allowed[initial_state] def set_allowed(self, initial_state, allowed_states): """Add an allowed transition state Args: initial_state (str): Initial state allowed_states (str or list): state or list of states that initial_state can transition to """ if not isinstance(allowed_states, list): allowed_states = [allowed_states] self._allowed.setdefault(initial_state, set()).update(allowed_states) for state in allowed_states + [initial_state]: if state not in self.possible_states: self.possible_states.append(state)
def handle_changes(self, changes): for k, v in changes: self.changes[k] = v block_changes = OrderedDict() for full_field, val in list(self.changes.items()): block_name, field_name = full_field.split(".", 1) block_changes.setdefault(block_name, []).append(( field_name, full_field, val)) for block_name, field_changes in block_changes.items(): # Squash changes block_mri = "%s:%s" % (self.mri, block_name) try: block_controller = self.process.get_controller(block_mri) except ValueError: self.log.debug("Block %s not known", block_name) for _, full_field, _ in field_changes: self.changes.pop(full_field) else: with block_controller.changes_squashed: self.do_field_changes(block_name, field_changes)
class StateSet: def __init__(self) -> None: self._allowed = OrderedDict() # These are all the states we can possibly be in self.possible_states: List[str] = [] def transition_allowed(self, initial_state: str, target_state: str) -> bool: """Check if a transition between two states is allowed""" assert (initial_state in self._allowed ), f"{initial_state} is not in {list(self._allowed)}" return target_state in self._allowed[initial_state] def set_allowed(self, initial_state: str, *allowed_states: str) -> None: """Add an allowed transition from initial_state to allowed_states""" allowed_states_list = list(allowed_states) self._allowed.setdefault(initial_state, set()).update(allowed_states_list) for state in allowed_states_list + [initial_state]: if state not in self.possible_states: self.possible_states.append(state)
class StateSet(object): def __init__(self): # type: () -> None self._allowed = OrderedDict() # These are all the states we can possibly be in self.possible_states = [] def transition_allowed(self, initial_state, target_state): # type: (str, str) -> bool """Check if a transition between two states is allowed""" assert initial_state in self._allowed, \ "%s is not in %s" % (initial_state, list(self._allowed)) return target_state in self._allowed[initial_state] def set_allowed(self, initial_state, *allowed_states): # type: (str, *str) -> None """Add an allowed transition from initial_state to allowed_states""" allowed_states = list(allowed_states) self._allowed.setdefault(initial_state, set()).update(allowed_states) for state in allowed_states + [initial_state]: if state not in self.possible_states: self.possible_states.append(state)
class FieldRegistry(object): def __init__(self): # type: () -> None self.fields = OrderedDict() # type: FieldDict def get_field(self, name): # type: (str) -> Field for fields in self.fields.values(): for (n, field, _) in fields: if n == name: return field raise ValueError("No field named %s found" % (name,)) def add_method_model(self, func, # type: Callable name=None, # type: Optional[str] description=None, # type: Optional[str] owner=None, # type: object ): # type: (...) -> MethodModel """Register a function to be added to the block""" if name is None: name = func.__name__ method = MethodModel.from_callable(func, description) self._add_field(owner, name, method, func) return method def add_attribute_model(self, name, # type: str attr, # type: AttributeModel writeable_func=None, # type: Optional[Callable] owner=None, # type: object ): # type: (...) -> AttributeModel self._add_field(owner, name, attr, writeable_func) return attr def _add_field(self, owner, name, model, writeable_func): # type: (object, str, Field, Callable) -> None assert CAMEL_RE.match(name), \ "Field %r published by %s is not camelCase" % (name, owner) part_fields = self.fields.setdefault(owner, []) part_fields.append((name, model, writeable_func))
def do_save(self, design=""): if not design: design = self.design.value assert design, "Please specify save design name when saving from new" assert not design.startswith( "template_"), "Cannot save over a template" structure = OrderedDict() attributes = structure.setdefault("attributes", OrderedDict()) # Add the layout table layout = attributes.setdefault("layout", OrderedDict()) for name, mri, x, y, visible in self.layout.value.rows(): layout_structure = OrderedDict() layout_structure["x"] = x layout_structure["y"] = y layout_structure["visible"] = visible layout[name] = layout_structure # Add the exports table exports = attributes.setdefault("exports", OrderedDict()) for source, export in self.exports.value.rows(): exports[source] = export # Add other attributes for name, attribute in self.our_config_attributes.items(): attributes[name] = attribute.value # Add any structure that a child part wants to save structure["children"] = self.run_hooks( SaveHook(p, c) for p, c in self.create_part_contexts(only_visible=False).items()) text = json_encode(structure, indent=2) filename = self._validated_config_filename(design) if filename.startswith("/tmp"): self.log.warning("Saving to tmp directory %s" % filename) with open(filename, "w") as f: f.write(text) # Run a sync command to make sure we flush this file to disk subprocess.call("sync") # Try and commit the file to git, don't care if it fails self._run_git_cmd("add", filename) msg = "Saved %s %s" % (self.mri, design) self._run_git_cmd("commit", "--allow-empty", "-m", msg, filename) self._mark_clean(design)
class StateMachine(Loggable): RESETTING = "Resetting" DISABLED = "Disabled" DISABLING = "Disabling" FAULT = "Fault" # Subclasses must override this AFTER_RESETTING = None def __init__(self): self.set_logger_name(type(self).__name__) self.allowed_transitions = OrderedDict() self.busy_states = [] assert self.AFTER_RESETTING is not None, \ "No AFTER_RESETTING state given" self.set_allowed(self.RESETTING, self.AFTER_RESETTING) self.set_busy(self.RESETTING) self.create_states() custom_states = list(self.allowed_transitions) + [self.AFTER_RESETTING] # Set transitions for standard states for state in custom_states: self.set_allowed(state, self.FAULT) self.set_allowed(state, self.DISABLING) self.set_allowed(self.FAULT, [self.RESETTING, self.DISABLING]) self.set_allowed(self.DISABLING, [self.FAULT, self.DISABLED]) self.set_allowed(self.DISABLED, self.RESETTING) # These are all the states we can possibly be in self.possible_states = list(self.allowed_transitions) def create_states(self): raise NotImplementedError() def is_allowed(self, initial_state, target_state): """ Check if a transition between two states is allowed Args: initial_state(str): Initial state target_state(str): Target state Returns: bool: True if allowed, False if not """ assert initial_state in self.allowed_transitions, \ "%s is not in %s" % (initial_state, list(self.allowed_transitions)) return target_state in self.allowed_transitions[initial_state] def set_allowed(self, initial_state, allowed_states): """ Add an allowed transition state Args: initial_state(str): Initial state allowed_states(list(str) / str): States that initial_state can transition to """ if not isinstance(allowed_states, list): allowed_states = [allowed_states] self.allowed_transitions.setdefault(initial_state, set()).update( allowed_states) def set_busy(self, state, busy=True): """ Set the busy-ness of a state; i.e. whether the block is considered to be busy in a certain state Args: state(str): State to update busy(bool): True or False for whether state is a busy state """ if not busy and state in self.busy_states: self.busy_states.remove(state) elif busy and state not in self.busy_states: self.busy_states.append(state) def is_busy(self, state): """ Check if a state is a busy state Args: state(str): State to check busy-ness for Returns: bool: True if state is a busy state, False if not """ return state in self.busy_states
class StateMachine(Loggable): RESETTING = "Resetting" DISABLED = "Disabled" DISABLING = "Disabling" FAULT = "Fault" # Subclasses must override this AFTER_RESETTING = None def __init__(self): self.set_logger_name(type(self).__name__) self.allowed_transitions = OrderedDict() self.busy_states = [] assert self.AFTER_RESETTING is not None, \ "No AFTER_RESETTING state given" self.set_allowed(self.RESETTING, self.AFTER_RESETTING) self.set_busy(self.RESETTING) self.create_states() custom_states = list(self.allowed_transitions) + [self.AFTER_RESETTING] # Set transitions for standard states for state in custom_states: self.set_allowed(state, self.FAULT) self.set_allowed(state, self.DISABLING) self.set_allowed(self.FAULT, [self.RESETTING, self.DISABLING]) self.set_allowed(self.DISABLING, [self.FAULT, self.DISABLED]) self.set_allowed(self.DISABLED, self.RESETTING) # These are all the states we can possibly be in self.possible_states = list(self.allowed_transitions) def create_states(self): raise NotImplementedError() def is_allowed(self, initial_state, target_state): """ Check if a transition between two states is allowed Args: initial_state(str): Initial state target_state(str): Target state Returns: bool: True if allowed, False if not """ assert initial_state in self.allowed_transitions, \ "%s is not in %s" % (initial_state, list(self.allowed_transitions)) return target_state in self.allowed_transitions[initial_state] def set_allowed(self, initial_state, allowed_states): """ Add an allowed transition state Args: initial_state(str): Initial state allowed_states(list(str) / str): States that initial_state can transition to """ if not isinstance(allowed_states, list): allowed_states = [allowed_states] self.allowed_transitions.setdefault(initial_state, set()).update(allowed_states) def set_busy(self, state, busy=True): """ Set the busy-ness of a state; i.e. whether the block is considered to be busy in a certain state Args: state(str): State to update busy(bool): True or False for whether state is a busy state """ if not busy and state in self.busy_states: self.busy_states.remove(state) elif busy and state not in self.busy_states: self.busy_states.append(state) def is_busy(self, state): """ Check if a state is a busy state Args: state(str): State to check busy-ness for Returns: bool: True if state is a busy state, False if not """ return state in self.busy_states