Class: Roby::Plan
- Inherits:
-
DistributedObject
- Object
- DistributedObject
- Roby::Plan
- Extended by:
- Logger::Forward, Logger::Hierarchy
- Includes:
- DRoby::EventLogging, DRoby::Identifiable, DRoby::V5::PlanDumper, GUI::GraphvizPlan, GUI::RelationsCanvasPlan
- Defined in:
- lib/roby/plan.rb,
lib/roby.rb,
lib/roby/event_structure/temporal_constraints.rb,
lib/roby/droby/enable.rb
Overview
A plan object manages a collection of tasks and events.
Direct Known Subclasses
DRoby::RebuiltPlan, ExecutablePlan, TemplatePlan, Transaction
Defined Under Namespace
Classes: ReplacementFilter, Trigger, UsefulFreeEventVisitor
Constant Summary
Constants included from GUI::RelationsCanvasPlan
GUI::RelationsCanvasPlan::PLAN_STROKE_WIDTH
Class Attribute Summary collapse
-
.structure_checks {|the| ... } ⇒ Object
readonly
A set of structure checking procedures that must be performed on all plans.
Instance Attribute Summary collapse
-
#active_fault_response_tables ⇒ Object
readonly
The list of fault response tables that are currently globally active on this plan.
-
#event_logger ⇒ Object
The event logger.
-
#event_relation_graphs ⇒ Object
readonly
The graphs that make event relations, formatted as required by Relations::DirectedRelationSupport#relation_graphs.
-
#free_events ⇒ Object
readonly
The list of events that are not included in a task.
-
#graph_observer ⇒ Object
readonly
The observer object that reacts to relation changes.
-
#local_owner ⇒ Object
The Peer ID of the local owner (i.e. of the local process / execution engine).
-
#null_event_relation_graphs ⇒ Object
readonly
A set of empty graphs that match #event_relation_graphs.
-
#null_task_relation_graphs ⇒ Object
readonly
A set of empty graphs that match #task_relation_graphs.
-
#plan_services ⇒ Object
readonly
The set of PlanService instances that are defined on this plan.
-
#structure_checks {|the| ... } ⇒ Object
readonly
The set of blocks that should be called to check the structure of the plan.
-
#task_events ⇒ Object
readonly
The set of events that are defined by #tasks.
-
#task_index ⇒ Object
readonly
The task index for this plan.
-
#task_relation_graphs ⇒ Object
readonly
The graphs that make task relations, formatted as required by Relations::DirectedRelationSupport#relation_graphs.
-
#tasks ⇒ Object
readonly
The list of tasks that are included in this plan.
-
#transactions ⇒ Object
readonly
The set of transactions which are built on top of this plan.
-
#triggers ⇒ Object
readonly
A set of pair of task matching objects and blocks defining this plan’s triggers.
Attributes included from GUI::RelationsCanvasPlan
Attributes included from GUI::GraphvizPlan
Attributes inherited from DistributedObject
Class Method Summary collapse
- .can_gc?(task) ⇒ Boolean
-
.check_failed_missions(plan) ⇒ Object
Get all missions that have failed.
- .instanciate_relation_graphs(graph_observer: nil) ⇒ Object
Instance Method Summary collapse
-
#[](object, create = true) ⇒ 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). -
#add(objects) ⇒ Object
call-seq: plan.add(task) => plan plan.add(event) => plan plan.add([task, event, task2, …]) => plan plan.add([t1, t2, …]) => plan.
-
#add_job_action(action) ⇒ Object
Add an action as a job.
-
#add_mission(task) ⇒ Object
deprecated
Deprecated.
use #add_mission_task instead
-
#add_mission_task(task) ⇒ Object
Add a task to the plan’s set of missions.
-
#add_permanent(object) ⇒ Object
deprecated
Deprecated.
use #add_permanent_task or #add_permanent_event instead
-
#add_permanent_event(event) ⇒ Object
Mark an event as permanent, optionally adding to the plan.
-
#add_permanent_task(task) ⇒ Object
Mark a task as permanent, optionally adding to the plan.
-
#add_plan_service(service) ⇒ Object
Register a new plan service on this plan.
-
#add_trigger(query_object) {|task| ... } ⇒ Object
Add a trigger.
-
#added_transaction(trsc) ⇒ Object
Hook called when a new transaction has been built on top of this plan.
- #apply_replacement_operations(new_relations, removed_relations) ⇒ Object
- #apply_triggers_matches(matches) ⇒ Object
- #call_structure_check_handler(handler) ⇒ Object
-
#check_structure ⇒ Hash<ExecutionException,Array<Roby::Task>,nil>
Perform the structure checking step by calling the procs registered in #structure_checks and Plan.structure_checks.
-
#clear ⇒ Object
Remove all tasks.
- #clear! ⇒ Object
- #compute_subplan_replacement(mappings, relation_graphs, child_objects: true) ⇒ Object private
-
#compute_useful_free_events ⇒ Set<EventGenerator>
private
Compute the set of events that are “useful” to the plan.
-
#compute_useful_tasks(seeds, graphs: default_useful_task_graphs) ⇒ Set
private
Compute the subplan that is useful for a given set of tasks.
- #copy_relation_graphs_to(copy, mappings) ⇒ Object
-
#copy_task_marks(to:, from:) ⇒ Object
Apply to
to
the marks (permanent, mission) offrom
. -
#copy_to(copy) ⇒ Object
deprecated
Deprecated.
use #merge instead
- #create_null_relations ⇒ Object
- #create_relations ⇒ Object
- #dedupe(source) ⇒ Object
- #deep_copy ⇒ Object
-
#deep_copy_to(copy) ⇒ Object
Copies this plan’s state (tasks, events and their relations) into the provided plan.
-
#default_useful_task_graphs ⇒ Object
private
Default set of graphs that should be discovered by #compute_useful_tasks.
- #dup ⇒ Object
-
#each_event_relation_graph {|graph| ... } ⇒ Object
Enumerate the graph objects that contain this plan’s event relation information.
-
#each_object_in_transaction_stack(object) {|object| ... } ⇒ Object
Enumerate object identities along the transaction stack.
-
#each_relation_graph(&block) ⇒ Object
Enumerate all graphs (event and tasks) that form this plan.
-
#each_task {|task| ... } ⇒ Object
Iterates on all tasks.
-
#each_task_relation_graph {|graph| ... } ⇒ Object
Enumerate the graph objects that contain this plan’s task relation information.
- #edit ⇒ Object
-
#empty? ⇒ Boolean
Returns true if there is no task in this plan.
-
#event_relation_graph_for(model) ⇒ Object
Resolves an event graph object from the graph class (i.e. the graph model).
-
#executable? ⇒ Boolean
Check that this is an executable plan.
-
#execute ⇒ Object
Calls the given block in the execution thread of this plan’s engine.
- #finalize_event(event, timestamp = nil) ⇒ Object
- #finalize_task(task, timestamp = nil) ⇒ Object
-
#finalized_event(event) ⇒ Object
Hook called when
event
has been removed from this plan. -
#finalized_task(task) ⇒ Object
Hook called when
task
has been removed from this plan. -
#find_all_plan_services(task) ⇒ Object
Find all the defined plan services for a given task.
-
#find_local_tasks(*args, &block) ⇒ Object
Starts a local query on this plan.
-
#find_plan_difference(other_plan, mappings) ⇒ Object
Finds a single difference between this plan and the other plan, using the provided mappings to map objects from self to object in other_plan.
-
#find_plan_service(task) ⇒ Object
If at least one plan service is defined for
task
, returns one of them. -
#find_tasks(model = nil, args = nil) ⇒ Object
Returns a Query object that applies on this plan.
- #find_triggers_matches(plan) ⇒ Object
- #force_replace(from, to) ⇒ Object
- #force_replace_task(from, to) ⇒ Object
-
#format_exception_set(result, new) ⇒ Object
private
Normalize the value returned by one of the #structure_checks, by computing the list of propagation parents if they were not specified in the return value.
- #handle_force_replace(from, to) {|from, to| ... } ⇒ Object
-
#handle_replace(from, to) ⇒ Object
:nodoc:.
-
#has_free_event?(generator) ⇒ Boolean
Tests whether a free event is present in this plan.
-
#has_task?(task) ⇒ Boolean
Tests whether a task is present in this plan.
-
#has_task_event?(generator) ⇒ Boolean
Tests whether a task event is present in this plan.
-
#in_transaction ⇒ Object
Creates a new transaction and yields it.
-
#in_useful_subplan?(reference_task, tested_task) ⇒ Boolean
Tests whether a task is useful from the point of view of a reference task.
-
#include?(object) ⇒ Boolean
deprecated
Deprecated.
use the more specific #has_task?, #has_free_event? or #has_task_event? instead
-
#initialize(graph_observer: nil, event_logger: DRoby::NullEventLogger.new) ⇒ Plan
constructor
A new instance of Plan.
-
#inspect ⇒ Object
:nodoc:.
- #local_tasks ⇒ Object
- #locally_useful_roots(with_transactions: true) ⇒ Object
- #locally_useful_tasks ⇒ Object
-
#make_useless(tasks) ⇒ Object
Ensures that the given tasks will end up being processed without forcefully stopping anything.
-
#merge(plan) ⇒ Object
Merges the content of a plan into self.
-
#merge!(plan) ⇒ Object
Moves the content of other_plan into self, and clears other_plan.
- #merge_base(plan) ⇒ Object
- #merge_relation_graphs(plan) ⇒ Object
- #merge_transaction(transaction, merged_graphs, _added, _removed, _updated) ⇒ Object
- #merge_transaction!(transaction, merged_graphs, added, removed, updated) ⇒ Object
-
#merged_plan(plan) ⇒ Object
Hook called when a #merge has been performed.
-
#merging_plan(plan) ⇒ Object
Hook called just before performing a #merge.
-
#mission?(task) ⇒ Boolean
deprecated
Deprecated.
use #mission_task? instead
-
#mission_task?(task) ⇒ Boolean
Checks if a task is part of the plan’s missions.
-
#mission_tasks ⇒ Object
The set of the robot’s missions.
-
#move_plan_service(service, new_task) ⇒ Object
Change the actual task a given plan service is representing.
-
#normalize_add_arguments(objects) ⇒ Object
private
Normalize an validate the arguments to #add into a list of plan objects.
-
#notify_event_status_change(event, status) ⇒ Object
private
Perform notifications related to the status change of an event.
-
#notify_task_status_change(task, status) ⇒ Object
private
Perform notifications related to the status change of a task.
-
#num_events ⇒ Object
The number of events, both free and task events.
-
#num_free_events ⇒ Object
The number of events that are not task events.
-
#num_tasks ⇒ Object
The number of tasks.
-
#owns?(object) ⇒ Boolean
True if this plan owns the given object, i.e.
-
#permanent?(object) ⇒ Boolean
deprecated
Deprecated.
use #permanent_task? or #permanent_event? instead
-
#permanent_event?(generator) ⇒ Boolean
True if the given event is registered as a permanent event on self.
-
#permanent_events ⇒ Object
The list of events that are kept outside GC.
-
#permanent_task?(task) ⇒ Boolean
True if the given task is registered as a permanent task on self.
-
#permanent_tasks ⇒ Object
The set of tasks that are kept around “just in case”.
-
#query_result_set(matcher) ⇒ Object
private
Internal delegation from the matchers to the plan object to determine the ‘right’ query algorithm.
-
#real_plan ⇒ Object
If this plan is a toplevel plan, returns self.
-
#recreate(task) ⇒ Object
Replace
task
with a fresh copy of itself. - #refresh_relations ⇒ Object
-
#register_event(event) ⇒ Object
private
Registers a task object in this plan.
-
#register_task(task) ⇒ Object
private
Registers a task object in this plan.
-
#registered_plan_services_for(task) ⇒ Object
Whether there are services registered for the given task.
- #remote_tasks ⇒ Object
-
#remove_fault_response_table(table_model) ⇒ void
Remove a fault response table that has been added with #use_fault_response_table.
- #remove_free_event(event, timestamp = Time.now) ⇒ Object
- #remove_free_event!(event, timestamp = Time.now) ⇒ Object
-
#remove_object(object, timestamp = Time.now) ⇒ Object
deprecated
Deprecated.
use #remove_task or #remove_free_event instead
-
#remove_plan_service(service) ⇒ Object
Deregisters a plan service from this plan.
- #remove_task(task, timestamp = Time.now) ⇒ Object
- #remove_task!(task, timestamp = Time.now) ⇒ Object
-
#remove_transaction(trsc) ⇒ Object
Removes the transaction
trsc
from the list of known transactions built on this plan. -
#remove_trigger(trigger) ⇒ void
Removes a trigger.
-
#replace(from, to, filter: ReplacementFilter::Null.new) ⇒ Object
Replace
from
byto
in the plan, in all relations in whichfrom
and its events are /children/. - #replace_relation_graphs(merged_graphs) ⇒ Object
-
#replace_subplan(task_mappings, event_mappings, task_children: true, event_children: true) ⇒ Object
Replace subgraphs by another in the plan.
-
#replace_task(from, to, filter: ReplacementFilter::Null.new) ⇒ Object
Replace the task
from
byto
in all relationsfrom
is part of (including events). -
#replaced(replaced_task, replacing_task) ⇒ Object
Hook called when
replacing_task
has replacedreplaced_task
in this plan. -
#replan(task) ⇒ Roby::Task
Creates a new planning pattern replacing the given task and its current planner.
-
#root_plan? ⇒ Boolean
True if this plan is root in the plan hierarchy.
-
#same_plan?(other_plan, mappings) ⇒ Boolean
Compares this plan to
other_plan
, mappings providing the mapping from task/Events inself
to task/events in other_plan. -
#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.
-
#static_garbage_collect(protected_roots: Set.new, &block) ⇒ Object
Run a garbage collection pass.
-
#task_relation_graph_for(model) ⇒ Object
Resolves a task graph object from the graph class (i.e. the graph model).
-
#template? ⇒ Boolean
A template plan is meant to be injected in another plan.
-
#transaction_stack ⇒ Array
Returns the set of stacked transaction.
-
#unmark_mission(task) ⇒ Object
deprecated
Deprecated.
use #unmark_mission_task instead
-
#unmark_mission_task(task) ⇒ Object
Removes a task from the plan’s missions.
-
#unmark_permanent(object) ⇒ Object
deprecated
Deprecated.
use #unmark_permanent_task or #unmark_permanent_event instead
-
#unmark_permanent_event(event) ⇒ Object
Removes a task from the set of permanent tasks.
-
#unmark_permanent_task(task) ⇒ Object
Removes a task from the set of permanent tasks.
-
#unneeded_events ⇒ Object
The set of events that can be removed from the plan.
- #unneeded_tasks(additional_useful_roots: Set.new) ⇒ Object
-
#use_fault_response_table(table_model, arguments = {}) ⇒ Coordination::FaultResponseTable, void
Enables a fault response table on this plan.
-
#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
task
is in it. - #useful_tasks(additional_roots: Set.new, with_transactions: true) ⇒ Object
-
#validate_graphs(graphs) ⇒ Object
Verifies that all graphs that should be acyclic are.
-
#verify_plan_object_finalization_sanity(object) ⇒ Object
private
Perform sanity checks on a plan object that will be finalized.
Methods included from DRoby::V5::PlanDumper
Methods included from DRoby::Identifiable
Methods included from GUI::RelationsCanvasPlan
#display, #display_create, #display_name, #display_parent
Methods included from GUI::GraphvizPlan
#all_events, #apply_layout, #compute_depth, #each_displayed_relation, #each_edge, #each_layout_relation, #layout_relations, #relations_to_dot, #to_dot
Methods included from DRoby::EventLogging
#log, #log_flush_cycle, #log_queue_size, #log_timepoint, #log_timepoint_group, #log_timepoint_group_end, #log_timepoint_group_start
Methods inherited from DistributedObject
#add_owner, #clear_owners, #initialize_copy, #owned_by?, #remove_owner
Constructor Details
#initialize(graph_observer: nil, event_logger: DRoby::NullEventLogger.new) ⇒ Plan
Returns a new instance of Plan.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/roby/plan.rb', line 88 def initialize(graph_observer: nil, event_logger: DRoby::NullEventLogger.new) @local_owner = DRoby::PeerID.new("local") @tasks = Set.new @free_events = Set.new @task_events = Set.new @transactions = Set.new @fault_response_tables = [] @triggers = [] @plan_services = {} self.event_logger = event_logger @active_fault_response_tables = [] @task_index = Roby::Queries::Index.new @graph_observer = graph_observer create_relations create_null_relations super() end |
Class Attribute Details
.structure_checks {|the| ... } ⇒ Object (readonly)
A set of structure checking procedures that must be performed on all plans
1782 1783 1784 |
# File 'lib/roby/plan.rb', line 1782 def structure_checks @structure_checks end |
Instance Attribute Details
#active_fault_response_tables ⇒ Object (readonly)
The list of fault response tables that are currently globally active on this plan
1946 1947 1948 |
# File 'lib/roby/plan.rb', line 1946 def active_fault_response_tables @active_fault_response_tables end |
#event_logger ⇒ Object
The event logger
83 84 85 |
# File 'lib/roby/plan.rb', line 83 def event_logger @event_logger end |
#event_relation_graphs ⇒ Object (readonly)
The graphs that make event relations, formatted as required by Relations::DirectedRelationSupport#relation_graphs
182 183 184 |
# File 'lib/roby/plan.rb', line 182 def event_relation_graphs @event_relation_graphs end |
#free_events ⇒ Object (readonly)
The list of events that are not included in a task
36 37 38 |
# File 'lib/roby/plan.rb', line 36 def free_events @free_events end |
#graph_observer ⇒ Object (readonly)
The observer object that reacts to relation changes
86 87 88 |
# File 'lib/roby/plan.rb', line 86 def graph_observer @graph_observer end |
#local_owner ⇒ Object
The Peer ID of the local owner (i.e. of the local process / execution engine)
12 13 14 |
# File 'lib/roby/plan.rb', line 12 def local_owner @local_owner end |
#null_event_relation_graphs ⇒ Object (readonly)
A set of empty graphs that match #event_relation_graphs
Used for finalized events
187 188 189 |
# File 'lib/roby/plan.rb', line 187 def null_event_relation_graphs @null_event_relation_graphs end |
#null_task_relation_graphs ⇒ Object (readonly)
A set of empty graphs that match #task_relation_graphs
Used for finalized tasks
176 177 178 |
# File 'lib/roby/plan.rb', line 176 def null_task_relation_graphs @null_task_relation_graphs end |
#plan_services ⇒ Object (readonly)
The set of PlanService instances that are defined on this plan
63 64 65 |
# File 'lib/roby/plan.rb', line 63 def plan_services @plan_services end |
#structure_checks {|the| ... } ⇒ Object (readonly)
The set of blocks that should be called to check the structure of the plan.
1771 1772 1773 |
# File 'lib/roby/plan.rb', line 1771 def structure_checks @structure_checks end |
#task_events ⇒ Object (readonly)
The set of events that are defined by #tasks
21 22 23 |
# File 'lib/roby/plan.rb', line 21 def task_events @task_events end |
#task_index ⇒ Object (readonly)
The task index for this plan. This is a Queries::Index object which allows efficient resolving of queries.
16 17 18 |
# File 'lib/roby/plan.rb', line 16 def task_index @task_index end |
#task_relation_graphs ⇒ Object (readonly)
The graphs that make task relations, formatted as required by Relations::DirectedRelationSupport#relation_graphs
171 172 173 |
# File 'lib/roby/plan.rb', line 171 def task_relation_graphs @task_relation_graphs end |
#tasks ⇒ Object (readonly)
The list of tasks that are included in this plan
19 20 21 |
# File 'lib/roby/plan.rb', line 19 def tasks @tasks end |
#transactions ⇒ Object (readonly)
The set of transactions which are built on top of this plan
51 52 53 |
# File 'lib/roby/plan.rb', line 51 def transactions @transactions end |
#triggers ⇒ Object (readonly)
A set of pair of task matching objects and blocks defining this plan’s triggers
See #add_trigger
48 49 50 |
# File 'lib/roby/plan.rb', line 48 def triggers @triggers end |
Class Method Details
.can_gc?(task) ⇒ Boolean
1538 1539 1540 1541 1542 1543 1544 1545 1546 |
# File 'lib/roby/plan.rb', line 1538 def self.can_gc?(task) if task.starting? true # wait for the task to be started before deciding ... elsif task.running? && !task.finishing? task.event(:stop).controlable? else true end end |
.check_failed_missions(plan) ⇒ Object
Get all missions that have failed
1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 |
# File 'lib/roby/plan.rb', line 1786 def self.check_failed_missions(plan) result = [] plan.mission_tasks.each do |task| result << MissionFailedError.new(task) if task.failed? end plan.permanent_tasks.each do |task| result << PermanentTaskError.new(task) if task.failed? end result end |
.instanciate_relation_graphs(graph_observer: nil) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/roby/plan.rb', line 137 def self.instanciate_relation_graphs(graph_observer: nil) task_relation_graphs = Relations::Space.new_relation_graph_mapping Task.all_relation_spaces.each do |space| task_relation_graphs.merge!( space.instanciate(observer: graph_observer) ) end event_relation_graphs = Relations::Space.new_relation_graph_mapping EventGenerator.all_relation_spaces.each do |space| event_relation_graphs.merge!( space.instanciate(observer: graph_observer) ) end [task_relation_graphs, event_relation_graphs] end |
Instance Method Details
#[](object, create = true) ⇒ 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#[]
1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 |
# File 'lib/roby/plan.rb', line 1524 def [](object, create = true) if object.plan == self object elsif !object.finalized? && object.plan.template? add(object) object elsif object.finalized? && create raise ArgumentError, "#{object} is has been finalized, and can't be reused" else raise ArgumentError, "#{object} is not from #{self}" end end |
#add(objects) ⇒ Object
call-seq:
plan.add(task) => plan
plan.add(event) => plan
plan.add([task, event, task2, ...]) => plan
plan.add([t1, t2, ...]) => plan
Adds the subplan of the given tasks and events into the plan.
That means that it adds the listed tasks/events and the task/events that are reachable through any relations).
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 |
# File 'lib/roby/plan.rb', line 1107 def add(objects) is_scalar = objects.respond_to?(:each) objects = normalize_add_arguments(objects) plans = Set.new objects.each do |plan_object| p = plan_object.plan next if p == self if plan_object.removed_at raise ArgumentError, "cannot add #{plan_object} in #{self}, "\ "it has been removed from the plan" elsif !p raise InternalError, "there seem to be an inconsistency, #{plan_object}#plan "\ "is nil but #removed_at is not set" elsif p.empty? raise InternalError, "there seem to be an inconsistency, #{plan_object} "\ "is associated with #{p} but #{p} is empty" elsif !p.template? raise ModelViolation, "cannot add #{plan_object} in #{self}, "\ "it is already included in #{p}" end plans << p end plans.each do |p| merge!(p) end if is_scalar objects.first else objects end end |
#add_job_action(action) ⇒ Object
Add an action as a job
551 552 553 554 555 |
# File 'lib/roby/plan.rb', line 551 def add_job_action(action) add_mission_task( action.as_plan(job_id: Roby::Interface::Job.allocate_job_id) ) end |
#add_mission(task) ⇒ Object
use #add_mission_task instead
506 507 508 509 510 511 |
# File 'lib/roby/plan.rb', line 506 def add_mission(task) Roby.warn_deprecated( "#add_mission is deprecated, use #add_mission_task instead" ) add_mission_task(task) end |
#add_mission_task(task) ⇒ Object
Add a task to the plan’s set of missions
A mission represents the system’s overall goal. As such a mission task and all its dependencies are protected against the garbage collection mechanisms, and the emission of a mission’s failed event causes a MissionFailedError exception to be generated.
Note that this method should be used to add the task to the plan and mark it as mission, and to mark an already added task as mission as well.
539 540 541 542 543 544 545 546 547 548 |
# File 'lib/roby/plan.rb', line 539 def add_mission_task(task) task = normalize_add_arguments([task]).first return if mission_tasks.include?(task) add([task]) mission_tasks << task task.mission = true if task.self_owned? notify_task_status_change(task, :mission) task end |
#add_permanent(object) ⇒ Object
use #add_permanent_task or #add_permanent_event instead
583 584 585 586 587 588 589 590 591 592 593 594 595 |
# File 'lib/roby/plan.rb', line 583 def add_permanent(object) Roby.warn_deprecated( "#add_permanent is deprecated, use either #add_permanent_task "\ "or #add_permanent_event instead" ) object = normalize_add_arguments([object]).first if object.respond_to?(:to_task) add_permanent_task(object) else add_permanent_event(object) end object end |
#add_permanent_event(event) ⇒ Object
Mark an event as permanent, optionally adding to the plan
Permanent events are protected against garbage collection
668 669 670 671 672 673 674 675 676 |
# File 'lib/roby/plan.rb', line 668 def add_permanent_event(event) event = normalize_add_arguments([event]).first return if permanent_events.include?(event) add([event]) permanent_events << event notify_event_status_change(event, :permanent) event end |
#add_permanent_task(task) ⇒ Object
Mark a task as permanent, optionally adding to the plan
Permanent tasks are protected against garbage collection. Like missions, failure of a permanent task will generate a plan exception Roby::PermanentTaskError. Unlike missions, this exception is non-fatal.
634 635 636 637 638 639 640 641 642 |
# File 'lib/roby/plan.rb', line 634 def add_permanent_task(task) task = normalize_add_arguments([task]).first return if permanent_tasks.include?(task) add([task]) permanent_tasks << task notify_task_status_change(task, :permanent) task end |
#add_plan_service(service) ⇒ Object
Register a new plan service on this plan
926 927 928 929 930 931 932 933 934 935 936 |
# File 'lib/roby/plan.rb', line 926 def add_plan_service(service) if service.task.plan != self raise ArgumentError, "trying to register a plan service on #{self} for "\ "#{service.task}, which is included in #{service.task.plan}" end set = (plan_services[service.task] ||= Set.new) set << service self end |
#add_trigger(query_object) {|task| ... } ⇒ Object
Add a trigger
This registers a notification: the given block will be called for each new task that match the given query object. It yields right away for the tasks that are already in the plan
1195 1196 1197 1198 1199 1200 1201 1202 |
# File 'lib/roby/plan.rb', line 1195 def add_trigger(query_object, &block) tr = Trigger.new(query_object, block) triggers << tr tr.each(self) do |t| tr.call(t) end tr end |
#added_transaction(trsc) ⇒ Object
Hook called when a new transaction has been built on top of this plan
1223 |
# File 'lib/roby/plan.rb', line 1223 def added_transaction(trsc); end |
#apply_replacement_operations(new_relations, removed_relations) ⇒ Object
1049 1050 1051 1052 1053 1054 1055 1056 |
# File 'lib/roby/plan.rb', line 1049 def apply_replacement_operations(new_relations, removed_relations) removed_relations.each do |graph, parent, child| graph.remove_relation(parent, child) end new_relations.each do |graph, parent, child, info| graph.add_relation(parent, child, info) end end |
#apply_triggers_matches(matches) ⇒ Object
319 320 321 322 323 324 325 |
# File 'lib/roby/plan.rb', line 319 def apply_triggers_matches(matches) matches.each do |trigger, matched_tasks| matched_tasks.each do |t| trigger.call(t) end end end |
#call_structure_check_handler(handler) ⇒ Object
1815 1816 1817 |
# File 'lib/roby/plan.rb', line 1815 def call_structure_check_handler(handler) handler.call(self) end |
#check_structure ⇒ Hash<ExecutionException,Array<Roby::Task>,nil>
Perform the structure checking step by calling the procs registered in #structure_checks and structure_checks
1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 |
# File 'lib/roby/plan.rb', line 1823 def check_structure # Do structure checking and gather the raised exceptions exceptions = {} (Plan.structure_checks + structure_checks).each do |prc| new_exceptions = call_structure_check_handler(prc) next unless new_exceptions format_exception_set(exceptions, new_exceptions) end exceptions end |
#clear ⇒ Object
Remove all tasks
1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 |
# File 'lib/roby/plan.rb', line 1678 def clear tasks = @tasks @tasks = Set.new free_events = @free_events @free_events = Set.new clear! remaining = tasks.find_all do |t| if t.running? true else finalize_task(t) false end end unless remaining.empty? Roby.warn "#{remaining.size} tasks remaining after clearing "\ "the plan as they are still running" remaining.each do |t| Roby.warn " #{t}" end end free_events.each do |e| finalize_event(e) end self end |
#clear! ⇒ Object
1668 1669 1670 1671 1672 1673 1674 1675 |
# File 'lib/roby/plan.rb', line 1668 def clear! each_task_relation_graph(&:clear) each_event_relation_graph(&:clear) @free_events.clear @tasks.clear @task_index.clear @task_events.clear end |
#compute_subplan_replacement(mappings, relation_graphs, child_objects: true) ⇒ Object
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.
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 |
# File 'lib/roby/plan.rb', line 999 def compute_subplan_replacement(mappings, relation_graphs, child_objects: true) mappings = mappings.dup mappings.compare_by_identity new_relations = [] removed_relations = [] relation_graphs.each do |graph| next if graph.strong? resolved_mappings = {} resolved_mappings.compare_by_identity mappings.each do |obj, (mapped_obj, mapped_obj_resolver)| next if !mapped_obj && !mapped_obj_resolver graph.each_in_neighbour(obj) do |parent| next if mappings.key?(parent) unless graph.copy_on_replace? removed_relations << [graph, parent, obj] end unless mapped_obj mapped_obj = mapped_obj_resolver.call(obj) resolved_mappings[obj] = mapped_obj end new_relations << [ graph, parent, mapped_obj, graph.edge_info(parent, obj) ] end next unless child_objects graph.each_out_neighbour(obj) do |child| next if mappings.key?(child) unless graph.copy_on_replace? removed_relations << [graph, obj, child] end unless mapped_obj mapped_obj = mapped_obj_resolver.call(obj) resolved_mappings[obj] = mapped_obj end new_relations << [ graph, mapped_obj, child, graph.edge_info(obj, child) ] end end mappings.merge!(resolved_mappings) end [new_relations, removed_relations] end |
#compute_useful_free_events ⇒ Set<EventGenerator>
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.
Compute the set of events that are “useful” to the plan.
It contains every event that is connected to an event in #permanent_events or to an event on a task in the plan
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 |
# File 'lib/roby/plan.rb', line 1377 def compute_useful_free_events # Quick path for a very common case return Set.new if free_events.empty? graphs = each_event_relation_graph .find_all { |g| g.root_relation? && !g.weak? } seen = Set.new result = permanent_events.dup pending_events = free_events.to_a until pending_events.empty? # This basically computes the subplan that contains "seed" and # determines if it is useful or not seed = pending_events.shift next if seen.include?(seed) visitors = [] graphs.each do |g| visitors << [ g, UsefulFreeEventVisitor.new( g, task_events, permanent_events ), [seed].to_set ] visitors << [ g.reverse, UsefulFreeEventVisitor.new( g.reverse, task_events, permanent_events ), [seed].to_set ] end component = [seed].to_set has_pending_seeds = true while has_pending_seeds has_pending_seeds = false visitors.each do |graph, visitor, seeds| next if seeds.empty? new_seeds = [] seeds.each do |vertex| next if visitor.finished_vertex?(vertex) next unless graph.has_vertex?(vertex) graph.depth_first_visit(vertex, visitor) do |v| new_seeds << v end end unless new_seeds.empty? has_pending_seeds = true component.merge(new_seeds) visitors.each { |g, _, s| s.merge(new_seeds) if g != graph } end seeds.clear end end seen.merge(component) result.merge(component) if visitors.any? { |_, v, _| v.useful? } end result end |
#compute_useful_tasks(seeds, graphs: default_useful_task_graphs) ⇒ Set
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.
Compute the subplan that is useful for a given set of tasks
1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 |
# File 'lib/roby/plan.rb', line 1247 def compute_useful_tasks(seeds, graphs: default_useful_task_graphs) seeds = seeds.to_set visitors = graphs.map do |g| [g, RGL::DFSVisitor.new(g), seeds.dup] end result = seeds.dup has_queued_nodes = true while has_queued_nodes has_queued_nodes = false visitors.each do |graph, visitor, queue| next if queue.empty? new_queue = [] queue.each do |vertex| if !visitor.finished_vertex?(vertex) && graph.has_vertex?(vertex) graph.depth_first_visit(vertex, visitor) do |v| yield(v) if block_given? new_queue << v end end end unless new_queue.empty? has_queued_nodes = true result.merge(new_queue) visitors.each { |g, _, s| s.merge(new_queue) if g != graph } end queue.clear end end result end |
#copy_relation_graphs_to(copy, mappings) ⇒ Object
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
# File 'lib/roby/plan.rb', line 428 def copy_relation_graphs_to(copy, mappings) each_task_relation_graph do |graph| target_graph = copy.task_relation_graph_for(graph.class) graph.each_edge do |parent, child| target_graph.add_edge( mappings[parent], mappings[child], graph.edge_info(parent, child) ) end end each_event_relation_graph do |graph| target_graph = copy.event_relation_graph_for(graph.class) graph.each_edge do |parent, child| target_graph.add_edge( mappings[parent], mappings[child], graph.edge_info(parent, child) ) end end end |
#copy_task_marks(to:, from:) ⇒ Object
Apply to to
the marks (permanent, mission) of from
It does not remove any marks from from
1615 1616 1617 1618 1619 |
# File 'lib/roby/plan.rb', line 1615 def copy_task_marks(to:, from:) add_permanent_task(to) if permanent_task?(from) add_mission_task(to) if mission_task?(from) end |
#copy_to(copy) ⇒ Object
use #merge instead
251 252 253 |
# File 'lib/roby/plan.rb', line 251 def copy_to(copy) copy.merge(self) end |
#create_null_relations ⇒ Object
111 112 113 114 115 116 117 118 |
# File 'lib/roby/plan.rb', line 111 def create_null_relations @null_task_relation_graphs, @null_event_relation_graphs = self.class.instanciate_relation_graphs(graph_observer: graph_observer) @null_task_relation_graphs.freeze @null_task_relation_graphs.each_value(&:freeze) @null_event_relation_graphs.freeze @null_event_relation_graphs.each_value(&:freeze) end |
#create_relations ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/roby/plan.rb', line 120 def create_relations @task_relation_graphs, @event_relation_graphs = self.class.instanciate_relation_graphs(graph_observer: graph_observer) @structure_checks = [] each_relation_graph do |graph| if graph.respond_to?(:check_structure) structure_checks << graph.method(:check_structure) end end end |
#dedupe(source) ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/roby/plan.rb', line 154 def dedupe(source) @task_relation_graphs.each do |relation, graph| if relation != graph graph.dedupe(source.task_relation_graph_for(relation)) end end @event_relation_graphs.each do |relation, graph| if relation != graph graph.dedupe(source.event_relation_graph_for(relation)) end end end |
#deep_copy ⇒ Object
370 371 372 373 374 |
# File 'lib/roby/plan.rb', line 370 def deep_copy plan = Roby::Plan.new mappings = deep_copy_to(plan) [plan, mappings] end |
#deep_copy_to(copy) ⇒ Object
Copies this plan’s state (tasks, events and their relations) into the provided plan
It returns the mapping from the plan objects in self
to the plan objects in copy
. For instance, if t
is a task in plan
, then
mapping = plan.copy_to(copy)
mapping[t] => corresponding task in +copy+
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 |
# File 'lib/roby/plan.rb', line 384 def deep_copy_to(copy) mappings = Hash.new do |_, k| if !include?(k) raise InternalError, "#{k} is listed in a relation, but is not included "\ "in the corresponding plan #{self}" else raise InternalError, "#{k} is an object in #{self} for which no mapping "\ "has been created in #{copy}" end end # First create a copy of all the tasks tasks.each do |t| new_t = t.dup mappings[t] = new_t t.each_event do |ev| new_ev = ev.dup new_ev.instance_variable_set :@task, new_t new_t.bound_events[ev.symbol] = new_ev mappings[ev] = new_ev end copy.register_task(new_t) new_t.each_event do |ev| copy.register_event(ev) end end free_events.each do |e| new_e = e.dup mappings[e] = new_e copy.register_event(new_e) end mission_tasks.each { |t| copy.add_mission_task(mappings[t]) } permanent_tasks.each { |t| copy.add_permanent_task(mappings[t]) } permanent_events.each { |e| copy.add_permanent_event(mappings[e]) } copy_relation_graphs_to(copy, mappings) mappings end |
#default_useful_task_graphs ⇒ Object
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.
Default set of graphs that should be discovered by #compute_useful_tasks
1235 1236 1237 |
# File 'lib/roby/plan.rb', line 1235 def default_useful_task_graphs each_task_relation_graph.find_all { |g| g.root_relation? && !g.weak? } end |
#dup ⇒ Object
231 232 233 234 235 |
# File 'lib/roby/plan.rb', line 231 def dup new_plan = Plan.new copy_to(new_plan) new_plan end |
#each_event_relation_graph {|graph| ... } ⇒ Object
Enumerate the graph objects that contain this plan’s event relation information
201 202 203 204 205 206 207 |
# File 'lib/roby/plan.rb', line 201 def each_event_relation_graph return enum_for(__method__) unless block_given? event_relation_graphs.each do |k, v| yield(v) if k == v end end |
#each_object_in_transaction_stack(object) {|object| ... } ⇒ Object
Enumerate object identities along the transaction stack
The enumeration starts with the deepest transaction and stops at the topmost plan where the object is not a transaction proxy.
2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 |
# File 'lib/roby/plan.rb', line 2015 def each_object_in_transaction_stack(object) return enum_for(__method__, object) unless block_given? current_plan = self loop do yield(current_plan, object) return unless object.transaction_proxy? current_plan = current_plan.plan object = object.__getobj__ end # Never reached end |
#each_relation_graph(&block) ⇒ Object
Enumerate all graphs (event and tasks) that form this plan
190 191 192 193 194 195 |
# File 'lib/roby/plan.rb', line 190 def each_relation_graph(&block) return enum_for(__method__) unless block_given? each_task_relation_graph(&block) each_event_relation_graph(&block) end |
#each_task {|task| ... } ⇒ Object
Iterates on all tasks
1513 1514 1515 1516 1517 |
# File 'lib/roby/plan.rb', line 1513 def each_task(&block) return enum_for(__method__) unless block_given? @tasks.each(&block) end |
#each_task_relation_graph {|graph| ... } ⇒ Object
Enumerate the graph objects that contain this plan’s task relation information
218 219 220 221 222 223 224 |
# File 'lib/roby/plan.rb', line 218 def each_task_relation_graph return enum_for(__method__) unless block_given? task_relation_graphs.each do |k, v| yield(v) if k == v end end |
#edit ⇒ Object
715 716 717 |
# File 'lib/roby/plan.rb', line 715 def edit yield if block_given? end |
#empty? ⇒ Boolean
Returns true if there is no task in this plan
1506 1507 1508 |
# File 'lib/roby/plan.rb', line 1506 def empty? @tasks.empty? && @free_events.empty? end |
#event_relation_graph_for(model) ⇒ Object
Resolves an event graph object from the graph class (i.e. the graph model)
210 211 212 |
# File 'lib/roby/plan.rb', line 210 def event_relation_graph_for(model) event_relation_graphs.fetch(model) end |
#executable? ⇒ Boolean
Check that this is an executable plan. This is always true for plain Plan objects and false for transcations
78 79 80 |
# File 'lib/roby/plan.rb', line 78 def executable? false end |
#execute ⇒ Object
Calls the given block in the execution thread of this plan’s engine. If there is no engine attached to this plan, yields immediately
See ExecutionEngine#execute
246 247 248 |
# File 'lib/roby/plan.rb', line 246 def execute yield end |
#finalize_event(event, timestamp = nil) ⇒ Object
1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 |
# File 'lib/roby/plan.rb', line 1601 def finalize_event(event, = nil) verify_plan_object_finalization_sanity(event) # Remove relations first. This is needed by transaction since # removing relations may need wrapping some new event, and in # that case these new event will be discovered as well event.clear_relations finalized_event(event) event.finalized!() end |
#finalize_task(task, timestamp = nil) ⇒ Object
1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 |
# File 'lib/roby/plan.rb', line 1578 def finalize_task(task, = nil) verify_plan_object_finalization_sanity(task) if (services = plan_services.delete(task)) services.each(&:finalized!) end # Remove relations first. This is needed by transaction since # removing relations may need wrapping some new task, and in # that case these new task will be discovered as well task.clear_relations(remove_internal: true) task.mission = false task.bound_events.each_value do |ev| finalized_event(ev) end finalized_task(task) task.bound_events.each_value do |ev| ev.finalized!() end task.finalized!() end |
#finalized_event(event) ⇒ Object
Hook called when event
has been removed from this plan
1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 |
# File 'lib/roby/plan.rb', line 1722 def finalized_event(event) log(:finalized_event, droby_id, event) return unless event.root_object? transactions.each do |trsc| next unless trsc. if (proxy = trsc.find_local_object_for_event(event)) trsc.finalized_plan_event(proxy) end end end |
#finalized_task(task) ⇒ Object
Hook called when task
has been removed from this plan
1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 |
# File 'lib/roby/plan.rb', line 1710 def finalized_task(task) transactions.each do |trsc| next unless trsc. if (proxy = trsc.find_local_object_for_task(task)) trsc.finalized_plan_task(proxy) end end log(:finalized_task, droby_id, task) end |
#find_all_plan_services(task) ⇒ Object
Find all the defined plan services for a given task
961 962 963 |
# File 'lib/roby/plan.rb', line 961 def find_all_plan_services(task) plan_services[task] || [] end |
#find_local_tasks(*args, &block) ⇒ Object
Starts a local query on this plan.
Unlike #find_tasks, when applied on a transaction, it will only match tasks that are already in the transaction.
See #find_global_tasks for a local query.
1930 1931 1932 1933 1934 |
# File 'lib/roby/plan.rb', line 1930 def find_local_tasks(*args, &block) query = find_tasks(*args, &block) query.local_scope query end |
#find_plan_difference(other_plan, mappings) ⇒ Object
Finds a single difference between this plan and the other plan, using the provided mappings to map objects from self to object in other_plan
1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 |
# File 'lib/roby/plan.rb', line 1853 def find_plan_difference(other_plan, mappings) all_self_objects = tasks | free_events | task_events all_other_objects = ( other_plan.tasks | other_plan.free_events | other_plan.task_events ) all_mapped_objects = all_self_objects.map do |obj| return [:new_object, obj] unless mappings.key?(obj) mappings[obj] end.to_set if all_mapped_objects != all_other_objects return [:removed_objects, all_other_objects - all_mapped_objects] elsif mission_tasks.map { |m| mappings[m] }.to_set != other_plan.mission_tasks return [:missions_differ] elsif permanent_tasks.map { |p| mappings[p] }.to_set != other_plan.permanent_tasks return [:permanent_tasks_differ] elsif permanent_events.map { |p| mappings[p] }.to_set != other_plan.permanent_events return [:permanent_events_differ] end each_task_relation_graph do |graph| other_graph = other_plan.task_relation_graph_for(graph.class) if (diff = graph.find_edge_difference(other_graph, mappings)) return [graph.class] + diff end end each_event_relation_graph do |graph| other_graph = other_plan.event_relation_graph_for(graph.class) if (diff = graph.find_edge_difference(other_graph, mappings)) return [graph.class] + diff end end nil end |
#find_plan_service(task) ⇒ Object
If at least one plan service is defined for task
, returns one of them. Otherwise, returns nil.
967 968 969 |
# File 'lib/roby/plan.rb', line 967 def find_plan_service(task) plan_services[task]&.first end |
#find_tasks(model = nil, args = nil) ⇒ Object
Returns a Query object that applies on this plan.
This is equivalent to
Roby::Query.new(self)
Additionally, the model
and args
options are passed to Query#which_fullfills. For example:
plan.find_tasks(Tasks::SimpleTask, id: 20)
is equivalent to
Roby::Query.new(self).which_fullfills(Tasks::SimpleTask, id: 20)
The returned query is applied on the global scope by default. This means that, if it is applied on a transaction, it will match tasks that are in the underlying plans but not yet in the transaction, import the matches in the transaction and return the new proxies.
See #find_local_tasks for a local query.
1918 1919 1920 1921 1922 |
# File 'lib/roby/plan.rb', line 1918 def find_tasks(model = nil, args = nil) q = Queries::Query.new(self) q.which_fullfills(model, args) if model || args q end |
#find_triggers_matches(plan) ⇒ Object
313 314 315 316 317 |
# File 'lib/roby/plan.rb', line 313 def find_triggers_matches(plan) triggers.map do |tr| [tr, tr.each(plan).to_a] end end |
#force_replace(from, to) ⇒ Object
731 732 733 734 735 |
# File 'lib/roby/plan.rb', line 731 def force_replace(from, to) handle_force_replace(from, to) do from.replace_subplan_by(to) end end |
#force_replace_task(from, to) ⇒ Object
725 726 727 728 729 |
# File 'lib/roby/plan.rb', line 725 def force_replace_task(from, to) handle_force_replace(from, to) do from.replace_by(to) end end |
#format_exception_set(result, new) ⇒ Object
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.
Normalize the value returned by one of the #structure_checks, by computing the list of propagation parents if they were not specified in the return value
1806 1807 1808 1809 1810 1811 1812 1813 |
# File 'lib/roby/plan.rb', line 1806 def format_exception_set(result, new) [*new].each do |error, tasks| roby_exception = error.to_execution_exception tasks = [error.parent] if !tasks && error.kind_of?(RelationFailedError) result[roby_exception] = tasks end result end |
#handle_force_replace(from, to) {|from, to| ... } ⇒ Object
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 |
# File 'lib/roby/plan.rb', line 737 def handle_force_replace(from, to) if !from.plan raise ArgumentError, "#{from} has been removed from plan, "\ "cannot use as source in a replacement" elsif !to.plan raise ArgumentError, "#{to} has been removed from plan, "\ "cannot use as target in a replacement" elsif from.plan != self raise ArgumentError, "trying to replace #{from} but its plan "\ "is #{from.plan}, expected #{self}" elsif to.plan.template? add(to) elsif to.plan != self raise ArgumentError, "trying to replace #{to} but its plan "\ "is #{to.plan}, expected #{self}" elsif from == to return end # Swap the subplans of +from+ and +to+ yield(from, to) if mission_task?(from) add_mission_task(to) replaced(from, to) unmark_mission_task(from) elsif permanent_task?(from) add_permanent_task(to) replaced(from, to) unmark_permanent_task(from) else add(to) replaced(from, to) end end |
#handle_replace(from, to) ⇒ Object
:nodoc:
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 |
# File 'lib/roby/plan.rb', line 777 def handle_replace(from, to) # :nodoc: handle_force_replace(from, to) do # Check that +to+ is valid in all hierarchy relations where # +from+ is a child unless to.fullfills?(*from.fullfilled_model) models = from.fullfilled_model.first missing = models.find_all do |m| !to.fullfills?(m) end if missing.empty? mismatching_argument = from.fullfilled_model.last.find do |key, expected_value| to.arguments.set?(key) && (to.arguments[key] != expected_value) end end if mismatching_argument raise InvalidReplace.new(from, to), "argument mismatch for #{mismatching_argument.first}" elsif !missing.empty? raise InvalidReplace.new(from, to), "missing provided models #{missing.map(&:name).join(', ')}" else raise InvalidReplace.new(from, to), "#{to} does not fullfill #{from}" end end # Swap the subplans of +from+ and +to+ yield(from, to) end end |
#has_free_event?(generator) ⇒ Boolean
Tests whether a free event is present in this plan
1485 1486 1487 |
# File 'lib/roby/plan.rb', line 1485 def has_free_event?(generator) free_events.include?(generator) end |
#has_task?(task) ⇒ Boolean
Tests whether a task is present in this plan
1475 1476 1477 |
# File 'lib/roby/plan.rb', line 1475 def has_task?(task) tasks.include?(task) end |
#has_task_event?(generator) ⇒ Boolean
Tests whether a task event is present in this plan
1480 1481 1482 |
# File 'lib/roby/plan.rb', line 1480 def has_task_event?(generator) task_events.include?(generator) end |
#in_transaction ⇒ Object
Creates a new transaction and yields it. Ensures that the transaction is discarded if the block returns without having committed it.
1216 1217 1218 1219 1220 |
# File 'lib/roby/plan.rb', line 1216 def in_transaction yield(trsc = Transaction.new(self)) ensure trsc.discard_transaction if trsc && !trsc.finalized? end |
#in_useful_subplan?(reference_task, tested_task) ⇒ Boolean
Tests whether a task is useful from the point of view of a reference task
It is O(N) where N is the number of edges in the combined task relation graphs. If you have to do a lot of tests with the same task, compute the set of useful tasks with #compute_useful_tasks
1999 2000 2001 2002 2003 2004 |
# File 'lib/roby/plan.rb', line 1999 def in_useful_subplan?(reference_task, tested_task) compute_useful_tasks([reference_task]) do |useful_t| return true if useful_t == tested_task end false end |
#include?(object) ⇒ Boolean
use the more specific #has_task?, #has_free_event? or #has_task_event? instead
1491 1492 1493 1494 1495 1496 1497 |
# File 'lib/roby/plan.rb', line 1491 def include?(object) Roby.warn_deprecated( "Plan#include? is deprecated, use one of the more specific "\ "#has_task? #has_task_event? and #has_free_event?" ) has_free_event?(object) || has_task_event?(object) || has_task?(object) end |
#inspect ⇒ Object
:nodoc:
237 238 239 240 |
# File 'lib/roby/plan.rb', line 237 def inspect # :nodoc: "#<#{self}: mission_tasks=#{mission_tasks} tasks=#{tasks} "\ "events=#{free_events} transactions=#{transactions}>" end |
#local_tasks ⇒ Object
1308 1309 1310 |
# File 'lib/roby/plan.rb', line 1308 def local_tasks task_index.self_owned end |
#locally_useful_roots(with_transactions: true) ⇒ Object
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 |
# File 'lib/roby/plan.rb', line 1282 def locally_useful_roots(with_transactions: true) # Create the set of tasks which must be kept as-is seeds = @task_index.mission_tasks | @task_index.permanent_tasks if with_transactions transactions.each do |trsc| seeds.merge trsc.proxy_tasks.keys.to_set end end seeds end |
#locally_useful_tasks ⇒ Object
1293 1294 1295 |
# File 'lib/roby/plan.rb', line 1293 def locally_useful_tasks compute_useful_tasks(locally_useful_roots) end |
#make_useless(tasks) ⇒ Object
Ensures that the given tasks will end up being processed without forcefully stopping anything
1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 |
# File 'lib/roby/plan.rb', line 1322 def make_useless(tasks) all_tasks = compute_useful_tasks( Array(tasks), graphs: default_useful_task_graphs.map(&:reverse) ).to_set all_tasks.compare_by_identity @task_index.mission_tasks.dup.each do |t| unmark_mission_task(t) if all_tasks.include?(t) end @task_index.permanent_tasks.dup.each do |t| unmark_permanent_task(t) if all_tasks.include?(t) end end |
#merge(plan) ⇒ Object
Merges the content of a plan into self
It is assumed that self and plan do not intersect.
Unlike #merge!, it does not update its argument, neither update the plan objects to point to self afterwards
335 336 337 338 339 340 341 342 343 344 |
# File 'lib/roby/plan.rb', line 335 def merge(plan) return if plan == self trigger_matches = find_triggers_matches(plan) merging_plan(plan) merge_base(plan) merge_relation_graphs(plan) merged_plan(plan) apply_triggers_matches(trigger_matches) end |
#merge!(plan) ⇒ Object
Moves the content of other_plan into self, and clears other_plan
It is assumed that other_plan and plan do not intersect
Unlike #merge, it ensures that all plan objects have their Roby::PlanObject#plan attribute properly updated, and it cleans plan
354 355 356 357 358 359 360 361 362 |
# File 'lib/roby/plan.rb', line 354 def merge!(plan) return if plan == self tasks = plan.tasks.dup events = plan.free_events.dup tasks.each { |t| t.plan = self } events.each { |e| e.plan = self } merge(plan) end |
#merge_base(plan) ⇒ Object
255 256 257 258 259 260 261 262 263 |
# File 'lib/roby/plan.rb', line 255 def merge_base(plan) free_events.merge(plan.free_events) mission_tasks.merge(plan.mission_tasks) tasks.merge(plan.tasks) permanent_tasks.merge(plan.permanent_tasks) permanent_events.merge(plan.permanent_events) task_index.merge(plan.task_index) task_events.merge(plan.task_events) end |
#merge_relation_graphs(plan) ⇒ Object
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/roby/plan.rb', line 265 def merge_relation_graphs(plan) # Now merge the relation graphs # # Since task_relation_graphs contains both Class<Graph>=>Graph and # Graph=>Graph, we merge only the graphs for which # self.task_relation_graphs has an entry (i.e. Class<Graph>) and # ignore the rest plan.task_relation_graphs.each do |rel_id, rel| next if rel_id == rel this_rel = task_relation_graphs.fetch(rel_id, nil) next unless this_rel this_rel.merge(rel) end plan.event_relation_graphs.each do |rel_id, rel| next if rel_id == rel this_rel = event_relation_graphs.fetch(rel_id, nil) next unless this_rel this_rel.merge(rel) end end |
#merge_transaction(transaction, merged_graphs, _added, _removed, _updated) ⇒ Object
296 297 298 299 300 301 |
# File 'lib/roby/plan.rb', line 296 def merge_transaction(transaction, merged_graphs, _added, _removed, _updated) merging_plan(transaction) merge_base(transaction) replace_relation_graphs(merged_graphs) merged_plan(transaction) end |
#merge_transaction!(transaction, merged_graphs, added, removed, updated) ⇒ Object
303 304 305 306 307 308 309 310 311 |
# File 'lib/roby/plan.rb', line 303 def merge_transaction!(transaction, merged_graphs, added, removed, updated) # NOTE: Task#plan= updates its bound events tasks = transaction.tasks.dup events = transaction.free_events.dup tasks.each { |t| t.plan = self } events.each { |e| e.plan = self } merge_transaction(transaction, merged_graphs, added, removed, updated) end |
#merged_plan(plan) ⇒ Object
Hook called when a #merge has been performed
368 |
# File 'lib/roby/plan.rb', line 368 def merged_plan(plan); end |
#merging_plan(plan) ⇒ Object
Hook called just before performing a #merge
365 |
# File 'lib/roby/plan.rb', line 365 def merging_plan(plan); end |
#mission?(task) ⇒ Boolean
use #mission_task? instead
514 515 516 517 |
# File 'lib/roby/plan.rb', line 514 def mission?(task) Roby.warn_deprecated "#mission? is deprecated, use #mission_task? instead" mission_task?(task) end |
#mission_task?(task) ⇒ Boolean
Checks if a task is part of the plan’s missions
560 561 562 |
# File 'lib/roby/plan.rb', line 560 def mission_task?(task) @task_index.mission_tasks.include?(task.to_task) end |
#mission_tasks ⇒ Object
The set of the robot’s missions
25 26 27 |
# File 'lib/roby/plan.rb', line 25 def mission_tasks @task_index.mission_tasks end |
#move_plan_service(service, new_task) ⇒ Object
Change the actual task a given plan service is representing
952 953 954 955 956 957 958 |
# File 'lib/roby/plan.rb', line 952 def move_plan_service(service, new_task) return if new_task == service.task remove_plan_service(service) service.task = new_task add_plan_service(service) end |
#normalize_add_arguments(objects) ⇒ Object
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.
Normalize an validate the arguments to #add into a list of plan objects
467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/roby/plan.rb', line 467 def normalize_add_arguments(objects) objects = [objects] unless objects.respond_to?(:each) objects.map do |o| if o.respond_to?(:as_plan) then o.as_plan elsif o.respond_to?(:to_event) then o.to_event elsif o.respond_to?(:to_task) then o.to_task else raise ArgumentError, "found #{o || 'nil'} which is neither a task nor an event" end end end |
#notify_event_status_change(event, status) ⇒ Object
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.
Perform notifications related to the status change of an event
711 712 713 |
# File 'lib/roby/plan.rb', line 711 def notify_event_status_change(event, status) log(:event_status_change, event, status) end |
#notify_task_status_change(task, status) ⇒ Object
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.
Perform notifications related to the status change of a task
701 702 703 704 705 706 |
# File 'lib/roby/plan.rb', line 701 def notify_task_status_change(task, status) if (services = plan_services[task]) services.each { |s| s.notify_task_status_change(status) } end log(:task_status_change, task, status) end |
#num_events ⇒ Object
The number of events, both free and task events
1470 1471 1472 |
# File 'lib/roby/plan.rb', line 1470 def num_events task_events.size + free_events.size end |
#num_free_events ⇒ Object
The number of events that are not task events
1465 1466 1467 |
# File 'lib/roby/plan.rb', line 1465 def num_free_events free_events.size end |
#num_tasks ⇒ Object
The number of tasks
1460 1461 1462 |
# File 'lib/roby/plan.rb', line 1460 def num_tasks tasks.size end |
#owns?(object) ⇒ Boolean
True if this plan owns the given object, i.e. if all the owners of the object are also owners of the plan.
721 722 723 |
# File 'lib/roby/plan.rb', line 721 def owns?(object) (object.owners - owners).empty? end |
#permanent?(object) ⇒ Boolean
use #permanent_task? or #permanent_event? instead
614 615 616 617 618 619 620 621 622 623 624 625 626 627 |
# File 'lib/roby/plan.rb', line 614 def permanent?(object) Roby.warn_deprecated( "#permanent? is deprecated, use either "\ "#permanent_task? or #permanent_event?" ) if object.respond_to?(:to_task) permanent_task?(object) elsif object.respond_to?(:to_event) permanent_event?(object) else raise ArgumentError, "expected a task or event and got #{object}" end end |
#permanent_event?(generator) ⇒ Boolean
True if the given event is registered as a permanent event on self
679 680 681 |
# File 'lib/roby/plan.rb', line 679 def permanent_event?(generator) @task_index.permanent_events.include?(generator) end |
#permanent_events ⇒ Object
The list of events that are kept outside GC. Do not change that set directly, use #permanent and #auto instead.
40 41 42 |
# File 'lib/roby/plan.rb', line 40 def permanent_events @task_index.permanent_events end |
#permanent_task?(task) ⇒ Boolean
True if the given task is registered as a permanent task on self
645 646 647 |
# File 'lib/roby/plan.rb', line 645 def permanent_task?(task) @task_index.permanent_tasks.include?(task) end |
#permanent_tasks ⇒ Object
The set of tasks that are kept around “just in case”
31 32 33 |
# File 'lib/roby/plan.rb', line 31 def permanent_tasks @task_index.permanent_tasks end |
#query_result_set(matcher) ⇒ Object
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.
Internal delegation from the matchers to the plan object to determine the ‘right’ query algorithm
1940 1941 1942 |
# File 'lib/roby/plan.rb', line 1940 def query_result_set(matcher) Queries::PlanQueryResult.from_plan(self, matcher) end |
#real_plan ⇒ Object
If this plan is a toplevel plan, returns self. If it is a transaction, returns the underlying plan
483 484 485 486 487 |
# File 'lib/roby/plan.rb', line 483 def real_plan ret = self ret = ret.plan while ret.respond_to?(:plan) ret end |
#recreate(task) ⇒ Object
Replace task
with a fresh copy of itself.
The new task takes the place of the old one in the plan: any relation that was going to/from task
or one of its events is removed, and the corresponding one is created, but this time involving the newly created task.
1741 1742 1743 1744 1745 |
# File 'lib/roby/plan.rb', line 1741 def recreate(task) new_task = task.create_fresh_copy replace_task(task, new_task) new_task end |
#refresh_relations ⇒ Object
132 133 134 135 |
# File 'lib/roby/plan.rb', line 132 def refresh_relations create_relations create_null_relations end |
#register_event(event) ⇒ Object
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.
Registers a task object in this plan
It is for Roby internal usage only, for the creation of template plans. Use #add.
1088 1089 1090 1091 1092 1093 1094 1095 |
# File 'lib/roby/plan.rb', line 1088 def register_event(event) event.plan = self if event.root_object? free_events << event else task_events << event end end |
#register_task(task) ⇒ Object
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.
Registers a task object in this plan
It is for Roby internal usage only, for the creation of template plans. Use #add.
1075 1076 1077 1078 1079 1080 |
# File 'lib/roby/plan.rb', line 1075 def register_task(task) task.plan = self tasks << task task_index.add(task) task_events.merge(task.each_event) end |
#registered_plan_services_for(task) ⇒ Object
Whether there are services registered for the given task
947 948 949 |
# File 'lib/roby/plan.rb', line 947 def registered_plan_services_for(task) @plan_services[task] || Set.new end |
#remote_tasks ⇒ Object
1312 1313 1314 1315 1316 1317 1318 |
# File 'lib/roby/plan.rb', line 1312 def remote_tasks if (local_tasks = task_index.self_owned) tasks - local_tasks else tasks end end |
#remove_fault_response_table(table) ⇒ void #remove_fault_response_table(table_model) ⇒ void
This method returns an undefined value.
Remove a fault response table that has been added with #use_fault_response_table
1981 1982 1983 1984 1985 1986 1987 1988 |
# File 'lib/roby/plan.rb', line 1981 def remove_fault_response_table(table_model) active_fault_response_tables.delete_if do |t| if (table_model.kind_of?(Class) && t.kind_of?(table_model)) || t == table_model t.removed! true end end end |
#remove_free_event(event, timestamp = Time.now) ⇒ Object
1639 1640 1641 1642 |
# File 'lib/roby/plan.rb', line 1639 def remove_free_event(event, = Time.now) verify_plan_object_finalization_sanity(event) remove_free_event!(event, ) end |
#remove_free_event!(event, timestamp = Time.now) ⇒ Object
1644 1645 1646 1647 1648 1649 |
# File 'lib/roby/plan.rb', line 1644 def remove_free_event!(event, = Time.now) @free_events.delete(event) @task_index.permanent_events.delete(event) finalize_event(event, ) self end |
#remove_object(object, timestamp = Time.now) ⇒ Object
use #remove_task or #remove_free_event instead
1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 |
# File 'lib/roby/plan.rb', line 1652 def remove_object(object, = Time.now) Roby.warn_deprecated( "#remove_object is deprecated, use either "\ "#remove_task or #remove_free_event" ) if has_task?(object) remove_task(object, ) elsif has_free_event?(object) remove_free_event(object, ) else raise ArgumentError, "#{object} is neither a task nor a free event of #{self}" end end |
#remove_plan_service(service) ⇒ Object
Deregisters a plan service from this plan
939 940 941 942 943 944 |
# File 'lib/roby/plan.rb', line 939 def remove_plan_service(service) return unless (set = plan_services[service.task]) set.delete(service) plan_services.delete(service.task) if set.empty? end |
#remove_task(task, timestamp = Time.now) ⇒ Object
1621 1622 1623 1624 |
# File 'lib/roby/plan.rb', line 1621 def remove_task(task, = Time.now) verify_plan_object_finalization_sanity(task) remove_task!(task, ) end |
#remove_task!(task, timestamp = Time.now) ⇒ Object
1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 |
# File 'lib/roby/plan.rb', line 1626 def remove_task!(task, = Time.now) @tasks.delete(task) @task_index.mission_tasks.delete(task) @task_index.permanent_tasks.delete(task) @task_index.remove(task) task.bound_events.each_value do |ev| @task_events.delete(ev) end finalize_task(task, ) self end |
#remove_transaction(trsc) ⇒ Object
Removes the transaction trsc
from the list of known transactions built on this plan
1227 1228 1229 |
# File 'lib/roby/plan.rb', line 1227 def remove_transaction(trsc) transactions.delete(trsc) end |
#remove_trigger(trigger) ⇒ void
This method returns an undefined value.
Removes a trigger
1209 1210 1211 1212 |
# File 'lib/roby/plan.rb', line 1209 def remove_trigger(trigger) triggers.delete(trigger) nil end |
#replace(from, to, filter: ReplacementFilter::Null.new) ⇒ Object
Replace from
by to
in the plan, in all relations in which from
and its events are /children/. It therefore replaces the subplan generated by from
(i.e. from
and all the tasks/events that can be reached by following the task and event relations) by the subplan generated by to
.
See also #replace_task
919 920 921 922 923 |
# File 'lib/roby/plan.rb', line 919 def replace(from, to, filter: ReplacementFilter::Null.new) handle_replace(from, to) do from.replace_subplan_by(to, filter: filter) end end |
#replace_relation_graphs(merged_graphs) ⇒ Object
290 291 292 293 294 |
# File 'lib/roby/plan.rb', line 290 def replace_relation_graphs(merged_graphs) merged_graphs.each do |self_g, new_g| self_g.replace(new_g) end end |
#replace_subplan(task_mappings, event_mappings, task_children: true, event_children: true) ⇒ Object
Replace subgraphs by another in the plan
It copies relations that are not within the keys in task_mappings and event_mappings to the corresponding task/events. The targets might be nil, in which case the relations involving the source will be simply ignored.
If needed, instead of providing an object as target, one can provide a resolver object which will be called with #call and the source, The resolver should be given as a second element of a pair, e.g.
source => [nil, #call]
984 985 986 987 988 989 990 991 992 993 994 995 996 |
# File 'lib/roby/plan.rb', line 984 def replace_subplan( task_mappings, event_mappings, task_children: true, event_children: true ) new_relations, removed_relations = compute_subplan_replacement(task_mappings, each_task_relation_graph, child_objects: task_children) apply_replacement_operations(new_relations, removed_relations) new_relations, removed_relations = compute_subplan_replacement(event_mappings, each_event_relation_graph, child_objects: event_children) apply_replacement_operations(new_relations, removed_relations) end |
#replace_task(from, to, filter: ReplacementFilter::Null.new) ⇒ Object
Replace the task from
by to
in all relations from
is part of (including events).
See also #replace
906 907 908 909 910 |
# File 'lib/roby/plan.rb', line 906 def replace_task(from, to, filter: ReplacementFilter::Null.new) handle_replace(from, to) do from.replace_by(to, filter: filter) end end |
#replaced(replaced_task, replacing_task) ⇒ Object
Hook called when replacing_task
has replaced replaced_task
in this plan
1059 1060 1061 1062 1063 1064 1065 1066 1067 |
# File 'lib/roby/plan.rb', line 1059 def replaced(replaced_task, replacing_task) # Make the PlanService object follow the replacement return unless (services = plan_services.delete(replaced_task)) services.each do |srv| srv.task = replacing_task (plan_services[replacing_task] ||= Set.new) << srv end end |
#replan(task) ⇒ Roby::Task
Creates a new planning pattern replacing the given task and its current planner
1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 |
# File 'lib/roby/plan.rb', line 1752 def replan(task) return task.create_fresh_copy unless task.planning_task planner = replan(task.planning_task) planned = task.create_fresh_copy planned.abstract = true planned.planned_by planner replace(task, planned) planned end |
#root_plan? ⇒ Boolean
True if this plan is root in the plan hierarchy
490 491 492 |
# File 'lib/roby/plan.rb', line 490 def root_plan? true end |
#same_plan?(other_plan, mappings) ⇒ Boolean
Compares this plan to other_plan
, mappings providing the mapping from task/Events in self
to task/events in other_plan
1893 1894 1895 |
# File 'lib/roby/plan.rb', line 1893 def same_plan?(other_plan, mappings) !find_plan_difference(other_plan, mappings) end |
#sibling_on?(peer) ⇒ Boolean
If this object is the main plan, checks if we are subscribed to the whole remote plan
55 56 57 58 59 60 |
# File 'lib/roby/plan.rb', line 55 def sibling_on?(peer) if Roby.plan == self then peer.remote_plan else super end end |
#size ⇒ Object
Count of tasks in this plan
1500 1501 1502 1503 |
# File 'lib/roby/plan.rb', line 1500 def size Roby.warn_deprecated "Plan#size is deprecated, use #num_tasks instead" @tasks.size end |
#static_garbage_collect(protected_roots: Set.new, &block) ⇒ Object
Run a garbage collection pass. This is ‘static’, as it does not care about the task’s state: it will simply remove *from the plan* any task that is not useful *in the context of the plan*.
This is mainly useful for static tests, and for transactions
Do not use it on executed plans.
1842 1843 1844 1845 1846 1847 1848 1849 |
# File 'lib/roby/plan.rb', line 1842 def static_garbage_collect(protected_roots: Set.new, &block) unneeded = unneeded_tasks(additional_useful_roots: protected_roots) if block unneeded.each(&block) else unneeded.each { |t| remove_task(t) } end end |
#task_relation_graph_for(model) ⇒ Object
Resolves a task graph object from the graph class (i.e. the graph model)
227 228 229 |
# File 'lib/roby/plan.rb', line 227 def task_relation_graph_for(model) task_relation_graphs.fetch(model) end |
#template? ⇒ Boolean
A template plan is meant to be injected in another plan
When a Roby::PlanObject is included in a template plan, adding relations to other tasks causes the plans to merge as needed. Doing the same operation with plain plans causes an error
72 73 74 |
# File 'lib/roby/plan.rb', line 72 def template? false end |
#transaction_stack ⇒ Array
Returns the set of stacked transaction
499 500 501 502 503 |
# File 'lib/roby/plan.rb', line 499 def transaction_stack plan_chain = [self] plan_chain << plan_chain.last.plan while plan_chain.last.respond_to?(:plan) plan_chain end |
#unmark_mission(task) ⇒ Object
use #unmark_mission_task instead
520 521 522 523 524 525 |
# File 'lib/roby/plan.rb', line 520 def unmark_mission(task) Roby.warn_deprecated( "#unmark_mission is deprecated, use #unmark_mission_task instead" ) unmark_mission_task(task) end |
#unmark_mission_task(task) ⇒ Object
Removes a task from the plan’s missions
It does not remove the task from the plan. In a plan that is being executed, it is done by garbage collection. In a static plan, it can either be done with #static_garbage_collect or directly by calling #remove_task or #remove_free_event
572 573 574 575 576 577 578 579 580 |
# File 'lib/roby/plan.rb', line 572 def unmark_mission_task(task) task = task.to_task return unless @task_index.mission_tasks.include?(task) @task_index.mission_tasks.delete(task) task.mission = false if task.self_owned? notify_task_status_change(task, :normal) self end |
#unmark_permanent(object) ⇒ Object
use #unmark_permanent_task or #unmark_permanent_event instead
598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
# File 'lib/roby/plan.rb', line 598 def unmark_permanent(object) Roby.warn_deprecated( "#unmark_permanent is deprecated, use either #unmark_permanent_task "\ "or #unmark_permanent_event" ) if object.respond_to?(:to_task) unmark_permanent_task(object) elsif object.respond_to?(:to_event) unmark_permanent_event(object) else raise ArgumentError, "expected a task or event and got #{object}" end end |
#unmark_permanent_event(event) ⇒ Object
Removes a task from the set of permanent tasks
This does not remove the event from the plan. In plans being executed, the removal will be done by garabage collection. In plans used as data structures, either use #static_garbage_collect or remove the event directly with #remove_task or #remove_free_event
691 692 693 694 695 696 |
# File 'lib/roby/plan.rb', line 691 def unmark_permanent_event(event) if @task_index.permanent_events.delete?(event.to_event) notify_event_status_change(event, :normal) end nil end |
#unmark_permanent_task(task) ⇒ Object
Removes a task from the set of permanent tasks
This does not remove the event from the plan. In plans being executed, the removal will be done by garabage collection. In plans used as data structures, either use #static_garbage_collect or remove the event directly with #remove_task or #remove_free_event
657 658 659 660 661 662 663 |
# File 'lib/roby/plan.rb', line 657 def unmark_permanent_task(task) if @task_index.permanent_tasks.delete?(task.to_task) notify_task_status_change(task, :normal) end nil end |
#unneeded_events ⇒ Object
The set of events that can be removed from the plan
1449 1450 1451 1452 1453 1454 1455 1456 1457 |
# File 'lib/roby/plan.rb', line 1449 def unneeded_events useful_events = self.useful_events result = (free_events - useful_events) result.delete_if do |ev| transactions.any? { |trsc| trsc.find_local_object_for_event(ev) } end result end |
#unneeded_tasks(additional_useful_roots: Set.new) ⇒ Object
1304 1305 1306 |
# File 'lib/roby/plan.rb', line 1304 def unneeded_tasks(additional_useful_roots: Set.new) tasks - useful_tasks(additional_roots: additional_useful_roots) end |
#use_fault_response_table(table_model, arguments = {}) ⇒ Coordination::FaultResponseTable, void
Enables a fault response table on this plan
1959 1960 1961 1962 1963 1964 |
# File 'lib/roby/plan.rb', line 1959 def use_fault_response_table(table_model, arguments = {}) table = table_model.new(self, arguments) table.attach_to(self) active_fault_response_tables << table table 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.
1444 1445 1446 |
# File 'lib/roby/plan.rb', line 1444 def useful_events compute_useful_free_events 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
1338 1339 1340 |
# File 'lib/roby/plan.rb', line 1338 def useful_task?(task) tasks.include?(task) && !unneeded_tasks.include?(task) end |
#useful_tasks(additional_roots: Set.new, with_transactions: true) ⇒ Object
1297 1298 1299 1300 1301 1302 |
# File 'lib/roby/plan.rb', line 1297 def useful_tasks(additional_roots: Set.new, with_transactions: true) compute_useful_tasks( locally_useful_roots(with_transactions: with_transactions) | additional_roots ) end |
#validate_graphs(graphs) ⇒ Object
Verifies that all graphs that should be acyclic are
449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/roby/plan.rb', line 449 def validate_graphs(graphs) # Make a topological sort of the graphs seen = Set.new Relations.each_graph_topologically(graphs) do |g| next if seen.include?(g) next unless g.dag? unless g.acyclic? raise Relations::CycleFoundError, "#{g.class} has cycles" end seen << g seen.merge(g.recursive_subsets) end end |
#verify_plan_object_finalization_sanity(object) ⇒ Object
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.
Perform sanity checks on a plan object that will be finalized
1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 |
# File 'lib/roby/plan.rb', line 1551 def verify_plan_object_finalization_sanity(object) unless object.root_object? raise ArgumentError, "cannot remove #{object} which is a non-root object" end return if object.plan == self if !object.plan if object.removed_at && !object.removed_at.empty? raise ArgumentError, "#{object} has already been removed from its plan\n"\ "Removed at\n #{object.removed_at.join("\n ")}" else raise ArgumentError, "#{object} has already been removed from its plan. "\ "Set PlanObject.debug_finalization_place to true to "\ "get the backtrace of where (in the code) the object "\ "got finalized" end elsif object.plan.template? raise ArgumentError, "#{object} has never been included in this plan" else raise ArgumentError, "#{object} is not in #{self}: #plan == #{object.plan}" end end |