Module: Roby::Test::RunPlanners

Included in:
Self, Spec
Defined in:
lib/roby/test/run_planners.rb

Overview

Module that implement the #run_planners functionality

It is already included in Roby’s own test classes, you do not need to use this module directly. Simply use #run_planners

Defined Under Namespace

Classes: ActionPlanningHandler, PlanningHandler

Constant Summary collapse

@@roby_planner_handlers =
[]

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.deregister_planning_handler(handler) ⇒ Object

Remove a planning handler added with roby_plan_with



184
185
186
# File 'lib/roby/test/run_planners.rb', line 184

def self.deregister_planning_handler(handler)
    @@roby_planner_handlers.delete_if { |_, h| h == handler }
end

.planner_handler_for(task) ⇒ PlanningHandler

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Find the handler that should be used by #roby_run_planner to plan a given task.

Parameters:

Returns:

Raises:

  • ArgumentError



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/roby/test/run_planners.rb', line 161

def self.planner_handler_for(task)
    _, handler_class =
        @@roby_planner_handlers.find do |matcher, _handler|
            matcher === task
        end
    unless handler_class
        raise ArgumentError, "no planning handler found for #{task}"
    end

    handler_class
end

.roby_plan_with(matcher, handler) ⇒ Object

Declare what #roby_run_planner should use to develop a given task during a test

The latest handler registered wins

Parameters:



179
180
181
# File 'lib/roby/test/run_planners.rb', line 179

def self.roby_plan_with(matcher, handler)
    @@roby_planner_handlers.unshift [matcher, handler]
end

.setup_planning_handlers(test, plan, root_tasks, recursive: true) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Helper that sets up the planning handlers for #run_planners



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/roby/test/run_planners.rb', line 13

def self.setup_planning_handlers(test, plan, root_tasks, recursive: true)
    root_tasks = root_tasks.map do |root_task|
        if root_task.respond_to?(:as_plan)
            root_task = root_task.as_plan
            plan.add(root_task)
        end
        root_task
    end

    if recursive
        tasks = root_tasks.each_with_object(root_tasks.to_set) do |task, s|
            s.merge(
                plan.task_relation_graph_for(Roby::TaskStructure::Dependency)
                    .enum_for(:depth_first_visit, task).to_set
            )
        end
    else
        tasks = root_tasks.to_set
    end

    by_handler =
        tasks
        .find_all { |t| t.abstract? && t.planning_task }
        .find_all { |t| !t.planning_task.failed? }
        .group_by { |t| RunPlanners.planner_handler_for(t) }
        .map { |h_class, h_tasks| [h_class.new(test), h_tasks] }
    return root_tasks.map(&:as_service), [] if by_handler.empty?

    placeholder_tasks = {}
    by_handler.each do |handler, handler_tasks|
        handler_tasks.each do |t|
            placeholder_tasks[t] ||= t.as_service
        end
        handler.start(handler_tasks)
    end

    services = root_tasks.map { |t| placeholder_tasks[t] ||= t.as_service }
    [services, by_handler]
end

Instance Method Details

#run_planners(root_tasks, recursive: true) ⇒ Object

Run the planners that are required by a task or subplan

Parameters:

  • root_task (Task, Array<Task>)

    the task whose planners we want to run, or the root of the subplan

  • recursive (Boolean) (defaults to: true)

    whether the method attempts to run the planners recursively in both plan (considering the whole subplan of root_task) and time (re-run planners for tasks if existing planning tasks generate subplans containing planning tasks themselves)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/roby/test/run_planners.rb', line 64

def run_planners(root_tasks, recursive: true)
    was_array = root_tasks.respond_to?(:to_ary)
    root_tasks = Array(root_tasks)
    if (not_a_task = root_tasks.find { |t| !t.respond_to?(:as_plan) })
        raise ArgumentError,
              "#{not_a_task} is not a Roby task and cannot "\
              "be converted to one"
    end

    unless execution_engine.in_propagation_context?
        services = nil
        expect_execution do
            services = run_planners(root_tasks, recursive: recursive)
        end.to_run

        result_tasks = services.map(&:to_task)
        return was_array ? result_tasks : result_tasks.first
    end

    root_task_services, by_handler =
        RunPlanners.setup_planning_handlers(
            self, plan, root_tasks, recursive: recursive
        )
    if by_handler.empty?
        return was_array ? root_task_services : root_task_services.first
    end

    add_expectations do
        all_handlers_finished = false
        achieve(description: "expected all planning handlers to finish") do
            # by_handler == nil is used to indicate that an execute
            # block is pending
            if all_handlers_finished
                all_handlers_finished = false
                if recursive
                    by_handler = nil
                    execute do
                        new_roots = root_task_services.map(&:to_task)
                        root_task_services, by_handler =
                            RunPlanners.setup_planning_handlers(
                                self, plan, new_roots, recursive: true
                            )
                    end
                else
                    by_handler = []
                end
            elsif by_handler && !by_handler.empty?
                execute do
                    all_handlers_finished =
                        by_handler.all? { |handler, _| handler.finished? }
                end
            end

            # by_handler == nil is used to indicate that an execute
            # block is pending
            by_handler&.empty?
        end
    end

    was_array ? root_task_services : root_task_services.first
end