Class: Roby::Plan
- Inherits:
- BasicObject
- Extended by:
- Logger::Forward, Logger::Hierarchy
- Includes:
- Distributed::EventNotifications::PlanCacheCleanup, Distributed::PlanModificationHooks, EventGenerator::FinalizedEventHook, Log::PlanHooks, Roby::Propagation::RemoveDelayedOnFinalized, TaskStructure::ExecutionAgentSpawn, Transactions::PlanUpdates
- Defined in:
- lib/roby/plan.rb,
lib/roby.rb,
lib/roby/query.rb,
lib/roby/distributed/proxy.rb
Overview
A plan object is a collection of tasks and events. In plans, tasks can be missions (#missions, #mission?), which means that they are the final goals of the system. A task is useful if it helps into the realization of a mission (it is linked to a mission by #hierarchy_relation or one of the #service_relations), and is not useful otherwise. #garbage_collect removes the tasks that are not useful.
The following event hooks are defined:
* #inserted
* #discarded
* #discovered_tasks
* #discovered_events
* #replaced
* #added_transaction
* #removed_transaction
* #garbage
* #finalized_task
* #finalized_event
Direct Known Subclasses
Defined Under Namespace
Classes: DRoby
Constant Summary
Constants included from Log::PlanHooks
Constants included from Log::BasicObjectHooks
Instance Attribute Summary collapse
-
#force_gc ⇒ Object
readonly
A set of tasks which are useful (and as such would not been garbage collected), but we want to GC anyway.
-
#free_events ⇒ Object
readonly
The list of events that are not included in a task.
-
#gc_quarantine ⇒ Object
readonly
A set of task for which GC should not be attempted, either because they are not interruptible or because their start or stop command failed.
-
#keepalive ⇒ Object
readonly
directly, use #permanent and #auto instead.
-
#known_tasks ⇒ Object
readonly
The list of tasks that are included in this plan.
-
#missions ⇒ Object
readonly
The list of the robot’s missions.
-
#repairs ⇒ Object
readonly
See also #add_repair and #remove_repair.
-
#task_events ⇒ Object
readonly
The set of events that are defined by #known_tasks.
-
#task_index ⇒ Object
readonly
The task index for this plan.
-
#transactions ⇒ Object
readonly
The set of transactions which are built on top of this plan.
Attributes inherited from BasicObject
Class Method Summary collapse
Instance Method Summary collapse
-
#[](object) ⇒ Object
Returns
objectif object is a plan object from this plan, or if it has no plan yet (in which case it is added to the plan first). -
#add_repair(failure_point, task) ⇒ Object
Install a plan repair for
failure_pointwithtask. -
#added_transaction(trsc) ⇒ Object
Hook called when a new transaction has been built on top of this plan.
-
#auto(task) ⇒ Object
Make GC finalize
taskif it is not useful anymore. -
#clear ⇒ Object
Remove all tasks.
-
#discard(task) ⇒ Object
Removes the task in
tasksfrom the list of missions. -
#discarded(tasks) ⇒ Object
Hook called when
taskshave been discarded from this plan. -
#discover(objects) ⇒ Object
call-seq: plan.discover([t1, t2, …]) => plan.
-
#discover_event_set(events) ⇒ Object
Add
eventsto the set of known events and call discovered_events for the new events. -
#discover_task_set(tasks) ⇒ Object
Add
tasksto the set of known tasks and call discovered_tasks for the new tasks. -
#discovered(tasks) ⇒ Object
DEPRECATED.
-
#discovered_events(events) ⇒ Object
Hook called when new events have been discovered in this plan.
-
#discovered_tasks(tasks) ⇒ Object
Hook called when new tasks have been discovered in this plan.
-
#droby_dump(dest) ⇒ Object
Returns an intermediate representation of
selfsuitable to be sent to thedestpeer. -
#each_task ⇒ Object
Iterates on all tasks.
- #edit ⇒ Object
-
#empty? ⇒ Boolean
Returns true if there is no task in this plan.
-
#executable? ⇒ Boolean
Check that this is an executable plan.
-
#finalized(task) ⇒ Object
backward compatibility.
-
#finalized_event(event) ⇒ Object
Hook called when
eventhas been removed from this plan. -
#finalized_task(task) ⇒ Object
Hook called when
taskhas been removed from this plan. -
#find_tasks(model = nil, args = nil) ⇒ Object
Returns a Query object on this plan.
-
#garbage(task) ⇒ Object
Hook called when
taskis marked as garbage. -
#garbage_collect(force_on = nil) ⇒ Object
Kills and removes all unneeded tasks.
- #handle_replace(from, to) {|from, to| ... } ⇒ Object
-
#include?(object) ⇒ Boolean
Checks if
taskis included in this plan. -
#initialize ⇒ Plan
constructor
A new instance of Plan.
-
#insert(task) ⇒ Object
Inserts a new mission in the plan.
-
#inserted(tasks) ⇒ Object
Hook called when
taskshave been inserted in this plan. - #inspect ⇒ Object
- #local_tasks ⇒ Object
-
#locally_useful_tasks ⇒ Object
Returns the set of useful tasks in this plan.
-
#mission?(task) ⇒ Boolean
Checks if
taskis a mission of this plan. - #owns?(object) ⇒ Boolean
-
#partition_event_task(objects) ⇒ Object
call-seq: plan.partition_event_task(objects) => events, tasks.
-
#permanent(task) ⇒ Object
Forbid the GC to take out
task. - #permanent?(task) ⇒ Boolean
-
#query_each(result_set, &block) ⇒ Object
Called by TaskMatcher#each and Query#each to return the result of this query on
self. -
#query_result_set(matcher) ⇒ Object
Called by TaskMatcher#result_set and Query#result_set to get the set of tasks matching
matcher. -
#query_roots(result_set, relation) ⇒ Object
Given the result set of
query, returns the subset of tasks which have no parent inquery. -
#real_plan ⇒ Object
If this plan is a toplevel plan, returns self.
- #remote_tasks ⇒ Object
- #remove_object(object) ⇒ Object
-
#remove_repair(task) ⇒ Object
Removes
taskfrom the set of active plan repairs. -
#remove_task(t) ⇒ Object
Backward compatibility.
-
#remove_transaction(trsc) ⇒ Object
Removes the transaction
trscfrom the list of known transactions built on this plan. -
#removed_transaction(trsc) ⇒ Object
Hook called when a new transaction has been built on top of this plan.
-
#repairs_for(event) ⇒ Object
Return all repairs which apply on
event. - #replace(from, to) ⇒ Object
- #replace_task(from, to) ⇒ Object
-
#replaced(from, to) ⇒ Object
Hook called when
tohas replacedfromin this plan. -
#respawn(task) ⇒ Object
Replace
taskwith a fresh copy of itself. -
#sibling_on?(peer) ⇒ Boolean
If this object is the main plan, checks if we are subscribed to the whole remote plan.
-
#size ⇒ Object
Count of tasks in this plan.
-
#unneeded_events ⇒ Object
The set of events that can be removed from the plan.
-
#unneeded_tasks ⇒ Object
Returns the set of unused tasks.
- #useful_event_component(useful_events) ⇒ Object
-
#useful_events ⇒ Object
Computes the set of events that are useful in the plan Events are ‘useful’ when they are chained to a task.
-
#useful_task?(task) ⇒ Boolean
Computes the set of useful tasks and checks that
taskis in it. -
#useful_task_component(complete_set, useful_set, seeds) ⇒ Object
Merges the set of tasks that are useful for
seedsintouseful_set.
Methods included from Distributed::PlanModificationHooks
discovered_objects, finalized_object
Methods included from Transactions::PlanUpdates
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 ⇒ Plan
Returns a new instance of Plan.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/roby/plan.rb', line 73 def initialize @missions = ValueSet.new @keepalive = ValueSet.new @known_tasks = ValueSet.new @free_events = ValueSet.new @task_events = ValueSet.new @force_gc = ValueSet.new @gc_quarantine = ValueSet.new @transactions = ValueSet.new @repairs = Hash.new @task_index = Roby::TaskIndex.new super() if defined? super end |
Instance Attribute Details
#force_gc ⇒ Object (readonly)
A set of tasks which are useful (and as such would not been garbage collected), but we want to GC anyway
55 56 57 |
# File 'lib/roby/plan.rb', line 55 def force_gc @force_gc end |
#free_events ⇒ Object (readonly)
The list of events that are not included in a task
41 42 43 |
# File 'lib/roby/plan.rb', line 41 def free_events @free_events end |
#gc_quarantine ⇒ Object (readonly)
A set of task for which GC should not be attempted, either because they are not interruptible or because their start or stop command failed
60 61 62 |
# File 'lib/roby/plan.rb', line 60 def gc_quarantine @gc_quarantine end |
#keepalive ⇒ Object (readonly)
directly, use #permanent and #auto instead.
44 45 46 |
# File 'lib/roby/plan.rb', line 44 def keepalive @keepalive end |
#known_tasks ⇒ Object (readonly)
The list of tasks that are included in this plan
34 35 36 |
# File 'lib/roby/plan.rb', line 34 def known_tasks @known_tasks end |
#missions ⇒ Object (readonly)
The list of the robot’s missions. Do not change that set directly, use #insert and #discard instead.
39 40 41 |
# File 'lib/roby/plan.rb', line 39 def missions @missions end |
#repairs ⇒ Object (readonly)
See also #add_repair and #remove_repair
51 52 53 |
# File 'lib/roby/plan.rb', line 51 def repairs @repairs end |
#task_events ⇒ Object (readonly)
The set of events that are defined by #known_tasks
36 37 38 |
# File 'lib/roby/plan.rb', line 36 def task_events @task_events end |
#task_index ⇒ Object (readonly)
The task index for this plan
31 32 33 |
# File 'lib/roby/plan.rb', line 31 def task_index @task_index end |
#transactions ⇒ Object (readonly)
The set of transactions which are built on top of this plan
63 64 65 |
# File 'lib/roby/plan.rb', line 63 def transactions @transactions end |
Class Method Details
.can_gc?(task) ⇒ Boolean
556 557 558 559 560 561 562 |
# File 'lib/roby/plan.rb', line 556 def self.can_gc?(task) if task.starting? then true # wait for the task to be started before deciding ... elsif task.running? && !task.finishing? task.event(:stop).controlable? else true end end |
Instance Method Details
#[](object) ⇒ Object
Returns object if object is a plan object from this plan, or if it has no plan yet (in which case it is added to the plan first). Otherwise, raises ArgumentError.
This method is provided for consistency with Transaction#[]
547 548 549 550 551 552 553 554 |
# File 'lib/roby/plan.rb', line 547 def [](object) if object.plan != self raise ArgumentError, "#{object} is not from #{plan}" elsif !object.plan discover(object) end object end |
#add_repair(failure_point, task) ⇒ Object
Install a plan repair for failure_point with task. If task is pending, it is started.
See also #repairs and #remove_repair
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
# File 'lib/roby/plan.rb', line 478 def add_repair(failure_point, task) if !failure_point.kind_of?(Event) raise TypeError, "failure point #{failure_point} should be an event" elsif task.plan && task.plan != self raise ArgumentError, "wrong plan: #{task} is in #{task.plan}, not #{self}" elsif repairs.has_key?(failure_point) raise ArgumentError, "there is already a plan repair defined for #{failure_point}: #{repairs[failure_point]}" elsif !task.plan discover(task) end repairs[failure_point] = task if failure_point.generator.respond_to?(:task) task_index.repaired_tasks << failure_point.generator.task end if task.pending? Roby.once { task.start! } end end |
#added_transaction(trsc) ⇒ Object
Hook called when a new transaction has been built on top of this plan
322 |
# File 'lib/roby/plan.rb', line 322 def added_transaction(trsc); super if defined? super end |
#auto(task) ⇒ Object
Make GC finalize task if it is not useful anymore
143 |
# File 'lib/roby/plan.rb', line 143 def auto(task); @keepalive.delete(task) end |
#clear ⇒ Object
Remove all tasks
172 173 174 175 176 177 178 179 |
# File 'lib/roby/plan.rb', line 172 def clear @known_tasks.each { |t| t.clear_relations } @known_tasks.clear @free_events.each { |e| e.clear_relations } @free_events.clear @missions.clear @keepalive.clear end |
#discard(task) ⇒ Object
Removes the task in tasks from the list of missions
156 157 158 159 160 161 162 163 |
# File 'lib/roby/plan.rb', line 156 def discard(task) @missions.delete(task) discover(task) task.mission = false if task.self_owned? discarded(task) self end |
#discarded(tasks) ⇒ Object
Hook called when tasks have been discarded from this plan
165 |
# File 'lib/roby/plan.rb', line 165 def discarded(tasks); super if defined? super end |
#discover(objects) ⇒ Object
call-seq:
plan.discover([t1, t2, ...]) => plan
Updates Plan#known_tasks with either the child tree of the tasks in objects
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/roby/plan.rb', line 233 def discover(objects) event_seeds, tasks = partition_event_task(objects) event_seeds = (event_seeds || ValueSet.new).to_value_set if tasks tasks = tasks.to_value_set new_tasks = useful_task_component(nil, tasks, tasks) unless new_tasks.empty? old_task_events = task_events.dup new_tasks = discover_task_set(new_tasks) event_seeds.merge(task_events - old_task_events) end end if !event_seeds.empty? events = event_seeds.dup # now, we include the set of free events that are linked to # +new_tasks+ in +events+ EventStructure.each_root_relation do |rel| components = rel.generated_subgraphs(event_seeds, false) components.concat rel.reverse.generated_subgraphs(event_seeds, false) for c in components events.merge(c.to_value_set) end end discover_event_set(events - task_events - free_events) end self end |
#discover_event_set(events) ⇒ Object
Add events to the set of known events and call discovered_events for the new events
This is for internal use, use #discover instead
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/roby/plan.rb', line 270 def discover_event_set(events) events = events.difference(free_events) events.delete_if do |e| if !e.root_object? true else e.plan = self false end end unless events.empty? free_events.merge(events) discovered_events(events) end events end |
#discover_task_set(tasks) ⇒ Object
Add tasks to the set of known tasks and call discovered_tasks for the new tasks
This is for internal use, use #discover instead
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/roby/plan.rb', line 293 def discover_task_set(tasks) tasks = tasks.difference(known_tasks) for t in tasks t.plan = self task_events.merge t.bound_events.values.to_value_set task_index.add t end known_tasks.merge tasks discovered_tasks(tasks) for t in tasks t.instantiate_model_event_relations end tasks end |
#discovered(tasks) ⇒ Object
DEPRECATED. Use #discovered_tasks instead
310 |
# File 'lib/roby/plan.rb', line 310 def discovered(tasks); super if defined? super end |
#discovered_events(events) ⇒ Object
Hook called when new events have been discovered in this plan
317 318 319 |
# File 'lib/roby/plan.rb', line 317 def discovered_events(events) super if defined? super end |
#discovered_tasks(tasks) ⇒ Object
Hook called when new tasks have been discovered in this plan
312 313 314 315 |
# File 'lib/roby/plan.rb', line 312 def discovered_tasks(tasks) discovered(tasks) super if defined? super end |
#droby_dump(dest) ⇒ Object
Returns an intermediate representation of self suitable to be sent to the dest peer.
295 296 297 |
# File 'lib/roby/distributed/proxy.rb', line 295 def droby_dump(dest) @__droby_marshalled__ ||= DRoby.new(Roby::Distributed.droby_dump(dest), remote_id) end |
#each_task ⇒ Object
Iterates on all tasks
472 |
# File 'lib/roby/plan.rb', line 472 def each_task; @known_tasks.each { |t| yield(t) } end |
#edit ⇒ Object
145 146 147 148 149 150 151 |
# File 'lib/roby/plan.rb', line 145 def edit if block_given? Roby::Control.synchronize do yield end end end |
#empty? ⇒ Boolean
Returns true if there is no task in this plan
470 |
# File 'lib/roby/plan.rb', line 470 def empty?; @known_tasks.empty? end |
#executable? ⇒ Boolean
Check that this is an executable plan. This is always true for plain Plan objects and false for transcations
226 |
# File 'lib/roby/plan.rb', line 226 def executable?; true end |
#finalized(task) ⇒ Object
backward compatibility
741 742 743 |
# File 'lib/roby/plan.rb', line 741 def finalized(task) # :nodoc: super if defined? super end |
#finalized_event(event) ⇒ Object
Hook called when event has been removed from this plan
750 |
# File 'lib/roby/plan.rb', line 750 def finalized_event(event); super if defined? super end |
#finalized_task(task) ⇒ Object
Hook called when task has been removed from this plan
745 746 747 748 |
# File 'lib/roby/plan.rb', line 745 def finalized_task(task) super if defined? super finalized(task) end |
#find_tasks(model = nil, args = nil) ⇒ Object
Returns a Query object on this plan
467 468 469 470 471 472 473 |
# File 'lib/roby/query.rb', line 467 def find_tasks(model = nil, args = nil) q = Query.new(self) if model || args q.which_fullfills(model, args) end q end |
#garbage(task) ⇒ Object
Hook called when task is marked as garbage. It will be garbage collected as soon as possible
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 |
# File 'lib/roby/plan.rb', line 724 def garbage(task) # Remove all signals that go *to* the task # # While we want events which come from the task to be properly # forwarded, the signals that go to the task are to be ignored if task.self_owned? task.each_event do |ev| ev.parent_objects(EventStructure::Signal).each do |signalling_event| signalling_event.remove_signal ev end end end super if defined? super end |
#garbage_collect(force_on = nil) ⇒ Object
Kills and removes all unneeded tasks
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 |
# File 'lib/roby/plan.rb', line 565 def garbage_collect(force_on = nil) if force_on && !force_on.empty? force_gc.merge(force_on.to_value_set) end # The set of tasks for which we queued stop! at this cycle # #finishing? is false until the next event propagation cycle finishing = ValueSet.new did_something = true while did_something did_something = false tasks = unneeded_tasks | force_gc local_tasks = self.local_tasks & tasks remote_tasks = tasks - local_tasks # Remote tasks are simply removed, regardless of other concerns for t in remote_tasks Plan.debug { "GC: removing the remote task #{t}" } remove_object(t) end break if local_tasks.empty? if local_tasks.all? { |t| t.pending? || t.finished? } local_tasks.each do |t| Plan.debug { "GC: #{t} is not running, removed" } garbage(t) remove_object(t) end break end # Mark all root local_tasks as garbage roots = nil 2.times do |i| roots = local_tasks.find_all do |t| if t.root? garbage(t) true else Plan.debug { "GC: ignoring #{t}, it is not root" } false end end break if i == 1 || !roots.empty? # There is a cycle somewhere. Try to break it by removing # weak relations within elements of local_tasks Plan.debug "cycle found, removing weak relations" local_tasks.each do |t| next if t.root? t.each_graph do |rel| rel.remove(t) if rel.weak? end end end (roots.to_value_set - finishing - gc_quarantine).each do |local_task| if local_task.pending? Plan.info "GC: removing pending task #{local_task}" remove_object(local_task) did_something = true elsif local_task.starting? # wait for task to be started before killing it Plan.debug { "GC: #{local_task} is starting" } elsif local_task.finished? Plan.debug { "GC: #{local_task} is not running, removed" } remove_object(local_task) did_something = true elsif !local_task.finishing? if local_task.event(:stop).controlable? Plan.debug { "GC: queueing #{local_task}/stop" } if !local_task.respond_to?(:stop!) Plan.fatal "something fishy: #{local_task}/stop is controlable but there is no #stop! method" gc_quarantine << local_task else finishing << local_task Roby::Control.once do Plan.debug { "GC: stopping #{local_task}" } local_task.stop!(nil) end end else Plan.warn "GC: ignored #{local_task}, it cannot be stopped" gc_quarantine << local_task end elsif local_task.finishing? Plan.debug { "GC: waiting for #{local_task} to finish" } else Plan.warn "GC: ignored #{local_task}" end end end unneeded_events.each do |event| remove_object(event) end end |
#handle_replace(from, to) {|from, to| ... } ⇒ Object
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/roby/plan.rb', line 181 def handle_replace(from, to) return if from == to # Check that +to+ is valid in all hierarchy relations where +from+ is a child if !to.fullfills?(*from.fullfilled_model) raise InvalidReplace.new(from, to, "to does not fullfills #{from.fullfilled_model}") end # Check that +to+ is in the same execution state than +from+ if !to.compatible_state?(from) raise InvalidReplace.new(from, to, "state. #{from.running?}, #{to.running?}") end # Swap the subplans of +from+ and +to+ yield(from, to) replaced(from, to) if mission?(from) discard(from) insert(to) elsif permanent?(from) auto(from) permanent(to) else discover(to) end end |
#include?(object) ⇒ Boolean
Checks if task is included in this plan
464 |
# File 'lib/roby/plan.rb', line 464 def include?(object); @known_tasks.include?(object) || @free_events.include?(object) end |
#insert(task) ⇒ Object
Inserts a new mission in the plan. Its child tree is automatically inserted too. Returns the plan
124 125 126 127 128 129 130 131 132 |
# File 'lib/roby/plan.rb', line 124 def insert(task) return if @missions.include?(task) @missions << task discover(task) task.mission = true if task.self_owned? inserted(task) self end |
#inserted(tasks) ⇒ Object
Hook called when tasks have been inserted in this plan
134 |
# File 'lib/roby/plan.rb', line 134 def inserted(tasks); super if defined? super end |
#inspect ⇒ Object
89 90 91 |
# File 'lib/roby/plan.rb', line 89 def inspect "#<#{to_s}: missions=#{missions.to_s} tasks=#{known_tasks.to_s} events=#{free_events.to_s} transactions=#{transactions.to_s}>" end |
#local_tasks ⇒ Object
381 382 383 |
# File 'lib/roby/plan.rb', line 381 def local_tasks task_index.by_owner[Roby::Distributed] || ValueSet.new end |
#locally_useful_tasks ⇒ Object
Returns the set of useful tasks in this plan
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/roby/plan.rb', line 355 def locally_useful_tasks # Remove all missions that are finished for finished_mission in (@missions & task_index.by_state[:finished?]) if !task_index.repaired_tasks.include?(finished_mission) discard(finished_mission) end end for finished_permanent in (@keepalive & task_index.by_state[:finished?]) if !task_index.repaired_tasks.include?(finished_permanent) auto(finished_permanent) end end # Create the set of tasks which must be kept as-is seeds = @missions | @keepalive for trsc in transactions seeds.merge trsc.proxy_objects.keys.to_value_set end return ValueSet.new if seeds.empty? # Compute the set of LOCAL tasks which serve the seeds. The set of # locally_useful_tasks is the union of the seeds and of this one useful_task_component(local_tasks, seeds & local_tasks, seeds) | seeds end |
#mission?(task) ⇒ Boolean
Checks if task is a mission of this plan
466 |
# File 'lib/roby/plan.rb', line 466 def mission?(task); @missions.include?(task) end |
#owns?(object) ⇒ Boolean
167 168 169 |
# File 'lib/roby/plan.rb', line 167 def owns?(object) (object.owners - owners).empty? end |
#partition_event_task(objects) ⇒ Object
call-seq:
plan.partition_event_task(objects) => events, tasks
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/roby/plan.rb', line 96 def partition_event_task(objects) if objects.respond_to?(:to_task) then return nil, [objects.to_task] elsif objects.respond_to?(:to_event) then return [objects.to_event], nil elsif !objects.respond_to?(:each) raise TypeError, "expecting a task, event, or a collection of tasks and events, got #{objects}" end evts, tasks = objects.partition do |o| if o.respond_to?(:to_event) then true elsif o.respond_to?(:to_task) then false else raise ArgumentError, "found #{o || 'nil'} which is neither a task nor an event" end end return evts, tasks end |
#permanent(task) ⇒ Object
Forbid the GC to take out task
137 138 139 140 |
# File 'lib/roby/plan.rb', line 137 def permanent(task) @keepalive << task discover(task) end |
#permanent?(task) ⇒ Boolean
153 |
# File 'lib/roby/plan.rb', line 153 def permanent?(task); @keepalive.include?(task) end |
#query_each(result_set, &block) ⇒ Object
Called by TaskMatcher#each and Query#each to return the result of this query on self
487 488 489 490 491 |
# File 'lib/roby/query.rb', line 487 def query_each(result_set, &block) for task in result_set yield(task) end end |
#query_result_set(matcher) ⇒ Object
Called by TaskMatcher#result_set and Query#result_set to get the set of tasks matching matcher
477 478 479 480 481 482 483 |
# File 'lib/roby/query.rb', line 477 def query_result_set(matcher) result = ValueSet.new for task in matcher.filter(known_tasks, task_index) result << task if matcher === task end result end |
#query_roots(result_set, relation) ⇒ Object
Given the result set of query, returns the subset of tasks which have no parent in query
495 496 497 498 499 500 501 502 503 504 505 506 |
# File 'lib/roby/query.rb', line 495 def query_roots(result_set, relation) children = ValueSet.new found = ValueSet.new for task in result_set next if children.include?(task) task_children = task.generated_subgraph(relation) found -= task_children children.merge(task_children) found << task end found end |
#real_plan ⇒ Object
If this plan is a toplevel plan, returns self. If it is a transaction, returns the underlying plan
114 115 116 117 118 119 120 |
# File 'lib/roby/plan.rb', line 114 def real_plan ret = self while ret.respond_to?(:plan) ret = ret.plan end ret end |
#remote_tasks ⇒ Object
385 386 387 388 389 390 391 |
# File 'lib/roby/plan.rb', line 385 def remote_tasks if local_tasks = task_index.by_owner[Roby::Distributed] known_tasks - local_tasks else known_tasks end end |
#remove_object(object) ⇒ Object
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 |
# File 'lib/roby/plan.rb', line 667 def remove_object(object) if !object.root_object? raise ArgumentError, "cannot remove #{object} which is a non-root object" elsif object.plan != self if known_tasks.include?(object) || free_events.include?(object) raise ArgumentError, "#{object} is included in #{self} but #plan == #{object.plan}" elsif !object.plan if object.removed_at raise ArgumentError, "#{object} has been removed at\n #{object.removed_at.join("\n ")}" else raise ArgumentError, "#{object} has not been included in this plan" end end raise ArgumentError, "#{object} is not in #{self}: #plan == #{object.plan}" end # Remove relations first. This is needed by transaction since # removing relations may need wrapping some new objects, and in # that case these new objects will be discovered as well object.clear_relations @free_events.delete(object) @missions.delete(object) @known_tasks.delete(object) @keepalive.delete(object) force_gc.delete(object) object.plan = nil object.removed_at = caller case object when EventGenerator finalized_event(object) when Task task_index.remove(object) for ev in object.bound_events.values task_events.delete(ev) finalized_event(ev) end finalized_task(object) else raise ArgumentError, "unknown object type #{object}" end self end |
#remove_repair(task) ⇒ Object
Removes task from the set of active plan repairs.
See also #repairs and #add_repair
502 503 504 505 506 507 508 509 510 511 |
# File 'lib/roby/plan.rb', line 502 def remove_repair(task) repairs.delete_if do |ev, repair| if repair == task if ev.generator.respond_to?(:task) task_index.repaired_tasks.delete(ev.generator.task) end true end end end |
#remove_task(t) ⇒ Object
Backward compatibility
718 719 720 |
# File 'lib/roby/plan.rb', line 718 def remove_task(t) # :nodoc: remove_object(t) end |
#remove_transaction(trsc) ⇒ Object
Removes the transaction trsc from the list of known transactions built on this plan
325 326 327 328 |
# File 'lib/roby/plan.rb', line 325 def remove_transaction(trsc) transactions.delete(trsc) removed_transaction(trsc) end |
#removed_transaction(trsc) ⇒ Object
Hook called when a new transaction has been built on top of this plan
330 |
# File 'lib/roby/plan.rb', line 330 def removed_transaction(trsc); super if defined? super end |
#repairs_for(event) ⇒ Object
Return all repairs which apply on event
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
# File 'lib/roby/plan.rb', line 514 def repairs_for(event) result = Hash.new if event.generator.respond_to?(:task) equivalent_generators = event.generator.generated_subgraph(EventStructure::Forwarding) history = event.generator.task.history id = event.propagation_id index = history.index(event) while index < history.size ev = history[index] break if ev.propagation_id != id if equivalent_generators.include?(ev.generator) && (task = repairs[ev]) result[ev] = task end index += 1 end elsif task = repairs[event] result[event] = task end result end |
#replace(from, to) ⇒ Object
215 216 217 218 219 |
# File 'lib/roby/plan.rb', line 215 def replace(from, to) handle_replace(from, to) do from.replace_subplan_by(to) end end |
#replace_task(from, to) ⇒ Object
209 210 211 212 213 |
# File 'lib/roby/plan.rb', line 209 def replace_task(from, to) handle_replace(from, to) do from.replace_by(to) end end |
#replaced(from, to) ⇒ Object
Hook called when to has replaced from in this plan
222 |
# File 'lib/roby/plan.rb', line 222 def replaced(from, to); super if defined? super end |
#respawn(task) ⇒ Object
Replace task with a fresh copy of itself
753 754 755 756 757 758 759 |
# File 'lib/roby/plan.rb', line 753 def respawn(task) new_task = task.class.new(task.arguments.dup) replace_task(task, new_task) Control.once { new_task.start!(nil) } new_task end |
#sibling_on?(peer) ⇒ Boolean
If this object is the main plan, checks if we are subscribed to the whole remote plan
67 68 69 70 71 |
# File 'lib/roby/plan.rb', line 67 def sibling_on?(peer) if Roby.plan == self then peer.remote_plan else super end end |
#size ⇒ Object
Count of tasks in this plan
468 |
# File 'lib/roby/plan.rb', line 468 def size; @known_tasks.size end |
#unneeded_events ⇒ Object
The set of events that can be removed from the plan
453 454 455 456 457 458 459 460 461 |
# File 'lib/roby/plan.rb', line 453 def unneeded_events useful_events = self.useful_events result = (free_events - useful_events) result.delete_if do |ev| transactions.any? { |trsc| trsc.wrap(ev, false) } end result end |
#unneeded_tasks ⇒ Object
Returns the set of unused tasks
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
# File 'lib/roby/plan.rb', line 394 def unneeded_tasks # Get the set of local tasks that are serving one of our own missions or # permanent tasks useful = self.locally_useful_tasks # Append to that the set of tasks that are useful for our peers and # include the set of local tasks that are serving tasks in # +remotely_useful+ remotely_useful = Distributed.remotely_useful_objects(remote_tasks, true, nil) serving_remote = useful_task_component(local_tasks, useful & local_tasks, remotely_useful) useful.merge remotely_useful useful.merge serving_remote (known_tasks - useful) end |
#useful_event_component(useful_events) ⇒ Object
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/roby/plan.rb', line 418 def useful_event_component(useful_events) current_size = useful_events.size for rel in EventStructure.relations next unless rel.root_relation? for subgraph in rel.components(free_events, false) subgraph = subgraph.to_value_set if subgraph.intersects?(useful_events) || subgraph.intersects?(task_events) useful_events.merge(subgraph) if useful_events.include_all?(free_events) return free_events end end end if useful_events.include_all?(free_events) return free_events end end if current_size != useful_events.size useful_event_component(useful_events) else useful_events end end |
#useful_events ⇒ Object
Computes the set of events that are useful in the plan Events are ‘useful’ when they are chained to a task.
447 448 449 450 |
# File 'lib/roby/plan.rb', line 447 def useful_events return ValueSet.new if free_events.empty? (free_events & useful_event_component(ValueSet.new)) end |
#useful_task?(task) ⇒ Boolean
Computes the set of useful tasks and checks that task is in it. This is quite slow. It is here for debugging purposes. Do not use it in production code
414 415 416 |
# File 'lib/roby/plan.rb', line 414 def useful_task?(task) known_tasks.include?(task) && !unneeded_tasks.include?(task) end |
#useful_task_component(complete_set, useful_set, seeds) ⇒ Object
Merges the set of tasks that are useful for seeds into useful_set. Only the tasks that are in complete_set are included.
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/roby/plan.rb', line 334 def useful_task_component(complete_set, useful_set, seeds) old_useful_set = useful_set.dup for rel in TaskStructure.relations next unless rel.root_relation? for subgraph in rel.generated_subgraphs(seeds, false) useful_set.merge(subgraph) end end if complete_set useful_set &= complete_set end if useful_set.size == old_useful_set.size || (complete_set && useful_set.size == complete_set.size) useful_set else useful_task_component(complete_set, useful_set, (useful_set - old_useful_set)) end end |