class Roby::Plan

A plan object manages a collection of tasks and events.

Attributes

structure_checks[R]

A set of structure checking procedures that must be performed on all plans

@yieldparam [Plan] the plan @yieldreturn [Array<(to_execution_exception,Array<Task>)>] a list

of exceptions, and the tasks toward which these exceptions
should be propagated. If the list of tasks is nil, all parents
of the exception's origin will be selected
active_fault_response_tables[R]

The list of fault response tables that are currently globally active on this plan

event_logger[RW]

The event logger

event_relation_graphs[R]

The graphs that make event relations, formatted as required by {Relations::DirectedRelationSupport#relation_graphs}

@see each_event_relation_graph

free_events[R]

The list of events that are not included in a task

graph_observer[R]

The observer object that reacts to relation changes

local_owner[RW]

The Peer ID of the local owner (i.e. of the local process / execution engine)

mission_tasks[R]

The set of the robot's missions @see add_mission_task unmark_mission_task

permanent_events[R]

The list of events that are kept outside GC. Do not change that set directly, use permanent and auto instead.

permanent_tasks[R]

The set of tasks that are kept around “just in case” @see add_permanent_task unmark_permanent_task

plan_services[R]

The set of PlanService instances that are defined on this plan

structure_checks[R]

The set of blocks that should be called to check the structure of the plan.

@yieldparam [Plan] the plan @yieldreturn [Array<(to_execution_exception,Array<Task>)>] a list

of exceptions, and the tasks toward which these exceptions
should be propagated. If the list of tasks is nil, all parents
of the exception's origin will be selected
task_events[R]

The set of events that are defined by tasks

task_index[R]

The task index for this plan. This is a {Queries::Index} object which allows efficient resolving of queries.

task_relation_graphs[R]

The graphs that make task relations, formatted as required by {Relations::DirectedRelationSupport#relation_graphs}

@see each_task_relation_graph

tasks[R]

The list of tasks that are included in this plan

transactions[R]

The set of transactions which are built on top of this plan

triggers[R]

A set of pair of task matching objects and blocks defining this plan's triggers

See {#add_trigger}

Public Class Methods

can_gc?(task) click to toggle source
# File lib/roby/plan.rb, line 1309
def self.can_gc?(task)
    if task.starting? then true # wait for the task to be started before deciding ...
    elsif task.running? && !task.finishing?
        task.event(:stop).controlable?
    else true
    end
end
check_failed_missions(plan) click to toggle source

Get all missions that have failed

# File lib/roby/plan.rb, line 1548
def self.check_failed_missions(plan)
    result = Array.new
    for task in plan.mission_tasks
        result << MissionFailedError.new(task) if task.failed?
    end
    for task in plan.permanent_tasks
        result << PermanentTaskError.new(task) if task.failed?
    end
    result
end
instanciate_relation_graphs(graph_observer: nil) click to toggle source
# File lib/roby/plan.rb, line 111
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
    return task_relation_graphs, event_relation_graphs
end
new(graph_observer: nil, event_logger: DRoby::NullEventLogger.new) click to toggle source
Calls superclass method
# File lib/roby/plan.rb, line 70
def initialize(graph_observer: nil, event_logger: DRoby::NullEventLogger.new)
    @local_owner = DRoby::PeerID.new('local')

    @mission_tasks    = Set.new
    @permanent_tasks  = Set.new
    @permanent_events = Set.new
    @tasks = Set.new
    @free_events = Set.new
    @task_events = Set.new
    @transactions = Set.new
    @fault_response_tables = Array.new
    @triggers = []

    @plan_services = Hash.new

    self.event_logger = event_logger
    @active_fault_response_tables = Array.new
    @task_index  = Roby::Queries::Index.new

    @graph_observer = graph_observer
    create_relations

    super()
end

Public Instance Methods

[](object, create = true) click to toggle source

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#[]

# File lib/roby/plan.rb, line 1296
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(task) → plan click to toggle source
add(event) → plan
add([task, event, task2, ...]) → 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).

# File lib/roby/plan.rb, line 923
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_mission(task) click to toggle source

@deprecated use {#add_mission_task} instead

# File lib/roby/plan.rb, line 469
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) click to toggle source

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.

@see mission_task? unmark_mission_task

# File lib/roby/plan.rb, line 503
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) click to toggle source

@deprecated use {#add_permanent_task} or {#add_permanent_event} instead

# File lib/roby/plan.rb, line 538
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) click to toggle source

Mark an event as permanent, optionally adding to the plan

Permanent events are protected against garbage collection

# File lib/roby/plan.rb, line 609
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) click to toggle source

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 {PermanentTaskError}. Unlike missions, this exception is non-fatal.

# File lib/roby/plan.rb, line 578
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) click to toggle source

Register a new plan service on this plan

# File lib/roby/plan.rb, line 757
def add_plan_service(service)
    if service.task.plan != self
        raise "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)
    if !set.include?(service)
        set << service
    end
    self
end
add_trigger(query_object, &block) click to toggle source

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

@param [#===] query_object the object against which tasks are tested.

Tasks for which #=== returns true are yield to the block

@yieldparam [Roby::Task] task the task that matched the query object @return [Object] an ID object that can be used in {#remove_trigger}

# File lib/roby/plan.rb, line 1001
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) click to toggle source

Hook called when a new transaction has been built on top of this plan

# File lib/roby/plan.rb, line 1032
def added_transaction(trsc)
end
apply_replacement_operations(new_relations, removed_relations) click to toggle source
# File lib/roby/plan.rb, line 865
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) click to toggle source
# File lib/roby/plan.rb, line 280
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) click to toggle source
# File lib/roby/plan.rb, line 1581
def call_structure_check_handler(handler)
    handler.call(self)
end
check_structure() click to toggle source

Perform the structure checking step by calling the procs registered in {#structure_checks} and {Plan.structure_checks}

@return [Hash<ExecutionException,Array<Roby::Task>,nil>

# File lib/roby/plan.rb, line 1589
def check_structure
    # Do structure checking and gather the raised exceptions
    exceptions = Hash.new
    for prc in (Plan.structure_checks + structure_checks)
        new_exceptions = call_structure_check_handler(prc)
        next unless new_exceptions

        format_exception_set(exceptions, new_exceptions)
    end
    exceptions
end
clear() click to toggle source

Remove all tasks

# File lib/roby/plan.rb, line 1444
def clear
    tasks, @tasks = @tasks, Set.new
    free_events, @free_events = @free_events, Set.new

    clear!

    remaining = tasks.find_all do |t|
        if executable? && t.running?
            true
        else
            finalize_task(t)
            false
        end
    end
    if !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!() click to toggle source
# File lib/roby/plan.rb, line 1427
def clear!
    each_task_relation_graph do |g|
        g.clear
    end
    each_event_relation_graph do |g|
        g.clear
    end
    @free_events.clear
    @mission_tasks.clear
    @tasks.clear
    @permanent_tasks.clear
    @permanent_events.clear
    @task_index.clear
    @task_events.clear
end
compute_subplan_replacement(mappings, relation_graphs, child_objects: true) click to toggle source
# File lib/roby/plan.rb, line 826
def compute_subplan_replacement(mappings, relation_graphs, child_objects: true)
    new_relations, removed_relations = Array.new, Array.new
    relation_graphs.each do |graph|
        next if graph.strong?

        resolved_mappings = Hash.new
        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.has_key?(parent)
                if !graph.copy_on_replace?
                    removed_relations << [graph, parent, obj]
                end
                if !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 if !child_objects
            graph.each_out_neighbour(obj) do |child|
                next if mappings.has_key?(child)
                if !graph.copy_on_replace?
                    removed_relations << [graph, obj, child]
                end
                if !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
    return new_relations, removed_relations
end
compute_useful_free_events() click to toggle source

@api private

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

@return [Set<EventGenerator>]

# File lib/roby/plan.rb, line 1170
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
    while !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 = Array.new
        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 = Array.new
                seeds.each do |vertex|
                    if !visitor.finished_vertex?(vertex) && graph.has_vertex?(vertex)
                        graph.depth_first_visit(vertex, visitor) { |v| new_seeds << v }
                    end
                end
                if !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)
        if visitors.any? { |_, v, _| v.useful? }
            result.merge(component)
        end
    end

    result
end
compute_useful_tasks(seeds, graphs: default_useful_task_graphs) { |v| ... } click to toggle source

@api private

Compute the subplan that is useful for a given set of tasks

@param [Set<Roby::Task>] seeds the root “useful” tasks @param [Array<Relations::BidirectionalDirectedAdjancencyGraph>] graphs the

graphs through which "usefulness" is propagated
# File lib/roby/plan.rb, line 1056
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_pending_seeds = true
    while has_pending_seeds
        has_pending_seeds = false
        visitors.each do |graph, visitor, seeds|
            next if seeds.empty?

            new_seeds = Array.new
            seeds.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_seeds << v
                    end
                end
            end
            if !new_seeds.empty?
                has_pending_seeds = true
                result.merge(new_seeds)
                visitors.each { |g, _, s| s.merge(new_seeds) if g != graph }
            end
            seeds.clear
        end
    end

    result
end
copy_relation_graphs_to(copy, mappings) click to toggle source
# File lib/roby/plan.rb, line 386
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_to(copy) click to toggle source

@deprecated use {#merge} instead

# File lib/roby/plan.rb, line 219
def copy_to(copy)
    copy.merge(self)
end
create_relations() click to toggle source
# File lib/roby/plan.rb, line 95
def create_relations
    @task_relation_graphs, @event_relation_graphs =
        self.class.instanciate_relation_graphs(graph_observer: graph_observer)

    @structure_checks = Array.new
    each_relation_graph do |graph|
        if graph.respond_to?(:check_structure)
            structure_checks << graph.method(:check_structure)
        end
    end
end
dedupe(source) click to toggle source
# File lib/roby/plan.rb, line 126
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() click to toggle source
# File lib/roby/plan.rb, line 332
def deep_copy
    plan = Roby::Plan.new
    mappings = deep_copy_to(plan)
    return plan, mappings
end
deep_copy_to(copy) click to toggle source

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+
# File lib/roby/plan.rb, line 346
def deep_copy_to(copy)
    mappings = Hash.new do |h, k|
        if !self.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() click to toggle source

@api private

Default set of graphs that should be discovered by {#compute_useful_tasks}

# File lib/roby/plan.rb, line 1045
def default_useful_task_graphs
    each_task_relation_graph.find_all { |g| g.root_relation? && !g.weak? }
end
dup() click to toggle source
# File lib/roby/plan.rb, line 200
def dup
    new_plan = Plan.new
    copy_to(new_plan)
    new_plan
end
each_event_relation_graph() { |v| ... } click to toggle source

Enumerate the graph objects that contain this plan's event relation information

@yieldparam [Relations::EventRelationGraph] graph

# File lib/roby/plan.rb, line 172
def each_event_relation_graph
    return enum_for(__method__) if !block_given?
    event_relation_graphs.each do |k, v|
        yield(v) if k == v
    end
end
each_object_in_transaction_stack(object) { |current_plan, object| ... } click to toggle source

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.

@param [PlanObject] object @yieldparam [PlanObject] object the object's identity at the

given level of the stack. Note that the last element is guaranteed
to not be a transaction proxy.
# File lib/roby/plan.rb, line 1819
def each_object_in_transaction_stack(object)
    return enum_for(__method__, object) if !block_given?
    current_plan = self
    while true
        yield(current_plan, object)

        return if !object.transaction_proxy?
        current_plan = current_plan.plan
        object = object.__getobj__
    end
    nil
end
each_relation_graph(&block) click to toggle source

Enumerate the graph objects that contain this plan's relation information

@yieldparam [Relations::Graph] graph

# File lib/roby/plan.rb, line 155
def each_relation_graph(&block)
    return enum_for(__method__) if !block_given?
    each_event_relation_graph(&block)
    each_task_relation_graph(&block)
end
each_task() { |t| ... } click to toggle source

Iterates on all tasks

@yieldparam [Task] task

# File lib/roby/plan.rb, line 1286
def each_task
    return enum_for(__method__) if !block_given?
    @tasks.each { |t| yield(t) }
end
each_task_relation_graph() { |v| ... } click to toggle source

Enumerate the graph objects that contain this plan's task relation information

@yieldparam [Relations::TaskRelationGraph] graph

# File lib/roby/plan.rb, line 188
def each_task_relation_graph
    return enum_for(__method__) if !block_given?
    task_relation_graphs.each do |k, v|
        yield(v) if k == v
    end
end
edit() { || ... } click to toggle source
# File lib/roby/plan.rb, line 654
def edit
    if block_given?
        yield
    end
end
empty?() click to toggle source

Returns true if there is no task in this plan

# File lib/roby/plan.rb, line 1282
def empty?; @tasks.empty? && @free_events.empty? end
event_relation_graph_for(model) click to toggle source

Resolves an event graph object from the graph class (i.e. the graph model)

# File lib/roby/plan.rb, line 180
def event_relation_graph_for(model)
    event_relation_graphs.fetch(model)
end
executable?() click to toggle source

Check that this is an executable plan. This is always true for plain Plan objects and false for transcations

# File lib/roby/plan.rb, line 62
def executable?; false end
execute() { || ... } click to toggle source

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

# File lib/roby/plan.rb, line 214
def execute(&block)
    yield
end
finalize_event(event, timestamp = nil) click to toggle source
# File lib/roby/plan.rb, line 1367
def finalize_event(event, timestamp = nil)
    verify_plan_object_finalization_sanity(event)
    if (event.plan != self) && has_free_event?(event)
        raise ArgumentError, "#{event} is included in #{self} but #plan == #{event.plan}"
    end

    # 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!(timestamp)
end
finalize_task(task, timestamp = nil) click to toggle source
# File lib/roby/plan.rb, line 1340
def finalize_task(task, timestamp = nil)
    verify_plan_object_finalization_sanity(task)
    if (task.plan != self) && has_task?(task)
        raise ArgumentError, "#{task} is included in #{self} but #plan == #{task.plan}"
    end

    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

    for ev in task.bound_events.each_value
        finalized_event(ev)
    end
    finalized_task(task)

    for ev in task.bound_events.each_value
        ev.finalized!(timestamp)
    end
    task.finalized!(timestamp)
end
finalized_event(event) click to toggle source

Hook called when event has been removed from this plan

# File lib/roby/plan.rb, line 1483
def finalized_event(event)
    log(:finalized_event, droby_id, event)
    return unless event.root_object?
    for trsc in transactions
        next unless trsc.proxying?
        if proxy = trsc.find_local_object_for_event(event)
            trsc.finalized_plan_event(proxy)
        end
    end
end
finalized_task(task) click to toggle source

Hook called when task has been removed from this plan

# File lib/roby/plan.rb, line 1472
def finalized_task(task)
    for trsc in transactions
        next unless trsc.proxying?
        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) click to toggle source

Find all the defined plan services for a given task

# File lib/roby/plan.rb, line 789
def find_all_plan_services(task)
    plan_services[task] || Array.new
end
find_local_tasks(*args, &block) click to toggle source

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.

# File lib/roby/plan.rb, line 1700
def find_local_tasks(*args, &block)
    query = find_tasks(*args, &block)
    query.local_scope
    query
end
find_plan_difference(other_plan, mappings) click to toggle source

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

# File lib/roby/plan.rb, line 1622
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|
        if !mappings.has_key?(obj)
            return [:new_object, obj]
        end
        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) click to toggle source

If at least one plan service is defined for task, returns one of them. Otherwise, returns nil.

# File lib/roby/plan.rb, line 795
def find_plan_service(task)
    if set = plan_services[task]
        set.find { true }
    end
end
find_tasks(model = nil, args = nil) click to toggle source

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.

# File lib/roby/plan.rb, line 1686
def find_tasks(model = nil, args = nil)
    q = Queries::Query.new(self)
    if model || args
        q.which_fullfills(model, args)
    end
    q
end
find_triggers_matches(plan) click to toggle source
# File lib/roby/plan.rb, line 274
def find_triggers_matches(plan)
    triggers.map do |tr|
        [tr, tr.each(plan).to_a]
    end
end
force_replace(from, to) click to toggle source
# File lib/roby/plan.rb, line 672
def force_replace(from, to)
    handle_force_replace(from, to) do
        from.replace_subplan_by(to)
    end
end
force_replace_task(from, to) click to toggle source
# File lib/roby/plan.rb, line 666
def force_replace_task(from, to)
    handle_force_replace(from, to) do
        from.replace_by(to)
    end
end
format_exception_set(result, new) click to toggle source

@api 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

@param [Hash] result @param [Array,Hash] new

# File lib/roby/plan.rb, line 1568
def format_exception_set(result, new)
    [*new].each do |error, tasks|
        roby_exception = error.to_execution_exception
        if !tasks
            if error.kind_of?(RelationFailedError)
                tasks = [error.parent]
            end
        end
        result[roby_exception] = tasks
    end
    result
end
handle_force_replace(from, to) { |from, to| ... } click to toggle source
# File lib/roby/plan.rb, line 678
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
has_free_event?(generator) click to toggle source

Tests whether a free event is present in this plan

# File lib/roby/plan.rb, line 1265
def has_free_event?(generator)
    free_events.include?(generator)
end
has_task?(task) click to toggle source

Tests whether a task is present in the plan

# File lib/roby/plan.rb, line 487
def has_task?(task)
    tasks.include?(task)
end
has_task_event?(generator) click to toggle source

Tests whether a task event is present in this plan

# File lib/roby/plan.rb, line 1260
def has_task_event?(generator)
    task_events.include?(generator)
end
in_transaction() { |trsc = transaction| ... } click to toggle source

Creates a new transaction and yields it. Ensures that the transaction is discarded if the block returns without having committed it.

# File lib/roby/plan.rb, line 1022
def in_transaction
    yield(trsc = Transaction.new(self))

ensure
    if trsc && !trsc.finalized?
        trsc.discard_transaction
    end
end
in_useful_subplan?(reference_task, task) click to toggle source

Tests whether a task is useful for another one 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 {Plan#compute_useful_tasks}

@param reference_task the reference task @param task the task whose usefulness is being tested @return [Boolean]

# File lib/roby/plan.rb, line 1801
def in_useful_subplan?(reference_task, task)
    compute_useful_tasks([task]) do |useful_t|
        if useful_t == self
            return true
        end
    end
    return false
end
include?(object) click to toggle source

@deprecated use the more specific {#has_task?}, {#has_free_event?} or

{#has_task_event?} instead
# File lib/roby/plan.rb, line 1271
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
local_tasks() click to toggle source
# File lib/roby/plan.rb, line 1114
def local_tasks
    task_index.self_owned
end
locally_useful_roots(with_transactions: true) click to toggle source
# File lib/roby/plan.rb, line 1091
def locally_useful_roots(with_transactions: true)
    # Create the set of tasks which must be kept as-is
    seeds = @mission_tasks | @permanent_tasks
    if with_transactions
        for trsc in transactions
            seeds.merge trsc.proxy_tasks.keys.to_set
        end
    end
    seeds
end
locally_useful_tasks() click to toggle source
# File lib/roby/plan.rb, line 1102
def locally_useful_tasks
    compute_useful_tasks(locally_useful_roots)
end
merge(plan) click to toggle source

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

@param [Roby::Plan] plan the plan to merge into self

# File lib/roby/plan.rb, line 296
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) click to toggle source

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 {PlanObject#plan} attribute properly updated, and it cleans plan

@param [Roby::Plan] plan the plan to merge into self

# File lib/roby/plan.rb, line 315
def merge!(plan)
    return if plan == self

    tasks, events = plan.tasks.dup, plan.free_events.dup
    tasks.each { |t| t.plan = self }
    events.each { |e| e.plan = self }
    merge(plan)
end
merge_base(plan) click to toggle source
# File lib/roby/plan.rb, line 223
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) click to toggle source
# File lib/roby/plan.rb, line 233
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
        next if !(this_rel = task_relation_graphs.fetch(rel_id, nil))
        this_rel.merge(rel)
    end
    plan.event_relation_graphs.each do |rel_id, rel|
        next if rel_id == rel
        next if !(this_rel = event_relation_graphs.fetch(rel_id, nil))
        this_rel.merge(rel)
    end
end
merge_transaction(transaction, merged_graphs, added, removed, updated) click to toggle source
# File lib/roby/plan.rb, line 258
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) click to toggle source
# File lib/roby/plan.rb, line 265
def merge_transaction!(transaction, merged_graphs, added, removed, updated)
    # Note: Task#plan= updates its bound events
    tasks, events = transaction.tasks.dup, 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) click to toggle source

Hook called when a {#merge} has been performed

# File lib/roby/plan.rb, line 329
def merged_plan(plan)
end
merging_plan(plan) click to toggle source

Hook called just before performing a {#merge}

# File lib/roby/plan.rb, line 325
def merging_plan(plan)
end
mission?(task) click to toggle source

@deprecated use {#mission_task?} instead

# File lib/roby/plan.rb, line 475
def mission?(task)
    Roby.warn_deprecated "#mission? is deprecated, use #mission_task? instead"
    mission_task?(task)
end
mission_task?(task) click to toggle source

Checks if a task is part of the plan's missions

@see add_mission_task unmark_mission_task

# File lib/roby/plan.rb, line 516
def mission_task?(task)
    @mission_tasks.include?(task.to_task)
end
move_plan_service(service, new_task) click to toggle source

Change the actual task a given plan service is representing

# File lib/roby/plan.rb, line 780
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) click to toggle source

@api private

Normalize an validate the arguments to {#add} into a list of plan objects

# File lib/roby/plan.rb, line 426
def normalize_add_arguments(objects)
    if !objects.respond_to?(:each)
        objects = [objects]
    end

    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) click to toggle source

@api private

Perform notifications related to the status change of an event

# File lib/roby/plan.rb, line 650
def notify_event_status_change(event, status)
    log(:event_status_change, event, status)
end
notify_task_status_change(task, status) click to toggle source

@api private

Perform notifications related to the status change of a task

# File lib/roby/plan.rb, line 640
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() click to toggle source

The number of events, both free and task events

# File lib/roby/plan.rb, line 1250
def num_events
    task_events.size + free_events.size
end
num_free_events() click to toggle source

The number of events that are not task events

# File lib/roby/plan.rb, line 1245
def num_free_events
    free_events.size
end
num_tasks() click to toggle source

The number of tasks

# File lib/roby/plan.rb, line 1240
def num_tasks
    tasks.size
end
owns?(object) click to toggle source

True if this plan owns the given object, i.e. if all the owners of the object are also owners of the plan.

# File lib/roby/plan.rb, line 662
def owns?(object)
    (object.owners - owners).empty?
end
permanent?(object) click to toggle source

@deprecated use {#permanent_task?} or {#permanent_event?} instead

# File lib/roby/plan.rb, line 562
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) click to toggle source

True if the given event is registered as a permanent event on self

# File lib/roby/plan.rb, line 619
def permanent_event?(generator)
    @permanent_events.include?(generator) 
end
permanent_task?(task) click to toggle source

True if the given task is registered as a permanent task on self

# File lib/roby/plan.rb, line 588
def permanent_task?(task)
    @permanent_tasks.include?(task)
end
quarantined_tasks() click to toggle source
# File lib/roby/plan.rb, line 1118
def quarantined_tasks
    tasks.find_all(&:quarantined?)
end
real_plan() click to toggle source

If this plan is a toplevel plan, returns self. If it is a transaction, returns the underlying plan

# File lib/roby/plan.rb, line 442
def real_plan
    ret = self
    while ret.respond_to?(:plan)
        ret = ret.plan
    end
    ret
end
recreate(task) click to toggle source

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.

# File lib/roby/plan.rb, line 1500
def recreate(task)
    new_task = task.create_fresh_copy
    replace_task(task, new_task)
    new_task
end
refresh_relations() click to toggle source
# File lib/roby/plan.rb, line 107
def refresh_relations
    create_relations
end
register_event(event) click to toggle source

@api private

Registers a task object in this plan

It is for Roby internal usage only, for the creation of template plans. Use {#add}.

# File lib/roby/plan.rb, line 904
def register_event(event)
    event.plan = self
    if event.root_object?
        free_events << event
    else
        task_events << event
    end
end
register_task(task) click to toggle source

@api private

Registers a task object in this plan

It is for Roby internal usage only, for the creation of template plans. Use {#add}.

# File lib/roby/plan.rb, line 891
def register_task(task)
    task.plan = self
    tasks << task
    task_index.add(task)
    task_events.merge(task.each_event)
end
remote_tasks() click to toggle source
# File lib/roby/plan.rb, line 1122
def remote_tasks
    if local_tasks = task_index.self_owned
        tasks - local_tasks
    else
        tasks
    end
end
remove_fault_response_table(table_model) click to toggle source

Remove a fault response table that has been added with {#use_fault_response_table}

@overload remove_fault_response_table(table)

@param [Coordination::FaultResponseTable] table the table that
  should be removed. This is the return value of
  {#use_fault_response_table}

@overload remove_fault_response_table(table_model)

Removes all the tables whose model is the given table model

@param [Model<Coordination::FaultResponseTable>] table_model

@return [void] @see use_fault_response_table

# File lib/roby/plan.rb, line 1783
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) click to toggle source
# File lib/roby/plan.rb, line 1401
def remove_free_event(event, timestamp = Time.now)
    if !@free_events.delete?(event)
        raise ArgumentError, "#{event} is not a free event of #{self}"
    end
    remove_free_event!(event, timestamp)
end
remove_free_event!(event, timestamp = Time.now) click to toggle source
# File lib/roby/plan.rb, line 1408
def remove_free_event!(event, timestamp = Time.now)
    @free_events.delete(event)
    @permanent_events.delete(event)
    finalize_event(event, timestamp)
    self
end
remove_object(object, timestamp = Time.now) click to toggle source

@deprecated use {#remove_task} or {#remove_free_event} instead

# File lib/roby/plan.rb, line 1416
def remove_object(object, timestamp = Time.now)
    Roby.warn_deprecated "#remove_object is deprecated, use either #remove_task or #remove_free_event"
    if has_task?(object)
        remove_task(object, timestamp)
    elsif has_free_event?(object)
        remove_free_event(object, timestamp)
    else
        raise ArgumentError, "#{object} is neither a task nor a free event of #{self}"
    end
end
remove_plan_service(service) click to toggle source

Deregisters a plan service from this plan

# File lib/roby/plan.rb, line 770
def remove_plan_service(service)
    if set = plan_services[service.task]
        set.delete(service)
        if set.empty?
            plan_services.delete(service.task)
        end
    end
end
remove_task(task, timestamp = Time.now) click to toggle source
# File lib/roby/plan.rb, line 1381
def remove_task(task, timestamp = Time.now)
    if !@tasks.delete?(task)
        raise ArgumentError, "#{task} is not a task of #{self}"
    end
    remove_task!(task, timestamp)
end
remove_task!(task, timestamp = Time.now) click to toggle source
# File lib/roby/plan.rb, line 1388
def remove_task!(task, timestamp = Time.now)
    @tasks.delete(task)
    @mission_tasks.delete(task)
    @permanent_tasks.delete(task)
    @task_index.remove(task)

    for ev in task.bound_events.each_value
        @task_events.delete(ev)
    end
    finalize_task(task, timestamp)
    self
end
remove_transaction(trsc) click to toggle source

Removes the transaction trsc from the list of known transactions built on this plan

# File lib/roby/plan.rb, line 1037
def remove_transaction(trsc)
    transactions.delete(trsc)
end
remove_trigger(trigger) click to toggle source

Removes a trigger

@param [Object] trigger the trigger to be removed. This is the return value of

the corresponding {#add_trigger} call

@return [void]

# File lib/roby/plan.rb, line 1015
def remove_trigger(trigger)
    triggers.delete(trigger)
    nil
end
replace(from, to) click to toggle source

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

# File lib/roby/plan.rb, line 750
def replace(from, to)
    handle_replace(from, to) do
        from.replace_subplan_by(to)
    end
end
replace_relation_graphs(merged_graphs) click to toggle source
# File lib/roby/plan.rb, line 252
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) click to toggle source

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]
# File lib/roby/plan.rb, line 814
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) click to toggle source

Replace the task from by to in all relations from is part of (including events).

See also replace

# File lib/roby/plan.rb, line 737
def replace_task(from, to)
    handle_replace(from, to) do
        from.replace_by(to)
    end
end
replaced(replaced_task, replacing_task) click to toggle source

Hook called when replacing_task has replaced replaced_task in this plan

# File lib/roby/plan.rb, line 875
def replaced(replaced_task, replacing_task)
    # Make the PlanService object follow the replacement
    if services = plan_services.delete(replaced_task)
        services.each do |srv|
            srv.task = replacing_task
            (plan_services[replacing_task] ||= Set.new) << srv
        end
    end
end
replan(task) click to toggle source

Creates a new planning pattern replacing the given task and its current planner

@param [Roby::Task] task the task that needs to be replanned @return [Roby::Task] the new planning pattern

# File lib/roby/plan.rb, line 1511
def replan(task)
    if !task.planning_task
        return task.create_fresh_copy
    end

    planner = replan(old_planner = task.planning_task)
    planned = task.create_fresh_copy
    planned.abstract = true
    planned.planned_by planner
    replace(task, planned)
    planned
end
root_in_query?(result_set, task, graph) click to toggle source
# File lib/roby/plan.rb, line 1730
def root_in_query?(result_set, task, graph)
    graph.depth_first_visit(task) do |v|
        return false if v != task && result_set.include?(v)
    end
    true
end
root_plan?() click to toggle source

True if this plan is root in the plan hierarchy

# File lib/roby/plan.rb, line 451
def root_plan?
    true
end
same_plan?(other_plan, mappings) click to toggle source

Compares this plan to other_plan, mappings providing the mapping from task/Events in self to task/events in other_plan

# File lib/roby/plan.rb, line 1661
def same_plan?(other_plan, mappings)
    !find_plan_difference(other_plan, mappings)
end
sibling_on?(peer) click to toggle source

If this object is the main plan, checks if we are subscribed to the whole remote plan

Calls superclass method
# File lib/roby/plan.rb, line 42
def sibling_on?(peer)
    if Roby.plan == self then peer.remote_plan
    else super
    end
end
size() click to toggle source

Count of tasks in this plan

# File lib/roby/plan.rb, line 1277
def size
    Roby.warn_deprecated "Plan#size is deprecated, use #num_tasks instead"
    @tasks.size
end
static_garbage_collect() { |t| ... } click to toggle source

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.

# File lib/roby/plan.rb, line 1608
def static_garbage_collect
    if block_given?
        for t in unneeded_tasks
            yield(t)
        end
    else
        for t in unneeded_tasks
            remove_task(t)
        end
    end
end
task_relation_graph_for(model) click to toggle source

Resolves a task graph object from the graph class (i.e. the graph model)

# File lib/roby/plan.rb, line 196
def task_relation_graph_for(model)
    task_relation_graphs.fetch(model)
end
template?() click to toggle source

A template plan is meant to be injected in another plan

When a {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

@see TemplatePlan

# File lib/roby/plan.rb, line 58
def template?; false end
transaction_stack() click to toggle source

Returns the set of stacked transaction

@return [Array] the list of plans in the transaction stack, the first

element being the most-nested transaction and the last element the
underlying real plan (equal to {#real_plan})
# File lib/roby/plan.rb, line 460
def transaction_stack
    plan_chain = [self]
    while plan_chain.last.respond_to?(:plan)
        plan_chain << plan_chain.last.plan
    end
    plan_chain
end
unmark_mission(task) click to toggle source

@deprecated use {#unmark_mission_task} instead

# File lib/roby/plan.rb, line 481
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) click to toggle source

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}

@see add_mission_task mission_task?

# File lib/roby/plan.rb, line 528
def unmark_mission_task(task)
    task = task.to_task
    return if !@mission_tasks.include?(task)
    @mission_tasks.delete(task)
    task.mission = false if task.self_owned?
    notify_task_status_change(task, :normal)
    self
end
unmark_permanent(object) click to toggle source

@deprecated use {#unmark_permanent_task} or {#unmark_permanent_event} instead

# File lib/roby/plan.rb, line 550
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) click to toggle source

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}

@see add_permanent_event permanent_event?

# File lib/roby/plan.rb, line 631
def unmark_permanent_event(event)
    if @permanent_events.delete?(event.to_event)
        notify_event_status_change(event, :normal)
    end
end
unmark_permanent_task(task) click to toggle source

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}

@see add_permanent_event permanent_event?

# File lib/roby/plan.rb, line 600
def unmark_permanent_task(task)
    if @permanent_tasks.delete?(task.to_task)
        notify_task_status_change(task, :normal)
    end
end
unneeded_events() click to toggle source

The set of events that can be removed from the plan

# File lib/roby/plan.rb, line 1229
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() click to toggle source
# File lib/roby/plan.rb, line 1110
def unneeded_tasks
    tasks - useful_tasks
end
use_fault_response_table(table_model, arguments = Hash.new) click to toggle source

Enables a fault response table on this plan

@param [Model<Coordination::FaultResponseTable>] table_model the fault

response table model

@param [Hash] arguments the arguments that should be passed to the

created table

@return [Coordination::FaultResponseTable] the fault response table

that got added to this plan. It can be removed using
{#remove_fault_response_table}

@return [void] @see remove_fault_response_table

# File lib/roby/plan.rb, line 1761
def use_fault_response_table(table_model, arguments = Hash.new)
    table = table_model.new(self, arguments)
    table.attach_to(self)
    active_fault_response_tables << table
    table
end
useful_events() click to toggle source

Computes the set of events that are useful in the plan Events are 'useful' when they are chained to a task.

# File lib/roby/plan.rb, line 1224
def useful_events
    compute_useful_free_events
end
useful_task?(task) click to toggle source

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

# File lib/roby/plan.rb, line 1133
def useful_task?(task)
    tasks.include?(task) && !unneeded_tasks.include?(task)
end
useful_tasks(with_transactions: true) click to toggle source
# File lib/roby/plan.rb, line 1106
def useful_tasks(with_transactions: true)
    compute_useful_tasks(locally_useful_roots(with_transactions: with_transactions))
end
validate_graphs(graphs) click to toggle source

Verifies that all graphs that should be acyclic are

# File lib/roby/plan.rb, line 405
def validate_graphs(graphs)
    # Make a topological sort of the graphs
    seen = Set.new
    Relations.each_graph_topologically(graphs) do |g|
        if seen.include?(g)
            next
        elsif !g.dag?
            next
        end

        if !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) click to toggle source

@api private

Perform sanity checks on a plan object that will be finalized

# File lib/roby/plan.rb, line 1320
def verify_plan_object_finalization_sanity(object)
    if !object.root_object?
        raise ArgumentError, "cannot remove #{object} which is a non-root object"
    elsif object.plan != self
        if !object.plan
            if object.removed_at
                if PlanObject.debug_finalization_place?
                    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
            else
                raise ArgumentError, "#{object} has never been included in this plan"
            end
        end
        raise ArgumentError, "#{object} is not in #{self}: #plan == #{object.plan}"
    end
end