class Roby::Queries::PlanObjectMatcher

Predicate that matches characteristics on a plan object

Attributes

children[R]

@api private

Per relation list of out-edges that the matched object is expected to have

@return [Hash]

indexed_neg_predicates[R]

@api private

Set of predicates that should be false on the object, and for which the index maintains a set of objects for which it is true

@return [Array<Symbol>]

indexed_predicates[R]

@api private

Set of predicates that should be true on the object, and for which the index maintains a set of objects for which it is true

@return [Array<Symbol>]

instance[R]

@api private

The actual instance that should match

@return [nil,Object]

model[R]

@api private

A set of models that should be provided by the object

@return [Array<Class>]

owners[R]

@api private

Set of owners that the object should have

@return [Array<DRobyID>]

parents[R]

@api private

Per-relation list of in-edges that the matched object is expected to have

@return [Hash]

Public Class Methods

match_predicate(name, positive_index = nil, negative_index = nil) click to toggle source

@api private

# File lib/roby/queries/plan_object_matcher.rb, line 114
            def match_predicate(name, positive_index = nil, negative_index = nil)
                method_name = name.to_s.gsub(/\?$/, '')
                if Index::PREDICATES.include?(name)
                    indexed_predicate = true
                    positive_index ||= [["#{name}"], []]
                    negative_index ||= [[], ["#{name}"]]
                end
                positive_index ||= [[], []]
                negative_index ||= [[], []]
                class_eval <<-EOD, __FILE__, __LINE__+1
                def #{method_name}
                    if neg_predicates.include?(:#{name})
                        raise ArgumentError, "trying to match (#{name} & !#{name})"
                    end
                    #{"@indexed_query = false" if !indexed_predicate}
                    predicates << :#{name}
                    #{if !positive_index[0].empty? then ["indexed_predicates", *positive_index[0]].join(" << :") end}
                    #{if !positive_index[1].empty? then ["indexed_neg_predicates", *positive_index[1]].join(" << :") end}
                    self
                end
                def not_#{method_name}
                    if predicates.include?(:#{name})
                        raise ArgumentError, "trying to match (#{name} & !#{name})"
                    end
                    #{"@indexed_query = false" if !indexed_predicate}
                    neg_predicates << :#{name}
                    #{if !negative_index[0].empty? then ["indexed_predicates", *negative_index[0]].join(" << :") end}
                    #{if !negative_index[1].empty? then ["indexed_neg_predicates", *negative_index[1]].join(" << :") end}
                    self
                end
                EOD
                declare_class_methods(method_name, "not_#{method_name}")
            end
new(instance = nil) click to toggle source

Initializes an empty TaskMatcher object

# File lib/roby/queries/plan_object_matcher.rb, line 57
def initialize(instance = nil)
    @instance             = instance
    @indexed_query        = !@instance
    @model                = Array.new
    @predicates           = Array.new
    @neg_predicates       = Array.new
    @indexed_predicates     = Array.new
    @indexed_neg_predicates = Array.new
    @owners               = Array.new
    @parents              = Hash.new
    @children             = Hash.new
end

Public Instance Methods

===(object) click to toggle source

Tests whether the given object matches this predicate

@param [PlanObject] object the object to match @return [Boolean]

# File lib/roby/queries/plan_object_matcher.rb, line 267
def ===(object)
    if instance
        return false if object != instance
    end

    if !model.empty?
        return unless object.fullfills?(model)
    end

    for parent_spec in @parents
        result = handle_parent_child_match(object, parent_spec) do |relation, m, relation_options|
            object.each_parent_object(relation).
                any? { |parent| m === parent && (!relation_options || relation_options === parent[object, relation]) }
        end
        return false if !result
    end

    for child_spec in @children
        result = handle_parent_child_match(object, child_spec) do |relation, m, relation_options|
            object.each_child_object(relation).
                any? { |child| m === child && (!relation_options || relation_options === object[child, relation]) }
        end
        return false if !result
    end

    for pred in predicates
        return false if !object.send(pred)
    end
    for pred in neg_predicates
        return false if object.send(pred)
    end

    return false if !owners.empty? && !(object.owners - owners).empty?
    true
end
executable() click to toggle source

Matches if the object is executable

See also not_executable, PlanObject#executable?

# File lib/roby/queries/plan_object_matcher.rb, line 150
        
filter(initial_set, index, initial_is_complete: false) click to toggle source

Filters the tasks in initial_set by using the information in index, and returns the result. The resulting set must include all tasks in initial_set which match with ===, but can include tasks which do not match ===

@param [Set] initial_set @param [Index] index @return [Set]

# File lib/roby/queries/plan_object_matcher.rb, line 341
def filter(initial_set, index, initial_is_complete: false)
    positive_sets, negative_sets = indexed_sets(index)
    positive_sets << initial_set if !initial_is_complete || positive_sets.empty?

    negative = negative_sets.shift || Set.new
    if negative_sets.size > 1
        negative = negative.dup
        negative_sets.each { |set| negative.merge(set) }
    end

    positive_sets = positive_sets.sort_by(&:size)

    result = Set.new
    result.compare_by_identity
    positive_sets.shift.each do |obj|
        result.add(obj) if !negative.include?(obj) && positive_sets.all? { |set| set.include?(obj) }
    end
    return result
end
indexed_query?() click to toggle source

Returns true if filtering with this TaskMatcher using === is equivalent to calling filter() using a Index. This is used to avoid an explicit O(N) filtering step after filter() has been called

# File lib/roby/queries/plan_object_matcher.rb, line 246
def indexed_query?
    @indexed_query
end
indexed_sets(index) click to toggle source

@api private

Resolve the indexed sets needed to filter an initial set in {#filter}

@return [(Set,Set)] the positive (intersection) and

negative (difference) sets
# File lib/roby/queries/plan_object_matcher.rb, line 309
def indexed_sets(index)
    positive_sets = []
    for m in @model
        positive_sets << index.by_model[m]
    end

    for o in @owners
        if candidates = index.by_owner[o]
            positive_sets << candidates
        else
            return [Set.new, Set.new]
        end
    end

    for pred in @indexed_predicates
        positive_sets << index.by_predicate[pred]
    end

    negative_sets = @indexed_neg_predicates.
        map { |pred| index.by_predicate[pred] }

    return positive_sets, negative_sets
end
not_executable() click to toggle source

Matches if the object is not executable

See also executable, PlanObject#executable?

# File lib/roby/queries/plan_object_matcher.rb, line 163
match_predicates :executable?
not_self_owned() click to toggle source

Filters out locally-owned tasks

Matches if the object is owned by the local plan manager.

# File lib/roby/queries/plan_object_matcher.rb, line 98
def not_self_owned
    neg_predicates << :self_owned?
    self
end
owned_by(*ids) click to toggle source

Filters on ownership

Matches if the object is owned by the listed peers.

Use self_owned to match if it is owned by the local plan manager.

# File lib/roby/queries/plan_object_matcher.rb, line 82
def owned_by(*ids)
    @owners |= ids
    self
end
self_owned() click to toggle source

Filters locally-owned tasks

Matches if the object is owned by the local plan manager.

# File lib/roby/queries/plan_object_matcher.rb, line 90
def self_owned
    predicates << :self_owned?
    self
end
to_s() click to toggle source
# File lib/roby/queries/plan_object_matcher.rb, line 250
def to_s
    description = 
        if instance
            instance.to_s
        elsif model.size == 1
            model.first.to_s
        else
            "(#{model.map(&:to_s).join(",")})"
        end
    ([description] + predicates.map(&:to_s) + neg_predicates.map { |p| "not_#{p}" }).join(".")
end
with_child(other_query, relation = nil, relation_options = nil) click to toggle source

Filters based on the object's children

Matches if this object has at least one child which matches query.

If relation is given, then only the children in this relation are considered. Moreover, relation options can be used to restrict the search even more.

Examples:

parent.depends_on(child)
TaskMatcher.new.
    with_child(TaskMatcher.new.pending) === parent # => true
TaskMatcher.new.
    with_child(TaskMatcher.new.pending, Roby::TaskStructure::Dependency) === parent # => true
TaskMatcher.new.
    with_child(TaskMatcher.new.pending, Roby::TaskStructure::PlannedBy) === parent # => false

TaskMatcher.new.
    with_child(TaskMatcher.new.pending,
               Roby::TaskStructure::Dependency,
               roles: ["trajectory_following"]) === parent # => false
parent.depends_on child, role: "trajectory_following"
TaskMatcher.new.
    with_child(TaskMatcher.new.pending,
               Roby::TaskStructure::Dependency,
               roles: ["trajectory_following"]) === parent # => true
# File lib/roby/queries/plan_object_matcher.rb, line 200
def with_child(other_query, relation = nil, relation_options = nil)
    relation, spec = handle_parent_child_arguments(other_query, relation, relation_options)
    (@children[relation] ||= Array.new) << spec
    @indexed_query = false
    self
end
with_instance(instance) click to toggle source

Match an instance explicitely

# File lib/roby/queries/plan_object_matcher.rb, line 71
def with_instance(instance)
    @instance = instance
    @indexed_query = false
    self
end
with_model(model) click to toggle source

Filters on the task model

Will match if the task is an instance of model or one of its subclasses.

# File lib/roby/queries/plan_object_matcher.rb, line 107
def with_model(model)
    @model = Array(model)
    self
end
with_parent(other_query, relation = nil, relation_options = nil) click to toggle source

Filters based on the object's parents

Matches if this object has at least one parent which matches query.

If relation is given, then only the parents in this relation are considered. Moreover, relation options can be used to restrict the search even more.

See examples for with_child

# File lib/roby/queries/plan_object_matcher.rb, line 216
def with_parent(other_query, relation = nil, relation_options = nil)
    relation, spec = handle_parent_child_arguments(other_query, relation, relation_options)
    (@parents[relation] ||= Array.new) << spec
    @indexed_query = false
    self
end