def _execute(self, execute_inputs, execute_outputs, backward_execution=False): """Calls the custom execute function of the script.py of the state""" plugins.run_hook('pre_script') outcome_item = self._script.execute(self, execute_inputs, execute_outputs, backward_execution) plugins.run_hook('post_script') # in the case of backward execution the outcome is not relevant if backward_execution: return # If the state was preempted, the state must be left on the preempted outcome if self.preempted: return Outcome(-2, "preempted") # Outcome id was returned if outcome_item in self.outcomes: return self.outcomes[outcome_item] # Outcome name was returned for outcome_id, outcome in self.outcomes.items(): if outcome.name == outcome_item: return self.outcomes[outcome_id] logger.error("Returned outcome of {0} not existing: {1}".format(self, outcome_item)) return Outcome(-1, "aborted")
def _finalize_hierarchy(self): """ This function finalizes the execution of a hierarchy state. It sets the correct status and manages the output data handling. :return: """ if self.last_child: self.last_child.state_execution_status = StateExecutionStatus.INACTIVE if not self.backward_execution: if self.last_error: self.output_data['error'] = copy.deepcopy(self.last_error) self.write_output_data() self.check_output_data_type() self.execution_history.push_return_history_item(self, CallType.CONTAINER, self, self.output_data) # add error message from child_state to own output_data self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE if self.preempted: self.final_outcome = Outcome(-2, "preempted") self.child_state = None self.last_child = None return self.finalize(self.final_outcome)
def run(self): """ This defines the sequence of actions that are taken when the hierarchy is executed. A hierarchy state executes all its child states recursively. Principally this code collects all input data for the next child state, executes it, stores its output data and determines the next state based on the outcome of the child state. :return: """ try: self._initialize_hierarchy() while self.child_state is not self: self.handling_execution_mode = True execution_mode = singleton.state_machine_execution_engine.handle_execution_mode(self, self.child_state) # in the case of starting the sm from a specific state not the transitions define the logic flow # but the the execution_engine.run_to_states; thus, do not alter the next state in this case if not self._start_state_modified: # check if e.g. the state machine was paused and the next state was modified (e.g. removed) self.check_if_child_state_was_modified() self.handling_execution_mode = False if self.state_execution_status is not StateExecutionStatus.EXECUTE_CHILDREN: self.state_execution_status = StateExecutionStatus.EXECUTE_CHILDREN self.backward_execution = False # TODO: why is this line needed? if self.preempted: if self.last_transition and self.last_transition.from_outcome == -2: logger.debug("Execute preemption handling for '{0}'".format(self.child_state)) else: break elif execution_mode == StateMachineExecutionStatus.BACKWARD: break_loop = self._handle_backward_execution_before_child_execution() if break_loop: break # This is only the case if this hierarchy-state is started in backward mode, # but the user directly switches to the forward execution mode if self.child_state is None: break self._execute_current_child() if self.backward_execution: break_loop = self._handle_backward_execution_after_child_execution() if break_loop: break else: break_loop = self._handle_forward_execution_after_child_execution() if break_loop: break return self._finalize_hierarchy() except Exception as e: logger.exception("{0} had an internal error:".format(self)) self.output_data["error"] = e self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE self.child_state = None self.last_child = None return self.finalize(Outcome(-1, "aborted"))
def run(self): """ This defines the sequence of actions that are taken when the execution state is executed :return: """ if self.is_root_state: self.execution_history.push_call_history_item( self, CallType.EXECUTE, None, self.input_data) logger.debug("Running {0}{1}".format( self, " (backwards)" if self.backward_execution else "")) if self.backward_execution: self.setup_backward_run() else: self.setup_run() try: outcome = self._execute(self.input_data, self.output_data, self.backward_execution) self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE if self.backward_execution: # outcome handling is not required as we are in backward mode and the execution order is fixed result = self.finalize() else: # check output data self.check_output_data_type() result = self.finalize(outcome) if self.is_root_state: self.execution_history.push_return_history_item( self, CallType.EXECUTE, None, self.output_data) return result except Exception as e: exc_type, exc_value, exc_traceback = sys.exc_info() formatted_exc = traceback.format_exception(exc_type, exc_value, exc_traceback) truncated_exc = [] for line in formatted_exc: if os.path.join("rafcon", "core") not in line: truncated_exc.append(line) logger.error("{0} had an internal error: {1}: {2}\n{3}".format( self, type(e).__name__, e, ''.join(truncated_exc))) # write error to the output_data of the state self.output_data["error"] = e self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE return self.finalize(Outcome(-1, "aborted"))
def finalize_concurrency_state(self, outcome): """ Utility function to finalize the forward execution of the concurrency state. :param outcome: :return: """ final_outcome = outcome self.write_output_data() self.check_output_data_type() self.execution_history.push_return_history_item(self, CallType.CONTAINER, self, self.output_data) self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE singleton.state_machine_execution_engine.modify_run_to_states(self) if self.preempted: final_outcome = Outcome(-2, "preempted") return self.finalize(final_outcome)
def run(self): """ This defines the sequence of actions that are taken when the barrier concurrency state is executed :return: """ logger.debug("Starting execution of {0}{1}".format(self, " (backwards)" if self.backward_execution else "")) self.setup_run() # data to be accessed by the decider state child_errors = {} final_outcomes_dict = {} decider_state = self.states[UNIQUE_DECIDER_STATE_ID] try: concurrency_history_item = self.setup_forward_or_backward_execution() self.start_child_states(concurrency_history_item, decider_state) ####################################################### # wait for all child threads to finish ####################################################### for history_index, state in enumerate(self.states.values()): # skip the decider state if state is not decider_state: self.join_state(state, history_index, concurrency_history_item) self.add_state_execution_output_to_scoped_data(state.output_data, state) self.update_scoped_variables_with_output_dictionary(state.output_data, state) # save the errors of the child state executions for the decider state if 'error' in state.output_data: child_errors[state.state_id] = (state.name, state.output_data['error']) final_outcomes_dict[state.state_id] = (state.name, state.final_outcome) ####################################################### # handle backward execution case ####################################################### if self.backward_execution: return self.finalize_backward_execution() else: self.backward_execution = False ####################################################### # execute decider state ####################################################### decider_state_error = self.run_decider_state(decider_state, child_errors, final_outcomes_dict) ####################################################### # handle no transition ####################################################### transition = self.get_transition_for_outcome(decider_state, decider_state.final_outcome) if transition is None: # final outcome is set here transition = self.handle_no_transition(decider_state) # if the transition is still None, then the child_state was preempted or aborted, in this case return decider_state.state_execution_status = StateExecutionStatus.INACTIVE if transition is None: self.output_data["error"] = RuntimeError("state aborted") else: if decider_state_error: self.output_data["error"] = decider_state_error self.final_outcome = self.outcomes[transition.to_outcome] return self.finalize_concurrency_state(self.final_outcome) except Exception as e: logger.exception("{0} had an internal error:".format(self)) self.output_data["error"] = e self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE return self.finalize(Outcome(-1, "aborted"))
def run(self): """ This defines the sequence of actions that are taken when the preemptive concurrency state is executed :return: """ logger.debug("Starting execution of {0}{1}".format( self, " (backwards)" if self.backward_execution else "")) self.setup_run() try: concurrency_history_item = self.setup_forward_or_backward_execution( ) concurrency_queue = self.start_child_states( concurrency_history_item) ####################################################### # wait for the first threads to finish ####################################################### finished_thread_id = concurrency_queue.get() finisher_state = self.states[finished_thread_id] finisher_state.join() # preempt all child states if not self.backward_execution: for state_id, state in self.states.items(): state.recursively_preempt_states() # join all states for history_index, state in enumerate(self.states.values()): self.join_state(state, history_index, concurrency_history_item) self.add_state_execution_output_to_scoped_data( state.output_data, state) self.update_scoped_variables_with_output_dictionary( state.output_data, state) # add the data of the first state now to overwrite data of the preempted states self.add_state_execution_output_to_scoped_data( finisher_state.output_data, finisher_state) self.update_scoped_variables_with_output_dictionary( finisher_state.output_data, finisher_state) ####################################################### # handle backward execution case ####################################################### if self.states[finished_thread_id].backward_execution: return self.finalize_backward_execution() else: self.backward_execution = False ####################################################### # handle no transition ####################################################### transition = self.get_transition_for_outcome( self.states[finished_thread_id], self.states[finished_thread_id].final_outcome) if transition is None: # final outcome is set here transition = self.handle_no_transition( self.states[finished_thread_id]) # it the transition is still None, then the state was preempted or aborted, in this case return if transition is None: self.output_data["error"] = RuntimeError("state aborted") else: if 'error' in self.states[finished_thread_id].output_data: self.output_data["error"] = self.states[ finished_thread_id].output_data['error'] self.final_outcome = self.outcomes[transition.to_outcome] return self.finalize_concurrency_state(self.final_outcome) except Exception as e: logger.exception("{0} had an internal error:".format(self)) self.output_data["error"] = e self.state_execution_status = StateExecutionStatus.WAIT_FOR_NEXT_STATE return self.finalize(Outcome(-1, "aborted"))
def load_state_recursively(parent, state_path=None, dirty_states=[]): """Recursively loads the state It calls this method on each sub-state of a container state. :param parent: the root state of the last load call to which the loaded state will be added :param state_path: the path on the filesystem where to find the meta file for the state :param dirty_states: a dict of states which changed during loading :return: """ from rafcon.core.states.execution_state import ExecutionState from rafcon.core.states.container_state import ContainerState from rafcon.core.states.hierarchy_state import HierarchyState from rafcon.core.singleton import library_manager path_core_data = get_core_data_path(state_path) path_meta_data = get_meta_data_path(state_path) logger.debug("Load state recursively: {0}".format(str(state_path))) try: state_info = load_data_file(path_core_data) except ValueError as e: logger.exception("Error while loading state data: {0}".format(e)) return except LibraryNotFoundException as e: if global_config.get_config_value( "RAISE_ERROR_ON_MISSING_LIBRARY_STATES", False) or not library_manager.show_dialog: raise logger.error( "Library could not be loaded: {0}\n" "Skipping library and continuing loading the state machine".format( e)) state_info = storage_utils.load_objects_from_json(path_core_data, as_dict=True) missing_library_meta_data = None if os.path.exists(path_meta_data): missing_library_meta_data = Vividict( storage_utils.load_objects_from_json(path_meta_data)) state_id = state_info["state_id"] outcomes = { outcome['outcome_id']: Outcome(outcome['outcome_id'], outcome['name']) for outcome in state_info["outcomes"].values() } dummy_state = HierarchyState( LIBRARY_NOT_FOUND_DUMMY_STATE_NAME, state_id=state_id, outcomes=outcomes, is_dummy=True, missing_library_meta_data=missing_library_meta_data) library_name = state_info['library_name'] path_parts = os.path.join(state_info['library_path'], library_name).split(os.sep) dummy_state.description = 'The Missing Library Path: %s\nThe Missing Library Name: %s\n\n' % ( state_info['library_path'], library_name) from rafcon.core.singleton import library_manager if path_parts[0] in library_manager.library_root_paths: dummy_state.description += 'The Missing Library OS Path: %s' % os.path.join( library_manager.library_root_paths[path_parts[0]], * path_parts[1:]) else: dummy_state.description += 'The missing library was located in the missing library root "%s"' % path_parts[ 0] # set parent of dummy state if isinstance(parent, ContainerState): parent.add_state(dummy_state, storage_load=True) else: dummy_state.parent = parent return dummy_state except LibraryNotFoundSkipException: return None # Transitions and data flows are not added when loading a state, as also states are not added. # We have to wait until the child states are loaded, before adding transitions and data flows, as otherwise the # validity checks for transitions and data flows would fail if not isinstance(state_info, tuple): state = state_info else: state = state_info[0] transitions = state_info[1] data_flows = state_info[2] # set parent of state if parent is not None and isinstance(parent, ContainerState): parent.add_state(state, storage_load=True) else: state.parent = parent # read script file if state is an ExecutionState if isinstance(state, ExecutionState): script_text = read_file(state_path, state.script.filename) state.script.set_script_without_compilation(script_text) # load semantic data try: semantic_data = load_data_file( os.path.join(state_path, SEMANTIC_DATA_FILE)) state.semantic_data = semantic_data except Exception as e: # semantic data file does not have to be there pass # load child states for p in os.listdir(state_path): child_state_path = os.path.join(state_path, p) if os.path.isdir(child_state_path): if not os.path.exists( os.path.join(child_state_path, FILE_NAME_CORE_DATA)): # this means that child_state_path is a folder, not containing a valid state # this also happens when pip creates __pycache__ folders for the script.py files upon installing rafcon continue child_state = load_state_recursively(state, child_state_path, dirty_states) if not child_state: return None # Now we can add transitions and data flows, as all child states were added if isinstance(state_info, tuple): safe_init = global_config.get_config_value("LOAD_SM_WITH_CHECKS", True) if safe_init: # this will trigger all validity checks the state machine state.transitions = transitions else: state._transitions = transitions state._data_flows = data_flows for _, transition in state.transitions.items(): transition._parent = ref(state) state._data_flows = data_flows for _, data_flow in state.data_flows.items(): data_flow._parent = ref(state) state.file_system_path = state_path if state.marked_dirty: dirty_states.append(state) return state