Class: Roby::PlanningLoop
- Inherits:
-
Task
- Object
- BasicObject
- PlanObject
- Task
- Roby::PlanningLoop
- Defined in:
- lib/roby/planning/loops.rb
Overview
This class unrolls a loop in the plan. It maintains lookahead patterns developped at all times by calling an external planner, and manages them. This documentation will start by describing the general behaviour of this task, and then we will detail different specific modes of operation.
Behaviour description
The task unrolls the loop by generating /patterns/, which are a combination of a task representing the operation to be done during one pass of the loop, and a planning task which will generate the subplan for this operation. These patterns are developped as children of either the PlanningLoop task itself, or its planned_task if there is one.
During the execution of this suite of patterns, the following constraints are always met:
-
the planning task of a pattern is started after the one of the previous pattern has finished.
-
a pattern is started after the previous one has finished.
The #start! command do not starts the loop per-se. It only makes the first lookahead patterns to be developped. You have to call #loop_start! once to start the generated patterns themselves.
Periodic and nonperiodic loops
On the one hand, if the :period option of #initialize is non-nil, it is expected to be a floating-point value representing a time in seconds. In that case, the loop is periodic and each pattern in the loop is started at the given periodic rate, triggered by the #periodic_trigger event. Note that the ‘zero-period’ case is a special situation where the loop runs as fast as possible.
On the other hand, if :period is nil, the loop is nonperiodic, and each pattern must be explicitely started by calling #loop_start!. Finally, #loop_start! can also be called to bypass the period value (i.e. to start a pattern earlier than expected). Repetitive calls to #loop_start! will make the loop develop and start at most one pattern.
Zero lookahead
When the loop lookahead is nonzero, patterns are planend ahead-of-time: they are planned as soon as possible. In some cases, it is non desirable, for instance because some information is available only at a later time.
For these situations, one can use a zero lookahead. In that case, the patterns are not pre-planned, but instead the planning task is started only when the pattern itself should have been started: either when the period timeouts, or when #loop_start! is explicitely called.
TODO: make figures.
Constant Summary
Constants included from Log::TaskHooks
Constants included from Log::BasicObjectHooks
Instance Attribute Summary collapse
-
#patterns ⇒ Object
readonly
An array of [planning_task, user_command].
-
#periodic_trigger ⇒ Object
readonly
If this loop is periodic of nonzero period, the state event which represents that period.
Attributes inherited from PlanObject
#executable, #plan, #removed_at
Attributes inherited from BasicObject
Class Method Summary collapse
-
.filter_options(options) ⇒ Object
Filters the options in
options, splitting between the options that are specific to the planning task and those that are to be forwarded to the planner itself.
Instance Method Summary collapse
-
#append_pattern(*context) ⇒ Object
Appends a new unplanned pattern after all the patterns already developped.
-
#initialize(options) ⇒ PlanningLoop
constructor
A new instance of PlanningLoop.
-
#last_planning_task ⇒ Object
The PlanningTask object for the last pattern.
-
#main_task ⇒ Object
The task on which the children are added.
-
#planned_task ⇒ Object
:nodoc:.
Methods inherited from Task
#_dump, _load, #droby_dump, improves, #improves?, improves?, match, needs, #needs?, needs?
Methods included from TaskStructure::ModelConflicts
#conflicts_with, #conflicts_with?
Methods included from Distributed::DRobyTaskModel::Dump
Methods included from Distributed::DRobyModel::Dump
Methods included from Distributed::TaskNotifications
Methods included from TaskOperations
Methods included from Log::TaskHooks
#added_child_object, #removed_child_object
Methods inherited from PlanObject
#add_child_object, #apply_relation_changes, child_plan_object, #each_plan_child, #executable?, #finalized?, #forget_peer, #read_write?, #remotely_useful?, #removing_child_object, #replace_by, #replace_subplan_by, #root_object, #root_object?, #subscribed?, #update_on?, #updated_by?
Methods included from Distributed::RelationModificationHooks
#added_child_object, #removed_child_object
Methods included from Transactions::PlanObjectUpdates
#adding_child_object, #removing_child_object
Methods included from DirectedRelationSupport
#add_child_object, #add_parent_object, #check_is_relation, #related_objects, #relations, #remove_child_object, #remove_children, #remove_parent_object, #remove_parents, #remove_relations
Methods inherited from BasicObject
#add_sibling_for, #distribute?, distribute?, #finalized?, #forget_peer, #has_sibling_on?, #initialize_copy, local_only, #read_write?, #remotely_useful?, #remove_sibling_for, #self_owned?, #sibling_of, #sibling_on, #subscribe, #subscribed?, #update_on?, #updated?, #updated_by?, #updated_peers
Methods included from Log::BasicObjectHooks
Constructor Details
#initialize(options) ⇒ PlanningLoop
Returns a new instance of PlanningLoop.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/roby/planning/loops.rb', line 104 def initialize() task_arguments, = PlanningLoop.() task_arguments[:method_options].merge!() super(task_arguments) if period && period > 0 @periodic_trigger = State.on_delta :t => period periodic_trigger.disable periodic_trigger.on event(:loop_start) end @patterns = [] @pattern_id = 0 end |
Instance Attribute Details
#patterns ⇒ Object (readonly)
An array of [planning_task, user_command]. The last element is the first arrived
58 59 60 |
# File 'lib/roby/planning/loops.rb', line 58 def patterns @patterns end |
#periodic_trigger ⇒ Object (readonly)
If this loop is periodic of nonzero period, the state event which represents that period.
102 103 104 |
# File 'lib/roby/planning/loops.rb', line 102 def periodic_trigger @periodic_trigger end |
Class Method Details
.filter_options(options) ⇒ Object
Filters the options in options, splitting between the options that are specific to the planning task and those that are to be forwarded to the planner itself
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/roby/planning/loops.rb', line 79 def self.() # :nodoc: task_arguments, = Kernel. , :period => nil, :lookahead => 1, :planner_model => nil, :planned_model => Roby::Task, :method_name => nil, :method_options => {}, :planning_owners => nil if !task_arguments[:method_name] raise ArgumentError, "required argument :method_name missing" elsif !task_arguments[:planner_model] raise ArgumentError, "required argument :planner_model missing" elsif task_arguments[:lookahead] < 0 raise ArgumentError, "lookahead must be positive" end task_arguments[:period] ||= nil [task_arguments, ] end |
Instance Method Details
#append_pattern(*context) ⇒ Object
Appends a new unplanned pattern after all the patterns already developped
context is forwarded to the planned task
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/roby/planning/loops.rb', line 136 def append_pattern(*context) # Create the new pattern task_arguments = arguments.slice(:planner_model, :planned_model, :method_name) task_arguments[:method_options] = .dup task_arguments[:method_options][:pattern_id] = @pattern_id @pattern_id += 1 planning = PlanningTask.new(task_arguments) planned = planning.planned_task planned.forward(:start, self, :loop_start) planned.forward(:success, self, :loop_success) planned.forward(:stop, self, :loop_end) main_task.realized_by planned # Schedule it. We start the new pattern when these three conditions are met: # * it has been planned (planning has finished) # * the previous one (if any) has finished # * the period (if any) has expired or an external event required # the explicit start of the pattern (call done to user_command, # for instance through a call to #loop_start!) # # The +precondition+ event represents a situation where the new pattern # *can* be started, while +command+ is the situation asking for the # pattern to start. precondition = planning.event(:success) user_command = EventGenerator.new(true) command = user_command if last_planning = last_planning_task last_planned = last_planning.planned_task if !last_planned.finished? precondition &= last_planned.event(:stop) end if period && !periodic_trigger command |= planned.event(:success) end if last_planning.finished? planning.start!(*context) else last_planning.event(:success).filter(*context).on(planning.event(:start)) end end command &= precondition patterns.unshift([planning, user_command]) command.on(planned.event(:start)) planning end |
#last_planning_task ⇒ Object
The PlanningTask object for the last pattern
127 128 129 130 131 |
# File 'lib/roby/planning/loops.rb', line 127 def last_planning_task if pattern = patterns.first pattern.first end end |
#main_task ⇒ Object
The task on which the children are added
120 |
# File 'lib/roby/planning/loops.rb', line 120 def main_task; planned_task || self end |
#planned_task ⇒ Object
:nodoc:
122 123 124 |
# File 'lib/roby/planning/loops.rb', line 122 def planned_task # :nodoc: planned_tasks.find { true } end |