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, as: nil) ⇒ 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
60 61 62 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 60 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.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 118 def capture(state, event = nil, &block) unless 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 ->(ev) { ev.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
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 163 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
144 145 146 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 144 def find_state_by_name(name) find_task_by_name("#{name}_state") end |
#map_tasks(mapping) ⇒ Object
75 76 77 78 79 80 81 82 83 84 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 75 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
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 185 def parse(&block) super unless 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 unless unreachable.empty? raise UnreachableStateUsed.new(unreachable), "#{unreachable.size} states are used in transitions "\ "but are actually not reachable" end end |
#parse_names ⇒ Object
86 87 88 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 86 def parse_names super(Task => "_state", Capture => "") end |
#start(state) ⇒ Object
Declares the starting state
91 92 93 94 95 96 97 98 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 91 def start(state) parse_names if @starting_state raise ArgumentError, "this state machine already has a starting "\ "state, use #depends_on to run more than one task at startup" end @starting_state = validate_task(state) end |
#state(object, task_model = Roby::Task, as: nil) ⇒ Object
100 101 102 103 104 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 100 def state(object, task_model = Roby::Task, as: nil) state = task(object, task_model) state.name = as state end |
#to_s ⇒ Object
235 236 237 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 235 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
68 69 70 71 72 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 68 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
65 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 65 inherited_attribute(:transition, :transitions) { [] } |
#validate_task(object) ⇒ Object
148 149 150 151 152 153 154 155 156 157 |
# File 'lib/roby/coordination/models/action_state_machine.rb', line 148 def validate_task(object) unless 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 |