class Roby::Transaction

A transaction is a special kind of plan. It allows to build plans in a separate sandbox, and then to apply the modifications to the real plan (using commit_transaction), or to discard all modifications (using discard)

In transactions, we do not manipulate plan objects like Task and EventGenerator directly, but through proxies which make sure that nothing forbidden is done

The Proxy module define base functionalities for these proxy objects

Attributes

options[R]

The option hash given at initialization

plan[R]

The plan this transaction applies on

proxy_events[R]

The proxy objects built for events this transaction

proxy_tasks[R]

The proxy objects built for tasks in this transaction

unmarked_mission_tasks[R]

The list of missions of the underlying plan that have been unmarked in the transaction

unmarked_permanent_events[R]

The list of permanent events of the underlying plan that have been unmarked in the transaction

unmarked_permanent_tasks[R]

The list of permanent tasks of the underlying plan that have been unmarked in the transaction

Public Class Methods

new(plan, options = {}) click to toggle source

Creates a new transaction which applies on plan

Calls superclass method Roby::Plan::new
# File lib/roby/transaction.rb, line 393
def initialize(plan, options = {})
    if !plan
        raise ArgumentError, "cannot create a transaction with no plan"
    end

    @options = options
    @frozen = false
    @disable_proxying = false
    @invalid = false

    super()

    @plan   = plan

    @proxy_tasks      = Hash.new
    @proxy_events     = Hash.new
    @unmarked_mission_tasks    = Set.new
    @unmarked_permanent_tasks  = Set.new
    @unmarked_permanent_events = Set.new

    plan.transactions << self
    plan.added_transaction(self)
end

Public Instance Methods

[](object, create: true) click to toggle source
# File lib/roby/transaction.rb, line 185
def [](object, create: true)
    wrap(object, create: create)
end
add(objects) click to toggle source
Calls superclass method Roby::Plan#add
# File lib/roby/transaction.rb, line 450
def add(objects)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    super(objects)
    self
end
add_mission_task(t) click to toggle source
Calls superclass method Roby::Plan#add_mission_task
# File lib/roby/transaction.rb, line 426
def add_mission_task(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    if t.transaction_proxy?
        unmarked_mission_tasks.delete(t.__getobj__)
    end
    super(t)
end
add_permanent_event(e) click to toggle source
Calls superclass method Roby::Plan#add_permanent_event
# File lib/roby/transaction.rb, line 442
def add_permanent_event(e)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    if e.transaction_proxy?
        unmarked_permanent_events.delete(e.__getobj__)
    end
    super(e)
end
add_permanent_task(t) click to toggle source
Calls superclass method Roby::Plan#add_permanent_task
# File lib/roby/transaction.rb, line 434
def add_permanent_task(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    if t.transaction_proxy?
        unmarked_permanent_tasks.delete(t.__getobj__)
    end
    super(t)
end
adding_plan_relation(parent, child, relations, info) click to toggle source

Hook called when a relation is added between plan objects that are present in the transaction

If the new relation is not present in the transaction as well, it invalidates the transaction and calls DecisionControl#adding_plan_relation(self, parent, child, relations, info) for further action

@param [PlanObject] parent the parent object represented by its proxy in self @param [PlanObject] child the child object represented by its proxy in self @param [Array<Relations::Graph>] relations the graphs in which a relation

has been added

@param [Object] info the added information for the new edges

(relation specific)

@return [void]

# File lib/roby/transaction.rb, line 926
def adding_plan_relation(parent, child, relations, info)
    missing_relations = relations.find_all do |rel|
        !parent.child_object?(child, rel)
    end
    unless missing_relations.empty?
        invalidate("plan added a relation #{parent} -> #{child} in #{relations} with info #{info}")
        control.adding_plan_relation(self, parent, child, relations, info)
    end
end
apply_graph_modifications(work_graphs, added, removed, updated) click to toggle source

@api private

Apply the graph modifications returned by {#compute_graph_modifications_for}

# File lib/roby/transaction.rb, line 553
def apply_graph_modifications(work_graphs, added, removed, updated)
    added.each do |graph, parent, child, info|
        work_graphs[graph].add_edge(parent, child, info)
    end
    removed.each do |graph, parent, child|
        work_graphs[graph].remove_edge(parent, child)
    end
    updated.each do |graph, parent, child, info|
        work_graphs[graph].set_edge_info(parent, child, info)
    end
end
apply_modifications_to_plan() click to toggle source

Apply the modifications represented by self to the underlying plan snippet in your redefinition if you do so.

# File lib/roby/transaction.rb, line 646
def apply_modifications_to_plan
    new_mission_tasks    = Set.new
    new_permanent_tasks  = Set.new
    new_permanent_events = Set.new

    added_relations   = Array.new
    removed_relations = Array.new
    updated_relations = Array.new

    # We're doing a lot of modifications of this plan .. store some of
    # the sets we need for later, one part to keep them unchanged, one
    # part to make sure we don't do modify-while-iterate
    proxy_tasks     = self.proxy_tasks.dup
    proxy_events    = self.proxy_events.dup
    plan_services   = self.plan_services.dup
    unmarked_mission_tasks    = self.unmarked_mission_tasks.dup
    unmarked_permanent_tasks  = self.unmarked_permanent_tasks.dup
    unmarked_permanent_events = self.unmarked_permanent_events.dup
    # We're taking care of the proxies first, so that we can merge the
    # transaction using Plan#merge!. However, this means that
    # #may_unwrap does not work after the first few steps. We therefore
    # have to store the object-to-proxy mapping
    real_objects  = Hash.new

    # We make a copy of all relation graphs, and update them with the
    # transaction data. The underlying plan graphs are not modified
    #
    # We do not #dup them because we don't want to dup the edge info.
    # Instead, we instanciate anew and merge. The add_vertex calls are
    # needed to make sure that the graph dups the in/out sets instead
    # of just copying them
    task_work_graphs, event_work_graphs =
        plan.class.instanciate_relation_graphs
    work_graphs, transaction_graphs = Hash.new, Hash.new
    plan.each_task_relation_graph do |g|
        work_g = work_graphs[g] = task_work_graphs[g.class]
        g.each_vertex { |v| work_g.add_vertex(v) }
        work_g.merge(g)
        transaction_graphs[g] = task_relation_graph_for(g.class)
    end
    plan.each_event_relation_graph do |g|
        work_g = work_graphs[g] = event_work_graphs[g.class]
        g.each_vertex { |v| work_g.add_vertex(v) }
        work_g.merge(g)
        transaction_graphs[g] = event_relation_graph_for(g.class)
    end

    # First apply all changes related to the proxies to the underlying
    # plan. This adds some new tasks to the plan graph, but does not add
    # them to the plan itself
    #
    # Note that we need to do that in two passes. The first one keeps
    # the transaction unchanged, the second one removes the proxies from
    # the transaction. This is needed so that #commit_transaction sees
    # the graph unchanged
    proxy_objects = proxy_tasks.merge(proxy_events)

    proxy_objects.each do |object, proxy|
        real_objects[proxy] = object
        compute_graph_modifications_for(
            proxy, added_relations, removed_relations, updated_relations)
        proxy.commit_transaction
    end
    proxy_tasks.dup.each do |object, proxy|
        if proxy.self_owned?
            if mission_task?(proxy)
                new_mission_tasks << object
            elsif permanent_task?(proxy)
                new_permanent_tasks << object
            end
        end
        remove_task(proxy)
    end
    proxy_events.dup.each do |object, proxy|
        if proxy.root_object?
            if permanent_event?(proxy)
                new_permanent_events << object
            end
            remove_free_event(proxy)
        end
    end

    work_graphs.each do |plan_g, work_g|
        work_g.merge(transaction_graphs[plan_g])
    end
    apply_graph_modifications(work_graphs, added_relations, removed_relations, updated_relations)

    begin
        validate_graphs(work_graphs.values)
    rescue Exception => e
        raise e, "cannot apply #{self}: #{e.message}", e.backtrace
    end

    #### UNTIL THIS POINT we have not modified the underlying plan AT ALL
    # We DID update the transaction, though

    # Apply #commit_transaction on the remaining tasks
    tasks.each(&:commit_transaction)
    free_events.each(&:commit_transaction)

    # What is left in the transaction is the network of new tasks. Just
    # merge it
    plan.merge_transaction!(self, work_graphs,
                           added_relations, removed_relations, updated_relations)

    # Update the plan services on the underlying plan. The only
    # thing we need to take care of is replacements and new
    # services. Other modifications will be applied automatically
    plan_services.each do |task, services|
        services.each do |srv|
            if srv.transaction_proxy?
                # Modified service. Might be moved to a new task
                original = srv.__getobj__
                # Do NOT use may_unwrap here ... See comments at the top
                # of the method
                task     = real_objects[task] || task
                srv.commit_transaction
                if original.task != task
                    plan.move_plan_service(original, task)
                end
            elsif task.transaction_proxy?
                # New service on an already existing task
                srv.task = task.__getobj__
                plan.add_plan_service(srv)
            else
                # New service on a new task
                plan.add_plan_service(srv)
            end
        end
    end

    new_mission_tasks.each { |t| plan.add_mission_task(t) }
    new_permanent_tasks.each { |t| plan.add_permanent_task(t) }
    new_permanent_events.each { |e| plan.add_permanent_event(e) }

    active_fault_response_tables.each do |tbl|
        plan.use_fault_response_table tbl.model, tbl.arguments
    end

    unmarked_permanent_events.each { |t| plan.unmark_permanent_event(t) }
    unmarked_permanent_tasks.each { |t| plan.unmark_permanent_task(t) }
    unmarked_mission_tasks.each { |t| plan.unmark_mission_task(t) }

    proxy_objects.each do |object, proxy|
        forwarder_module = Transaction::Proxying.forwarder_module_for(object.model)
        proxy.extend forwarder_module
        proxy.__getobj__ = object
        proxy.__freeze__
    end
end
apply_triggers_on_committed_transaction(triggered_matches) click to toggle source

@api private

Apply the triggers as returned by {#compute_triggers_for_committed_transaction}

# File lib/roby/transaction.rb, line 638
def apply_triggers_on_committed_transaction(triggered_matches)
    triggered_matches.each do |task, trigger|
        trigger.call(task)
    end
end
check_valid_transaction() click to toggle source

Tests if it is safe to commit this transaction

@return [void] @raise [InvalidTransaction] in all cases where {#valid_transaction?}

returns false
# File lib/roby/transaction.rb, line 537
def check_valid_transaction
    return if valid_transaction?

    unless transactions.empty?
        raise InvalidTransaction, "there is still transactions on top of this one"
    end
    message = invalidation_reasons.map do |reason, trace|
        "#{trace[0]}: #{reason}\n  #{trace[1..-1].join("\n  ")}"
    end.join("\n")
    raise InvalidTransaction, "invalid transaction: #{message}"
end
clear() click to toggle source

Clears this transaction

A cleared transaction behaves as a new transaction on the same plan @return [void]

Calls superclass method Roby::Plan#clear
# File lib/roby/transaction.rb, line 871
def clear
    unmarked_mission_tasks.clear
    unmarked_permanent_tasks.clear
    unmarked_permanent_events.clear
    proxy_tasks.each_value { |proxy| proxy.clear_relations }
    proxy_tasks.clear
    proxy_events.each_value { |proxy| proxy.clear_relations }
    proxy_events.clear
    super
end
commit_transaction() { || ... } click to toggle source

Commit all modifications that have been registered in this transaction

# File lib/roby/transaction.rb, line 799
def commit_transaction
    check_valid_transaction
    trigger_matches = compute_triggers_for_committed_transaction
    apply_modifications_to_plan
    apply_triggers_on_committed_transaction(trigger_matches)
    frozen!

    @committed = true
    committed_transaction
    plan.remove_transaction(self)
    @plan = nil

    yield if block_given?
end
committed_transaction() click to toggle source

Hook called just after this transaction has been committed

@return [void]

# File lib/roby/transaction.rb, line 817
def committed_transaction; end
compute_graph_modifications_for(proxy, new_relations, removed_relations, updated_relations) click to toggle source

@api private

Compute the graph modifications that are involving the given proxy

It only computes the parent modifications involving objects that are not proxies themselves. It computes the child modifications for every child

# File lib/roby/transaction.rb, line 572
def compute_graph_modifications_for(proxy, new_relations, removed_relations, updated_relations)
    real_object = proxy.__getobj__
    proxy.partition_new_old_relations(:each_parent_object, include_proxies: false) do |trsc_objects, rel, new, del, existing|
        trsc_graph = proxy.relation_graph_for(rel)
        plan_graph = proxy.__getobj__.relation_graph_for(rel)

        new.each do |task|
            edge_info = trsc_graph.edge_info(trsc_objects[task], proxy)
            new_relations << [plan_graph, task, real_object, edge_info]
        end
        del.each do |task|
            removed_relations << [plan_graph, task, real_object]
        end
        existing.each do |task|
            edge_info = trsc_graph.edge_info(trsc_objects[task], proxy)
            if plan_graph.edge_info(task, real_object) != edge_info
                updated_relations << [plan_graph, task, real_object, edge_info]
            end
        end
    end

    proxy.partition_new_old_relations(:each_child_object) do |trsc_objects, rel, new, del, existing|
        trsc_graph = proxy.relation_graph_for(rel)
        plan_graph = proxy.__getobj__.relation_graph_for(rel)

        new.each do |task|
            edge_info = trsc_graph.edge_info(proxy, trsc_objects[task])
            new_relations << [plan_graph, real_object, task, edge_info]
        end
        del.each do |task|
            removed_relations << [plan_graph, real_object, task]
        end
        existing.each do |task|
            edge_info = trsc_graph.edge_info(proxy, trsc_objects[task])
            if plan_graph.edge_info(real_object, task) != edge_info
                updated_relations << [plan_graph, real_object, task, edge_info]
            end
        end
    end
end
compute_triggers_for_committed_transaction() click to toggle source

@api private

This compute the triggers that shoul be applied if we commit this transaction

# File lib/roby/transaction.rb, line 617
def compute_triggers_for_committed_transaction
    trigger_matches = Hash.new
    plan.triggers.each do |tr|
        tr.each(self) do |t|
            trigger_matches[t] = tr
        end
    end
    proxy_tasks.each do |obj, proxy|
        if tr = trigger_matches.delete(proxy)
            if !(tr === obj) # already triggered
                trigger_matches[obj] = tr
            end
        end
    end
    trigger_matches
end
control() click to toggle source

The decision control object associated with this transaction. It is in general plan.control

# File lib/roby/transaction.rb, line 390
def control; plan.control end
copy_object_relations(object, proxy, proxy_map) click to toggle source

This method copies on proxy all relations of object for which both ends of the relation are already in the transaction.

# File lib/roby/transaction.rb, line 236
def copy_object_relations(object, proxy, proxy_map)
    # Create edges between the neighbours that are really in the transaction
    object.each_relation do |rel|
        plan_graph = object.relation_graph_for(rel)
        trsc_graph = proxy.relation_graph_for(rel)

        plan_graph.each_in_neighbour(object) do |parent|
            if parent_proxy = proxy_map[parent]
                trsc_graph.add_edge(parent_proxy, proxy, plan_graph.edge_info(parent, object))
            end
        end
        plan_graph.each_out_neighbour(object) do |child|
            if child_proxy = proxy_map[child]
                trsc_graph.add_edge(proxy, child_proxy, plan_graph.edge_info(object, child))
            end
        end
    end
end
create_and_register_proxy_event(object) click to toggle source
# File lib/roby/transaction.rb, line 93
def create_and_register_proxy_event(object)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    proxy = object.dup
    setup_and_register_proxy_event(proxy, object)
    copy_object_relations(object, proxy, proxy_events)
    proxy
end
create_and_register_proxy_plan_service(object) click to toggle source
# File lib/roby/transaction.rb, line 101
def create_and_register_proxy_plan_service(object)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    # Ensure the underlying task is wrapped
    proxy = object.dup
    setup_and_register_proxy_plan_service(proxy, object)
    proxy
end
create_and_register_proxy_task(object) click to toggle source
# File lib/roby/transaction.rb, line 85
def create_and_register_proxy_task(object)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    proxy = object.dup
    setup_and_register_proxy_task(proxy, object)
    copy_object_relations(object, proxy, proxy_tasks)
    proxy
end
disable_proxying() { || ... } click to toggle source
# File lib/roby/transaction.rb, line 820
def disable_proxying
    @disable_proxying = true
    if block_given?
        begin
            yield
        ensure
            @disable_proxying = false
        end
    end
end
discard_modifications(object) click to toggle source
# File lib/roby/transaction.rb, line 1116
def discard_modifications(object)
    if object.respond_to?(:to_task)
        remove_task(object.to_task)
    else
        remove_event(object.to_task)
    end
end
discard_transaction() click to toggle source

Discard all the modifications that have been registered in this transaction

@return [void]

# File lib/roby/transaction.rb, line 846
def discard_transaction
    if !transactions.empty?
        raise InvalidTransaction, "there is still transactions on top of this one"
    end

    frozen!

    discarded_transaction
    plan.remove_transaction(self)
    @plan = nil
end
discard_transaction!() click to toggle source

Discards this transaction and all the transactions it is part of

@return [void]

# File lib/roby/transaction.rb, line 835
def discard_transaction!
    transactions.each do |trsc|
        trsc.discard_transaction!
    end
    discard_transaction
end
discarded_transaction() click to toggle source

Hook called just after this transaction has been discarded

@return [void]

# File lib/roby/transaction.rb, line 861
def discarded_transaction; end
edit() { || ... } click to toggle source
# File lib/roby/transaction.rb, line 191
def edit
    yield if block_given?
end
enable_proxying() click to toggle source
# File lib/roby/transaction.rb, line 819
def enable_proxying; @disable_proxying = false end
executable?() click to toggle source

If true, an engine could execute tasks included in this plan. This is alwxays false for transactions

@return [Boolean]

# File lib/roby/transaction.rb, line 13
def executable?; false end
execute(&block) click to toggle source

Calls the given block in the execution thread of the engine of the underlying plan. If there is no engine attached to this plan, yields immediately.

See Plan#execute and ExecutionEngine#execute

# File lib/roby/transaction.rb, line 422
def execute(&block)
    plan.execute(&block)
end
extend_proxy_object(proxy, object, klass = object.class) click to toggle source
# File lib/roby/transaction.rb, line 36
def extend_proxy_object(proxy, object, klass = object.class)
    proxy.extend Roby::Transaction::Proxying.proxying_module_for(klass)
end
finalized?() click to toggle source

True if this transaction has either been discarded or committed

@return [Boolean] @see committed?

# File lib/roby/transaction.rb, line 29
def finalized?; !plan end
finalized_plan_event(event) click to toggle source

Hook called when an event included in self got finalized from {#plan}

It invalidates the transaction and calls DecisionControl#finalized_plan_event(self, event) for further actions

@param [EventGenerator] event the finalized event represented by its proxy in self @return [void]

# File lib/roby/transaction.rb, line 904
def finalized_plan_event(event)
    proxied_event = event.__getobj__

    invalidate("event #{event} has been removed from the plan")
    discard_modifications(proxied_event)
    control.finalized_plan_event(self, event)
end
finalized_plan_task(task) click to toggle source

Hook called when a task included in self got finalized from {#plan}

It invalidates the transaction and calls DecisionControl#finalized_plan_task(self, event) for further actions

@param [Task] task the finalized task represented by its proxy in self @return [void]

# File lib/roby/transaction.rb, line 889
def finalized_plan_task(task)
    proxied_task = task.__getobj__

    invalidate("task #{task} has been removed from the plan")
    discard_modifications(proxied_task)
    control.finalized_plan_task(self, task)
end
find_local_object_for_event(object) click to toggle source
# File lib/roby/transaction.rb, line 126
def find_local_object_for_event(object)
    find_local_object_for_plan_object(object, proxy_events)
end
find_local_object_for_plan_object(object, proxy_map) click to toggle source
# File lib/roby/transaction.rb, line 109
def find_local_object_for_plan_object(object, proxy_map)
    if object.plan == self
        return object
    elsif proxy = proxy_map[object]
        return proxy
    elsif !object.plan
        raise ArgumentError, "#{object} has been removed from plan"
    elsif object.plan.template?
        add(object)
        return object
    end
end
find_local_object_for_plan_service(object) click to toggle source
# File lib/roby/transaction.rb, line 130
def find_local_object_for_plan_service(object)
    if local_task = find_local_object_for_task(object.task)
        find_plan_service(local_task)
    end
end
find_local_object_for_task(object) click to toggle source
# File lib/roby/transaction.rb, line 122
def find_local_object_for_task(object)
    find_local_object_for_plan_object(object, proxy_tasks)
end
frozen!() click to toggle source
# File lib/roby/transaction.rb, line 863
def frozen!
    @frozen = true
end
has_proxy_for_event?(object) click to toggle source

Tests whether an event has a proxy in self

Unlike {#wrap}, the provided object must be a plan object from the transaction's underlying plan

@param [Roby::EventGenerator] object the object to test for

# File lib/roby/transaction.rb, line 274
def has_proxy_for_event?(object)
    if object.plan != self.plan
        raise ArgumentError, "#{object} is not in #{self}.plan (#{plan})"
    end
    proxy_events.has_key?(object)
end
has_proxy_for_task?(object) click to toggle source

Tests whether a plan object has a proxy in self

Unlike {#wrap}, the provided object must be a plan object from the transaction's underlying plan

@param [Roby::Task] object the object to test for

# File lib/roby/transaction.rb, line 261
def has_proxy_for_task?(object)
    if object.plan != self.plan
        raise ArgumentError, "#{object} is not in #{self}.plan (#{plan})"
    end
    proxy_tasks.has_key?(object)
end
import_subplan_relations(graphs, mappings, proxy_map) click to toggle source

@api private

Copies relations when importing a new subplan from the main plan

@param [Hash<Relations::Graph,Relations::Graph>] relation graph

mapping from the plan graphs to the transaction graphs

@param [Hash<PlanObject,PlanObject>] mappings

mapping from the plan objects to the transaction objects
# File lib/roby/transaction.rb, line 203
def import_subplan_relations(graphs, mappings, proxy_map)
    graphs.each do |plan_g, self_g|
        plan_g.copy_subgraph_to(self_g, mappings)
        mappings.each do |plan_v, self_v|
            # The method assumes that the plan objects are new. We are
            # therefore done if there is the same number of relations in
            # both plan and transactions
            #
            # It is NOT true in the general case as one can add extra
            # relations in the transaction
            if plan_g.in_degree(plan_v) != self_g.in_degree(self_v)
                plan_g.each_in_neighbour(plan_v) do |plan_parent|
                    next if mappings.has_key?(plan_parent)
                    if self_parent = proxy_map[plan_parent]
                        self_g.add_edge(self_parent, self_v, plan_g.edge_info(plan_parent, plan_v))
                    end
                end
            end

            if plan_g.out_degree(plan_v) != self_g.out_degree(self_v)
                plan_g.each_out_neighbour(plan_v) do |plan_child|
                    next if mappings.has_key?(plan_child)
                    if self_child = proxy_map[plan_child]
                        self_g.add_edge(self_v, self_child, plan_g.edge_info(plan_v, plan_child))
                    end
                end
            end
        end
    end
end
invalid=(flag) click to toggle source

Marks this transaction as either invalid or valid. If it is marked as valid, it clears {#invalidation_reasons}.

# File lib/roby/transaction.rb, line 504
def invalid=(flag)
    if !flag
        invalidation_reasons.clear
    end
    @invalid = flag
end
invalid?() click to toggle source

True if {#invalidate} has been called, and {#invalid=} has not been called to clear the invalidation afterwards.

# File lib/roby/transaction.rb, line 513
def invalid?; @invalid end
invalidate(reason = nil) click to toggle source

Marks this transaction as valid

# File lib/roby/transaction.rb, line 524
def invalidate(reason = nil)
    self.invalid = true
    invalidation_reasons << [reason, caller(1)] if reason
    Roby.debug do
        "invalidating #{self}: #{reason}"
    end
end
may_unwrap(object) click to toggle source

If object is in this transaction, may_unwrap will return the underlying plan object. In all other cases, returns object.

# File lib/roby/transaction.rb, line 357
def may_unwrap(object)
    if object.respond_to?(:plan) 
        if object.plan == self && object.respond_to?(:__getobj__)
            object.__getobj__
        elsif object.plan == self.plan
            object
        else
            object
        end
    else object
    end
end
may_wrap(objects, create: true) click to toggle source
# File lib/roby/transaction.rb, line 343
def may_wrap(objects, create: true)
    if objects.respond_to?(:to_ary)
        objects.map { |obj| may_wrap(obj, create: create) }
    elsif objects.respond_to?(:each)
        raise ArgumentError, "don't know how to wrap containers of class #{objects.class}"
    elsif objects.kind_of?(PlanObject)
        wrap(objects, create: create)
    else
        objects
    end
end
propose() click to toggle source
# File lib/roby/transaction.rb, line 190
def propose; end
proxying?() click to toggle source
# File lib/roby/transaction.rb, line 830
def proxying?; !@frozen && !@disable_proxying end
reachable_on_applied_transaction?(transaction_seeds, transaction_set, transaction_graph, plan_seeds, plan_set, plan_graph) click to toggle source

@api private

Tests whether a task in plan_set or proxy_set would be reachable from 'task' if the transaction was applied

# File lib/roby/transaction.rb, line 1052
def reachable_on_applied_transaction?(transaction_seeds, transaction_set, transaction_graph,
                                      plan_seeds, plan_set, plan_graph)
    transaction_visitor = ReachabilityTransactionVisitor.new(
        transaction_graph, self, plan_seeds, transaction_set)
    if task = transaction_seeds.first
        transaction_visitor.handle_start_vertex(task)
    end
    plan_visitor        = ReachabilityPlanVisitor.new(
        plan_graph, self, transaction_seeds, plan_set)
    if task = plan_seeds.first
        plan_visitor.handle_start_vertex(task)
    end

    catch(:reachable) do
        while !transaction_seeds.empty? || !plan_seeds.empty?
            transaction_seeds.each do |seed|
                seed = transaction_seeds.shift
                if !transaction_visitor.finished_vertex?(seed)
                    transaction_graph.depth_first_visit(seed, transaction_visitor) {}
                end
            end
            transaction_seeds.clear

            plan_seeds.each do |seed|
                seed = plan_seeds.shift
                if !plan_visitor.finished_vertex?(seed)
                    plan_graph.depth_first_visit(seed, plan_visitor) {}
                end
            end
            plan_seeds.clear
        end
        return false
    end
    true
end
remove_free_event(event, timestamp = Time.now) click to toggle source
Calls superclass method Roby::Plan#remove_free_event
# File lib/roby/transaction.rb, line 335
def remove_free_event(event, timestamp = Time.now)
    unwrapped, proxy = remove_plan_object(event, proxy_events)
    if proxy
        unmarked_permanent_events.delete(unwrapped)
    end
    super(proxy || event, timestamp)
end
remove_plan_object(object, proxy_map) click to toggle source

Removes an object from this transaction

This does not remove the object from the underlying plan. Removing objects directly is (at best) dangerous, and should be handled by garbage collection.

# File lib/roby/transaction.rb, line 310
def remove_plan_object(object, proxy_map)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?

    object = may_unwrap(object)
    proxy  = proxy_map.delete(object)
    actual_plan = (proxy || object).plan

    if actual_plan != self
        raise InternalError, "inconsistency: #{proxy || object} plan is #{actual_plan}, was expected to be #{self}"
    end
    return object, proxy
end
remove_task(task, timestamp = Time.now) click to toggle source
Calls superclass method Roby::Plan#remove_task
# File lib/roby/transaction.rb, line 323
def remove_task(task, timestamp = Time.now)
    unwrapped, proxy = remove_plan_object(task, proxy_tasks)
    if proxy
        unmarked_mission_tasks.delete(unwrapped)
        unmarked_permanent_tasks.delete(unwrapped)
        proxy.each_plan_child do |task_event_proxy|
            remove_plan_object(task_event_proxy, proxy_events)
        end
    end
    super(proxy || task, timestamp)
end
removing_plan_relation(parent, child, relations) click to toggle source

Hook called when a relation is removed between plan objects that are present in the transaction

If the removed relation is still present in the transaction as well, it invalidates the transaction and calls DecisionControl#removing_plan_relation(self, parent, child, relations, info) for further action

@param [PlanObject] parent the parent object represented by its proxy in self @param [PlanObject] child the child object represented by its proxy in self @param [Array<Relations::Graph>] relations the graphs in which a relation

has been added

@return [void]

# File lib/roby/transaction.rb, line 948
def removing_plan_relation(parent, child, relations)
    present_relations = relations.find_all do |rel|
        parent.child_object?(child, rel)
    end
    unless present_relations.empty?
        invalidate("plan removed a relation #{parent} -> #{child} in #{relations}")
        control.removing_plan_relation(self, parent, child, relations)
    end
end
restore_relation(proxy, relation) click to toggle source
# File lib/roby/transaction.rb, line 281
def restore_relation(proxy, relation)
    object = proxy.__getobj__

    proxy_children = proxy.child_objects(relation)
    object.child_objects(relation).each do |object_child| 
        next unless proxy_child = wrap(object_child, create: false)
        if proxy_children.include?(proxy_child)
            relation.remove_edge(proxy, proxy_child)
        end
    end

    proxy_parents = proxy.parent_objects(relation)
    object.parent_objects(relation).each do |object_parent| 
        next unless proxy_parent = wrap(object_parent, create: false)
        if proxy_parents.include?(proxy_parent)
            relation.remove_edge(parent, proxy_parent)
        end
    end

    added_objects.delete(proxy)
    proxy.discovered_relations.delete(relation)
    proxy.do_discover(relation, false)
end
root_plan?() click to toggle source

(see Plan#root_plan?)

# File lib/roby/transaction.rb, line 32
def root_plan?
    false
end
setup_and_register_proxy_event(proxy, event) click to toggle source
# File lib/roby/transaction.rb, line 61
def setup_and_register_proxy_event(proxy, event)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?

    proxy_events[event] = proxy
    extend_proxy_object(proxy, event)
    proxy.plan = self
    proxy.setup_proxy(event, self)
    register_event(proxy)
    if plan.permanent_event?(event)
        add_permanent_event(proxy)
    end
    proxy
end
setup_and_register_proxy_plan_service(proxy, plan_service) click to toggle source
# File lib/roby/transaction.rb, line 75
def setup_and_register_proxy_plan_service(proxy, plan_service)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?

    extend_proxy_object(proxy, plan_service)
    proxy.setup_proxy(plan_service, self)
    proxy.task = wrap_task(plan_service.to_task) 
    add_plan_service(proxy)
    proxy
end
setup_and_register_proxy_task(proxy, task) click to toggle source
# File lib/roby/transaction.rb, line 40
def setup_and_register_proxy_task(proxy, task)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?

    proxy_tasks[task] = proxy
    extend_proxy_object(proxy, task)
    proxy.plan = self
    proxy.setup_proxy(task, self)
    register_task(proxy)
    if plan.mission_task?(task)
        add_mission_task(proxy)
    elsif plan.permanent_task?(task)
        add_permanent_task(proxy)
    end
    if services = plan.find_all_plan_services(task)
        services.each do |original_srv|
            create_and_register_proxy_plan_service(original_srv)
        end
    end
    proxy
end
unmark_mission_task(t) click to toggle source
Calls superclass method Roby::Plan#unmark_mission_task
# File lib/roby/transaction.rb, line 482
def unmark_mission_task(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    t = t.as_plan
    if proxy = find_local_object_for_task(t)
        super(proxy)
    end

    t = may_unwrap(t)
    if t.plan == self.plan
        unmarked_mission_tasks.add(t)
    end
end
unmark_permanent_event(t) click to toggle source
Calls superclass method Roby::Plan#unmark_permanent_event
# File lib/roby/transaction.rb, line 456
def unmark_permanent_event(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    t = t.as_plan
    if proxy = find_local_object_for_event(t)
        super(proxy)
    end

    t = may_unwrap(t)
    if t.plan == self.plan
        unmarked_permanent_events.add(t)
    end
end
unmark_permanent_task(t) click to toggle source
Calls superclass method Roby::Plan#unmark_permanent_task
# File lib/roby/transaction.rb, line 469
def unmark_permanent_task(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if frozen?
    t = t.as_plan
    if proxy = find_local_object_for_task(t)
        super(proxy)
    end

    t = may_unwrap(t)
    if t.plan == self.plan
        unmarked_permanent_tasks.add(t)
    end
end
valid_transaction?() click to toggle source

Tests if it is safe to commit this transaction

@return [Boolean] it returns false if there are other transactions on

top of it. They must be committed or discarded before this transaction
can be committed or discarded. It also returns safe if if this
transaction has been marked as invalid with {#invalidate}
# File lib/roby/transaction.rb, line 521
def valid_transaction?; transactions.empty? && !invalid? end
wrap(object, create: true) click to toggle source

Get the transaction proxy for object

# File lib/roby/transaction.rb, line 169
def wrap(object, create: true)
    if object.kind_of?(PlanService)
        wrap_plan_service(object, create: create)
    elsif object.respond_to?(:to_task)
        wrap_task(object, create: create)
    elsif object.respond_to?(:to_event)
        wrap_event(object, create: create)
    elsif object.respond_to?(:to_ary) 
        object.map { |o| wrap(o, create: create) }
    elsif object.respond_to?(:each)
        raise ArgumentError, "don't know how to wrap containers of class #{object.class}"
    else
        raise TypeError, "don't know how to wrap #{object || 'nil'} of type #{object.class.ancestors}"
    end
end
wrap_event(event, create: true) click to toggle source
# File lib/roby/transaction.rb, line 152
def wrap_event(event, create: true)
    if local_event = find_local_object_for_event(event)
        local_event
    elsif create
        wrap_plan_object(event, proxy_events)
    end
end
wrap_plan_object(object, proxy_map) click to toggle source
# File lib/roby/transaction.rb, line 136
def wrap_plan_object(object, proxy_map)
    if object.plan != self.plan
        raise ArgumentError, "#{object} is in #{object.plan}, this transaction #{self} applies on #{self.plan}"
    else
        return object.create_transaction_proxy(self)
    end
end
wrap_plan_service(plan_service, create: true) click to toggle source
# File lib/roby/transaction.rb, line 160
def wrap_plan_service(plan_service, create: true)
    if local_plan_service = find_local_object_for_plan_service(plan_service)
        local_plan_service
    elsif create
        plan_service.create_transaction_proxy(self)
    end
end
wrap_task(task, create: true) click to toggle source
# File lib/roby/transaction.rb, line 144
def wrap_task(task, create: true)
    if local_task = find_local_object_for_task(task)
        local_task
    elsif create
        wrap_plan_object(task, proxy_tasks)
    end
end