Module: Roby::Coordination::Models::ActionStateMachine
- Includes:
- Actions
- Included in:
- ActionStateMachine
- Defined in:
- lib/roby/coordination/models/action_state_machine.rb
Overview
Definition of model-level functionality for action state machines
In an action state machine, each state is represented by a single Roby task. At the model level, they get represented by a Task object, and more specifically very often by a TaskFromAction object. One important bit to understand is that a given Task object represents always the same state (i.e. task0 == task1 if task0 and task1 represent the same state). It is for instance possible that task0 != task1 even if task0 and task1 are issued by the same action.
Transitions are stored as (Task,Event,Task) triplets, specifying the origin state, the triggering event and the target state. Event#task is often the same than the origin state, but not always (see below)
Note that because some states are subclasses of TaskWithDependencies, it is possible that some Task objects are not states, only tasks that are dependencies of other states created with TaskWithDependencies#depends_on or dependencies of the state machine itself created with Roby::Coordination::Models::Actions#depends_on. The events of these task objects can be used in transitions
Action state machine models are usually created through an action interface with Interface#action_state_machine. The state machine model can then be retrieved using Actions::Models::Action#coordination_model.
Instance Attribute Summary collapse
-
#starting_state ⇒ Task
readonly
The starting state.
Attributes included from Actions
Attributes included from Base
Instance Method Summary collapse
-
#capture(state, event = nil, &block) ⇒ Object
Capture the value of an event context.
-
#compute_unreachable_states ⇒ Array<Task>
Computes the set of states that are used in the transitions but are actually not reachable.
-
#find_state_by_name(name) ⇒ Object
Returns the state for the given name, if found, nil otherwise.
- #map_tasks(mapping) ⇒ Object
-
#parse(&block) ⇒ Object
Overloaded from Actions to validate the state machine definition.
- #parse_names ⇒ Object
-
#start(state) ⇒ Object
Declares the starting state.
- #state(object, task_model = Roby::Task) ⇒ Object
- #to_s ⇒ Object
-
#toplevel_state?(state) ⇒ Boolean
private
Raise if the given state is not a toplevel state.
-
#transition(*spec) ⇒ Object
Declares a transition from a state to a new state, caused by an event.
- #validate_task(object) ⇒ Object
Methods included from Actions
#dependency, #depends_on, #event_active_in_state?, #forward, #from, #from_state, #method_missing, #rebind, #required_tasks_for, #respond_to_missing?, #root_event?, #setup_submodel
Methods included from Base
#find_event, #find_task_by_name, #method_missing, #respond_to_missing?, #setup_submodel, #task, #task_model, #use_fault_response_table, #used_fault_response_table, #validate_event, #validate_or_create_task
Methods included from Arguments
#argument, #validate_arguments
Dynamic Method Handling
This class handles dynamic methods through the method_missing method in the class Roby::Coordination::Models::Actions
Instance Attribute Details
#starting_state ⇒ Task (readonly)
The starting state
59 60 61 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 59 def starting_state @starting_state end |
Instance Method Details
#capture(state, event = nil, &block) ⇒ Object
Capture the value of an event context
The value returned by #capture is meant to be used as arguments to other states. This is a mechanism to pass information from one state to the next.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 111 def capture(state, event = nil, &block) if !event state, event = state.task, state end if !toplevel_state?(state) raise ArgumentError, "#{state} is not a toplevel state, a capture's state must be toplevel" elsif !event_active_in_state?(event, state) raise ArgumentError, "#{event} is not an event that is active in state #{state}" end filter = if block lambda(&block) else lambda { |event| event.context.first } end capture = Capture.new(filter) captures[capture] = [state, event] capture end |
#compute_unreachable_states ⇒ Array<Task>
Computes the set of states that are used in the transitions but are actually not reachable
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 150 def compute_unreachable_states queue = [starting_state].to_set transitions = self.each_transition.to_a.dup done_something = true while done_something done_something = false transitions.delete_if do |from, _, to| if queue.include?(from) queue << to done_something = true end end end transitions.map(&:first).to_set end |
#find_state_by_name(name) ⇒ Object
Returns the state for the given name, if found, nil otherwise
137 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 137 def find_state_by_name(name) find_task_by_name("#{name}_state") end |
#map_tasks(mapping) ⇒ Object
74 75 76 77 78 79 80 81 82 83 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 74 def map_tasks(mapping) super @starting_state = mapping[starting_state] @transitions = transitions.map do |state, event, new_state| [mapping[state], mapping[event.task].find_event(event.symbol), mapping[new_state]] end end |
#parse(&block) ⇒ Object
Overloaded from Actions to validate the state machine definition
172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 172 def parse(&block) super if !starting_state raise ArgumentError, "no starting state defined" end # Validate that all source states in transitions are reachable # from the start state unreachable = compute_unreachable_states if !unreachable.empty? raise UnreachableStateUsed.new(unreachable), "#{unreachable.size} states are used in transitions but are actually not reachable" end end |
#parse_names ⇒ Object
85 86 87 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 85 def parse_names super(Task => '_state', Capture => "") end |
#start(state) ⇒ Object
Declares the starting state
90 91 92 93 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 90 def start(state) parse_names @starting_state = validate_task(state) end |
#state(object, task_model = Roby::Task) ⇒ Object
95 96 97 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 95 def state(object, task_model = Roby::Task) task(object, task_model) end |
#to_s ⇒ Object
216 217 218 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 216 def to_s "#{action_interface}.#{name}" end |
#toplevel_state?(state) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Raise if the given state is not a toplevel state
67 68 69 70 71 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 67 def toplevel_state?(state) root == state || starting_state == state || transitions.any? { |from_state, _, to_state| from_state == state || to_state == state } end |
#transition(state.my_event, new_state) ⇒ Object #transition(state, event, new_state) ⇒ Object
Declares a transition from a state to a new state, caused by an event
64 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 64 inherited_attribute(:transition, :transitions) { Array.new } |
#validate_task(object) ⇒ Object
139 140 141 142 143 144 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 139 def validate_task(object) if !object.kind_of?(Coordination::Models::Task) raise ArgumentError, "expected a state object, got #{object}. States need to be created from e.g. actions by calling #state before they can be used in the state machine" end object end |