Class: Roby::Plan
- Inherits:
-
DistributedObject
- Object
- DistributedObject
- Roby::Plan
- Extended by:
- Logger::Forward, Logger::Hierarchy
- Includes:
- DRoby::Identifiable, DRoby::V5::PlanDumper, EventLogging::Mixin, 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
readonly
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
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(objects) ⇒ Object
call-seq: plan.add(task) => plan plan.add(event) => plan plan.add([task, event, task2, ...]) => plan plan.add([t1, t2, ...]) => plan.
- #add_event_logger(logger) ⇒ Object
-
#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
tothe 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
eventhas been removed from this plan. -
#finalized_task(task) ⇒ Object
Hook called when
taskhas 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) ⇒ 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
taskwith 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
trscfrom 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
frombytoin the plan, in all relations in whichfromand 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
frombytoin all relationsfromis part of (including events). -
#replaced(replaced_task, replacing_task) ⇒ Object
Hook called when
replacing_taskhas replacedreplaced_taskin 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 inselfto 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
taskis 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 EventLogging::Mixin
#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) ⇒ 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) @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 = {} @event_logger = EventLogging::AggregateEventLogger.new @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
1786 1787 1788 |
# File 'lib/roby/plan.rb', line 1786 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
1950 1951 1952 |
# File 'lib/roby/plan.rb', line 1950 def active_fault_response_tables @active_fault_response_tables end |
#event_logger ⇒ Object (readonly)
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
186 187 188 |
# File 'lib/roby/plan.rb', line 186 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
191 192 193 |
# File 'lib/roby/plan.rb', line 191 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
180 181 182 |
# File 'lib/roby/plan.rb', line 180 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.
1775 1776 1777 |
# File 'lib/roby/plan.rb', line 1775 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
175 176 177 |
# File 'lib/roby/plan.rb', line 175 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
1542 1543 1544 1545 1546 1547 1548 1549 1550 |
# File 'lib/roby/plan.rb', line 1542 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
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 |
# File 'lib/roby/plan.rb', line 1790 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
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/roby/plan.rb', line 141 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#[]
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 |
# File 'lib/roby/plan.rb', line 1528 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).
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 1146 1147 1148 1149 |
# File 'lib/roby/plan.rb', line 1111 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_event_logger(logger) ⇒ Object
111 112 113 |
# File 'lib/roby/plan.rb', line 111 def add_event_logger(logger) @event_logger.add(logger) end |
#add_job_action(action) ⇒ Object
Add an action as a job
555 556 557 558 559 |
# File 'lib/roby/plan.rb', line 555 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
510 511 512 513 514 515 |
# File 'lib/roby/plan.rb', line 510 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.
543 544 545 546 547 548 549 550 551 552 |
# File 'lib/roby/plan.rb', line 543 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
587 588 589 590 591 592 593 594 595 596 597 598 599 |
# File 'lib/roby/plan.rb', line 587 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
672 673 674 675 676 677 678 679 680 |
# File 'lib/roby/plan.rb', line 672 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.
638 639 640 641 642 643 644 645 646 |
# File 'lib/roby/plan.rb', line 638 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
930 931 932 933 934 935 936 937 938 939 940 |
# File 'lib/roby/plan.rb', line 930 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
1199 1200 1201 1202 1203 1204 1205 1206 |
# File 'lib/roby/plan.rb', line 1199 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
1227 |
# File 'lib/roby/plan.rb', line 1227 def added_transaction(trsc); end |
#apply_replacement_operations(new_relations, removed_relations) ⇒ Object
1053 1054 1055 1056 1057 1058 1059 1060 |
# File 'lib/roby/plan.rb', line 1053 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
323 324 325 326 327 328 329 |
# File 'lib/roby/plan.rb', line 323 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
1819 1820 1821 |
# File 'lib/roby/plan.rb', line 1819 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
1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 |
# File 'lib/roby/plan.rb', line 1827 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
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 1708 1709 1710 1711 |
# File 'lib/roby/plan.rb', line 1682 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
1672 1673 1674 1675 1676 1677 1678 1679 |
# File 'lib/roby/plan.rb', line 1672 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.
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 1048 1049 1050 1051 |
# File 'lib/roby/plan.rb', line 1003 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
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 1441 1442 1443 1444 |
# File 'lib/roby/plan.rb', line 1381 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
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 1281 1282 1283 1284 |
# File 'lib/roby/plan.rb', line 1251 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
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/roby/plan.rb', line 432 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
1619 1620 1621 1622 1623 |
# File 'lib/roby/plan.rb', line 1619 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
255 256 257 |
# File 'lib/roby/plan.rb', line 255 def copy_to(copy) copy.merge(self) end |
#create_null_relations ⇒ Object
115 116 117 118 119 120 121 122 |
# File 'lib/roby/plan.rb', line 115 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
124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/roby/plan.rb', line 124 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
158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/roby/plan.rb', line 158 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
374 375 376 377 378 |
# File 'lib/roby/plan.rb', line 374 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+
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 427 428 429 430 |
# File 'lib/roby/plan.rb', line 388 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
1239 1240 1241 |
# File 'lib/roby/plan.rb', line 1239 def default_useful_task_graphs each_task_relation_graph.find_all { |g| g.root_relation? && !g.weak? } end |
#dup ⇒ Object
235 236 237 238 239 |
# File 'lib/roby/plan.rb', line 235 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
205 206 207 208 209 210 211 |
# File 'lib/roby/plan.rb', line 205 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.
2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 |
# File 'lib/roby/plan.rb', line 2019 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
194 195 196 197 198 199 |
# File 'lib/roby/plan.rb', line 194 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
1517 1518 1519 1520 1521 |
# File 'lib/roby/plan.rb', line 1517 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
222 223 224 225 226 227 228 |
# File 'lib/roby/plan.rb', line 222 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
719 720 721 |
# File 'lib/roby/plan.rb', line 719 def edit yield if block_given? end |
#empty? ⇒ Boolean
Returns true if there is no task in this plan
1510 1511 1512 |
# File 'lib/roby/plan.rb', line 1510 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)
214 215 216 |
# File 'lib/roby/plan.rb', line 214 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
250 251 252 |
# File 'lib/roby/plan.rb', line 250 def execute yield end |
#finalize_event(event, timestamp = nil) ⇒ Object
1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 |
# File 'lib/roby/plan.rb', line 1605 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
1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 |
# File 'lib/roby/plan.rb', line 1582 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
1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 |
# File 'lib/roby/plan.rb', line 1726 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
1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 |
# File 'lib/roby/plan.rb', line 1714 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
965 966 967 |
# File 'lib/roby/plan.rb', line 965 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.
1934 1935 1936 1937 1938 |
# File 'lib/roby/plan.rb', line 1934 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
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 1890 1891 1892 1893 |
# File 'lib/roby/plan.rb', line 1857 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.
971 972 973 |
# File 'lib/roby/plan.rb', line 971 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.
1922 1923 1924 1925 1926 |
# File 'lib/roby/plan.rb', line 1922 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
317 318 319 320 321 |
# File 'lib/roby/plan.rb', line 317 def find_triggers_matches(plan) triggers.map do |tr| [tr, tr.each(plan).to_a] end end |
#force_replace(from, to) ⇒ Object
735 736 737 738 739 |
# File 'lib/roby/plan.rb', line 735 def force_replace(from, to) handle_force_replace(from, to) do from.replace_subplan_by(to) end end |
#force_replace_task(from, to) ⇒ Object
729 730 731 732 733 |
# File 'lib/roby/plan.rb', line 729 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
1810 1811 1812 1813 1814 1815 1816 1817 |
# File 'lib/roby/plan.rb', line 1810 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
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 776 777 778 779 |
# File 'lib/roby/plan.rb', line 741 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:
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 810 811 812 813 |
# File 'lib/roby/plan.rb', line 781 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
1489 1490 1491 |
# File 'lib/roby/plan.rb', line 1489 def has_free_event?(generator) free_events.include?(generator) end |
#has_task?(task) ⇒ Boolean
Tests whether a task is present in this plan
1479 1480 1481 |
# File 'lib/roby/plan.rb', line 1479 def has_task?(task) tasks.include?(task) end |
#has_task_event?(generator) ⇒ Boolean
Tests whether a task event is present in this plan
1484 1485 1486 |
# File 'lib/roby/plan.rb', line 1484 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.
1220 1221 1222 1223 1224 |
# File 'lib/roby/plan.rb', line 1220 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
2003 2004 2005 2006 2007 2008 |
# File 'lib/roby/plan.rb', line 2003 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
1495 1496 1497 1498 1499 1500 1501 |
# File 'lib/roby/plan.rb', line 1495 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:
241 242 243 244 |
# File 'lib/roby/plan.rb', line 241 def inspect # :nodoc: "#<#{self}: mission_tasks=#{mission_tasks} tasks=#{tasks} "\ "events=#{free_events} transactions=#{transactions}>" end |
#local_tasks ⇒ Object
1312 1313 1314 |
# File 'lib/roby/plan.rb', line 1312 def local_tasks task_index.self_owned end |
#locally_useful_roots(with_transactions: true) ⇒ Object
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 |
# File 'lib/roby/plan.rb', line 1286 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
1297 1298 1299 |
# File 'lib/roby/plan.rb', line 1297 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
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 |
# File 'lib/roby/plan.rb', line 1326 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
339 340 341 342 343 344 345 346 347 348 |
# File 'lib/roby/plan.rb', line 339 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
358 359 360 361 362 363 364 365 366 |
# File 'lib/roby/plan.rb', line 358 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
259 260 261 262 263 264 265 266 267 |
# File 'lib/roby/plan.rb', line 259 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
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/roby/plan.rb', line 269 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
300 301 302 303 304 305 |
# File 'lib/roby/plan.rb', line 300 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
307 308 309 310 311 312 313 314 315 |
# File 'lib/roby/plan.rb', line 307 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
372 |
# File 'lib/roby/plan.rb', line 372 def merged_plan(plan); end |
#merging_plan(plan) ⇒ Object
Hook called just before performing a #merge
369 |
# File 'lib/roby/plan.rb', line 369 def merging_plan(plan); end |
#mission?(task) ⇒ Boolean
use #mission_task? instead
518 519 520 521 |
# File 'lib/roby/plan.rb', line 518 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
564 565 566 |
# File 'lib/roby/plan.rb', line 564 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
956 957 958 959 960 961 962 |
# File 'lib/roby/plan.rb', line 956 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
471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/roby/plan.rb', line 471 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
715 716 717 |
# File 'lib/roby/plan.rb', line 715 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
705 706 707 708 709 710 |
# File 'lib/roby/plan.rb', line 705 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
1474 1475 1476 |
# File 'lib/roby/plan.rb', line 1474 def num_events task_events.size + free_events.size end |
#num_free_events ⇒ Object
The number of events that are not task events
1469 1470 1471 |
# File 'lib/roby/plan.rb', line 1469 def num_free_events free_events.size end |
#num_tasks ⇒ Object
The number of tasks
1464 1465 1466 |
# File 'lib/roby/plan.rb', line 1464 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.
725 726 727 |
# File 'lib/roby/plan.rb', line 725 def owns?(object) (object.owners - owners).empty? end |
#permanent?(object) ⇒ Boolean
use #permanent_task? or #permanent_event? instead
618 619 620 621 622 623 624 625 626 627 628 629 630 631 |
# File 'lib/roby/plan.rb', line 618 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
683 684 685 |
# File 'lib/roby/plan.rb', line 683 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
649 650 651 |
# File 'lib/roby/plan.rb', line 649 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
1944 1945 1946 |
# File 'lib/roby/plan.rb', line 1944 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
487 488 489 490 491 |
# File 'lib/roby/plan.rb', line 487 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.
1745 1746 1747 1748 1749 |
# File 'lib/roby/plan.rb', line 1745 def recreate(task) new_task = task.create_fresh_copy replace_task(task, new_task) new_task end |
#refresh_relations ⇒ Object
136 137 138 139 |
# File 'lib/roby/plan.rb', line 136 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.
1092 1093 1094 1095 1096 1097 1098 1099 |
# File 'lib/roby/plan.rb', line 1092 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.
1079 1080 1081 1082 1083 1084 |
# File 'lib/roby/plan.rb', line 1079 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
951 952 953 |
# File 'lib/roby/plan.rb', line 951 def registered_plan_services_for(task) @plan_services[task] || Set.new end |
#remote_tasks ⇒ Object
1316 1317 1318 1319 1320 1321 1322 |
# File 'lib/roby/plan.rb', line 1316 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
1985 1986 1987 1988 1989 1990 1991 1992 |
# File 'lib/roby/plan.rb', line 1985 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
1643 1644 1645 1646 |
# File 'lib/roby/plan.rb', line 1643 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
1648 1649 1650 1651 1652 1653 |
# File 'lib/roby/plan.rb', line 1648 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
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 |
# File 'lib/roby/plan.rb', line 1656 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
943 944 945 946 947 948 |
# File 'lib/roby/plan.rb', line 943 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
1625 1626 1627 1628 |
# File 'lib/roby/plan.rb', line 1625 def remove_task(task, = Time.now) verify_plan_object_finalization_sanity(task) remove_task!(task, ) end |
#remove_task!(task, timestamp = Time.now) ⇒ Object
1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 |
# File 'lib/roby/plan.rb', line 1630 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
1231 1232 1233 |
# File 'lib/roby/plan.rb', line 1231 def remove_transaction(trsc) transactions.delete(trsc) end |
#remove_trigger(trigger) ⇒ void
This method returns an undefined value.
Removes a trigger
1213 1214 1215 1216 |
# File 'lib/roby/plan.rb', line 1213 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
923 924 925 926 927 |
# File 'lib/roby/plan.rb', line 923 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
294 295 296 297 298 |
# File 'lib/roby/plan.rb', line 294 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]
988 989 990 991 992 993 994 995 996 997 998 999 1000 |
# File 'lib/roby/plan.rb', line 988 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
910 911 912 913 914 |
# File 'lib/roby/plan.rb', line 910 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
1063 1064 1065 1066 1067 1068 1069 1070 1071 |
# File 'lib/roby/plan.rb', line 1063 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
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 |
# File 'lib/roby/plan.rb', line 1756 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
494 495 496 |
# File 'lib/roby/plan.rb', line 494 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
1897 1898 1899 |
# File 'lib/roby/plan.rb', line 1897 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
1504 1505 1506 1507 |
# File 'lib/roby/plan.rb', line 1504 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.
1846 1847 1848 1849 1850 1851 1852 1853 |
# File 'lib/roby/plan.rb', line 1846 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)
231 232 233 |
# File 'lib/roby/plan.rb', line 231 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
503 504 505 506 507 |
# File 'lib/roby/plan.rb', line 503 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
524 525 526 527 528 529 |
# File 'lib/roby/plan.rb', line 524 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
576 577 578 579 580 581 582 583 584 |
# File 'lib/roby/plan.rb', line 576 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
602 603 604 605 606 607 608 609 610 611 612 613 614 615 |
# File 'lib/roby/plan.rb', line 602 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
695 696 697 698 699 700 |
# File 'lib/roby/plan.rb', line 695 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
661 662 663 664 665 666 667 |
# File 'lib/roby/plan.rb', line 661 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
1453 1454 1455 1456 1457 1458 1459 1460 1461 |
# File 'lib/roby/plan.rb', line 1453 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
1308 1309 1310 |
# File 'lib/roby/plan.rb', line 1308 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
1963 1964 1965 1966 1967 1968 |
# File 'lib/roby/plan.rb', line 1963 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.
1448 1449 1450 |
# File 'lib/roby/plan.rb', line 1448 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
1342 1343 1344 |
# File 'lib/roby/plan.rb', line 1342 def useful_task?(task) tasks.include?(task) && !unneeded_tasks.include?(task) end |
#useful_tasks(additional_roots: Set.new, with_transactions: true) ⇒ Object
1301 1302 1303 1304 1305 1306 |
# File 'lib/roby/plan.rb', line 1301 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
453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
# File 'lib/roby/plan.rb', line 453 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
1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 |
# File 'lib/roby/plan.rb', line 1555 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 |