roby 0.7.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +7 -5
- data/Manifest.txt +91 -16
- data/README.txt +24 -24
- data/Rakefile +92 -64
- data/app/config/app.yml +42 -43
- data/app/config/init.rb +26 -0
- data/benchmark/alloc_misc.rb +123 -0
- data/benchmark/discovery_latency.rb +67 -0
- data/benchmark/garbage_collection.rb +48 -0
- data/benchmark/genom.rb +31 -0
- data/benchmark/transactions.rb +62 -0
- data/bin/roby +1 -1
- data/bin/roby-log +16 -6
- data/doc/guide/.gitignore +2 -0
- data/doc/guide/config.yaml +34 -0
- data/doc/guide/ext/init.rb +14 -0
- data/doc/guide/ext/previous_next.rb +40 -0
- data/doc/guide/ext/rdoc_links.rb +33 -0
- data/doc/guide/index.rdoc +16 -0
- data/doc/guide/overview.rdoc +62 -0
- data/doc/guide/plan_modifications.rdoc +67 -0
- data/doc/guide/src/abstraction/achieve_with.page +8 -0
- data/doc/guide/src/abstraction/forwarding.page +8 -0
- data/doc/guide/src/abstraction/hierarchy.page +19 -0
- data/doc/guide/src/abstraction/index.page +28 -0
- data/doc/guide/src/abstraction/task_models.page +13 -0
- data/doc/guide/src/basics.template +6 -0
- data/doc/guide/src/basics/app.page +139 -0
- data/doc/guide/src/basics/code_examples.page +33 -0
- data/doc/guide/src/basics/dry.page +69 -0
- data/doc/guide/src/basics/errors.page +443 -0
- data/doc/guide/src/basics/events.page +179 -0
- data/doc/guide/src/basics/hierarchy.page +275 -0
- data/doc/guide/src/basics/index.page +11 -0
- data/doc/guide/src/basics/log_replay/goForward_1.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_2.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_3.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_4.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_5.png +0 -0
- data/doc/guide/src/basics/log_replay/hierarchy_error_1.png +0 -0
- data/doc/guide/src/basics/log_replay/hierarchy_error_2.png +0 -0
- data/doc/guide/src/basics/log_replay/hierarchy_error_3.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_1.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_2.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_3.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_4.png +0 -0
- data/doc/guide/src/basics/log_replay/roby_log_main_window.png +0 -0
- data/doc/guide/src/basics/log_replay/roby_log_relation_window.png +0 -0
- data/doc/guide/src/basics/log_replay/roby_replay_event_representation.png +0 -0
- data/doc/guide/src/basics/plan_objects.page +71 -0
- data/doc/guide/src/basics/relations_display.page +203 -0
- data/doc/guide/src/basics/roby_cycle_overview.png +0 -0
- data/doc/guide/src/basics/shell.page +102 -0
- data/doc/guide/src/basics/summary.page +32 -0
- data/doc/guide/src/basics/tasks.page +357 -0
- data/doc/guide/src/basics_shell_header.txt +16 -0
- data/doc/guide/src/cycle/cycle-overview.png +0 -0
- data/doc/guide/src/cycle/cycle-overview.svg +208 -0
- data/doc/guide/src/cycle/error_handling.page +168 -0
- data/doc/guide/src/cycle/error_instantaneous_repair.png +0 -0
- data/doc/guide/src/cycle/error_instantaneous_repair.svg +1224 -0
- data/doc/guide/src/cycle/garbage_collection.page +10 -0
- data/doc/guide/src/cycle/index.page +23 -0
- data/doc/guide/src/cycle/propagation.page +154 -0
- data/doc/guide/src/cycle/propagation_diamond.png +0 -0
- data/doc/guide/src/cycle/propagation_diamond.svg +1279 -0
- data/doc/guide/src/default.css +319 -0
- data/doc/guide/src/default.template +74 -0
- data/doc/guide/src/htmldoc.metainfo +20 -0
- data/doc/guide/src/htmldoc.virtual +18 -0
- data/doc/guide/src/images/bodybg.png +0 -0
- data/doc/guide/src/images/contbg.png +0 -0
- data/doc/guide/src/images/footerbg.png +0 -0
- data/doc/guide/src/images/gradient1.png +0 -0
- data/doc/guide/src/images/gradient2.png +0 -0
- data/doc/guide/src/index.page +7 -0
- data/doc/guide/src/introduction/index.page +29 -0
- data/doc/guide/src/introduction/install.page +133 -0
- data/doc/{papers.rdoc → guide/src/introduction/publications.page} +5 -2
- data/doc/{videos.rdoc → guide/src/introduction/videos.page} +4 -2
- data/doc/guide/src/plugins/fault_tolerance.page +44 -0
- data/doc/guide/src/plugins/index.page +11 -0
- data/doc/guide/src/plugins/subsystems.page +45 -0
- data/doc/guide/src/relations/dependency.page +89 -0
- data/doc/guide/src/relations/index.page +12 -0
- data/doc/misc/update_github +24 -0
- data/doc/tutorials/02-GoForward.rdoc +3 -3
- data/ext/graph/graph.cc +46 -0
- data/lib/roby.rb +57 -22
- data/lib/roby/app.rb +132 -112
- data/lib/roby/app/plugins/rake.rb +21 -0
- data/lib/roby/app/rake.rb +0 -7
- data/lib/roby/app/run.rb +1 -1
- data/lib/roby/app/scripts/distributed.rb +1 -2
- data/lib/roby/app/scripts/generate/bookmarks.rb +1 -1
- data/lib/roby/app/scripts/results.rb +2 -1
- data/lib/roby/app/scripts/run.rb +6 -2
- data/lib/roby/app/scripts/shell.rb +11 -11
- data/lib/roby/config.rb +1 -1
- data/lib/roby/decision_control.rb +62 -3
- data/lib/roby/distributed.rb +4 -0
- data/lib/roby/distributed/base.rb +8 -0
- data/lib/roby/distributed/communication.rb +12 -8
- data/lib/roby/distributed/connection_space.rb +61 -44
- data/lib/roby/distributed/distributed_object.rb +1 -1
- data/lib/roby/distributed/notifications.rb +22 -30
- data/lib/roby/distributed/peer.rb +13 -8
- data/lib/roby/distributed/proxy.rb +5 -5
- data/lib/roby/distributed/subscription.rb +4 -4
- data/lib/roby/distributed/transaction.rb +3 -3
- data/lib/roby/event.rb +176 -110
- data/lib/roby/exceptions.rb +12 -4
- data/lib/roby/execution_engine.rb +1604 -0
- data/lib/roby/external_process_task.rb +225 -0
- data/lib/roby/graph.rb +0 -6
- data/lib/roby/interface.rb +221 -137
- data/lib/roby/log/console.rb +5 -3
- data/lib/roby/log/data_stream.rb +94 -16
- data/lib/roby/log/dot.rb +8 -8
- data/lib/roby/log/event_stream.rb +13 -3
- data/lib/roby/log/file.rb +43 -18
- data/lib/roby/log/gui/basic_display_ui.rb +89 -0
- data/lib/roby/log/gui/chronicle_view_ui.rb +90 -0
- data/lib/roby/log/gui/data_displays.rb +4 -5
- data/lib/roby/log/gui/data_displays_ui.rb +146 -0
- data/lib/roby/log/gui/relations.rb +18 -18
- data/lib/roby/log/gui/relations_ui.rb +120 -0
- data/lib/roby/log/gui/relations_view_ui.rb +144 -0
- data/lib/roby/log/gui/replay.rb +41 -13
- data/lib/roby/log/gui/replay_controls.rb +3 -0
- data/lib/roby/log/gui/replay_controls.ui +133 -110
- data/lib/roby/log/gui/replay_controls_ui.rb +249 -0
- data/lib/roby/log/hooks.rb +19 -18
- data/lib/roby/log/logger.rb +7 -6
- data/lib/roby/log/notifications.rb +4 -4
- data/lib/roby/log/plan_rebuilder.rb +20 -22
- data/lib/roby/log/relations.rb +44 -16
- data/lib/roby/log/server.rb +1 -4
- data/lib/roby/log/timings.rb +88 -19
- data/lib/roby/plan-object.rb +135 -11
- data/lib/roby/plan.rb +408 -224
- data/lib/roby/planning/loops.rb +32 -25
- data/lib/roby/planning/model.rb +157 -51
- data/lib/roby/planning/task.rb +47 -20
- data/lib/roby/query.rb +128 -92
- data/lib/roby/relations.rb +254 -136
- data/lib/roby/relations/conflicts.rb +6 -9
- data/lib/roby/relations/dependency.rb +358 -0
- data/lib/roby/relations/ensured.rb +0 -1
- data/lib/roby/relations/error_handling.rb +0 -1
- data/lib/roby/relations/events.rb +0 -2
- data/lib/roby/relations/executed_by.rb +26 -11
- data/lib/roby/relations/planned_by.rb +14 -14
- data/lib/roby/robot.rb +46 -0
- data/lib/roby/schedulers/basic.rb +34 -0
- data/lib/roby/standalone.rb +4 -0
- data/lib/roby/standard_errors.rb +21 -15
- data/lib/roby/state/events.rb +5 -4
- data/lib/roby/support.rb +107 -6
- data/lib/roby/task-operations.rb +23 -19
- data/lib/roby/task.rb +522 -148
- data/lib/roby/task_index.rb +80 -0
- data/lib/roby/test/common.rb +283 -44
- data/lib/roby/test/distributed.rb +53 -37
- data/lib/roby/test/testcase.rb +9 -204
- data/lib/roby/test/tools.rb +3 -3
- data/lib/roby/transactions.rb +154 -111
- data/lib/roby/transactions/proxy.rb +40 -7
- data/manifest.xml +20 -0
- data/plugins/fault_injection/README.txt +0 -3
- data/plugins/fault_injection/Rakefile +2 -8
- data/plugins/fault_injection/app.rb +1 -1
- data/plugins/fault_injection/fault_injection.rb +3 -3
- data/plugins/fault_injection/test/test_fault_injection.rb +19 -25
- data/plugins/subsystems/README.txt +0 -3
- data/plugins/subsystems/Rakefile +2 -7
- data/plugins/subsystems/app.rb +27 -16
- data/plugins/subsystems/test/app/config/init.rb +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +1 -1
- data/plugins/subsystems/test/app/tasks/services.rb +1 -1
- data/plugins/subsystems/test/test_subsystems.rb +23 -16
- data/test/distributed/test_communication.rb +32 -15
- data/test/distributed/test_connection.rb +28 -26
- data/test/distributed/test_execution.rb +59 -54
- data/test/distributed/test_mixed_plan.rb +34 -34
- data/test/distributed/test_plan_notifications.rb +26 -26
- data/test/distributed/test_protocol.rb +57 -48
- data/test/distributed/test_query.rb +11 -7
- data/test/distributed/test_remote_plan.rb +71 -71
- data/test/distributed/test_transaction.rb +50 -47
- data/test/mockups/external_process +28 -0
- data/test/planning/test_loops.rb +163 -119
- data/test/planning/test_model.rb +3 -3
- data/test/planning/test_task.rb +27 -7
- data/test/relations/test_conflicts.rb +3 -3
- data/test/relations/test_dependency.rb +324 -0
- data/test/relations/test_ensured.rb +2 -2
- data/test/relations/test_executed_by.rb +94 -19
- data/test/relations/test_planned_by.rb +11 -9
- data/test/suite_core.rb +6 -3
- data/test/suite_distributed.rb +1 -0
- data/test/suite_planning.rb +1 -0
- data/test/suite_relations.rb +2 -2
- data/test/tasks/test_external_process.rb +126 -0
- data/test/{test_thread_task.rb → tasks/test_thread_task.rb} +17 -20
- data/test/test_bgl.rb +21 -1
- data/test/test_event.rb +229 -155
- data/test/test_exceptions.rb +79 -80
- data/test/test_execution_engine.rb +987 -0
- data/test/test_gui.rb +1 -1
- data/test/test_interface.rb +11 -5
- data/test/test_log.rb +18 -7
- data/test/test_log_server.rb +1 -0
- data/test/test_plan.rb +229 -395
- data/test/test_query.rb +193 -35
- data/test/test_relations.rb +88 -8
- data/test/test_state.rb +55 -37
- data/test/test_support.rb +1 -1
- data/test/test_task.rb +371 -218
- data/test/test_testcase.rb +32 -16
- data/test/test_transactions.rb +211 -170
- data/test/test_transactions_proxy.rb +37 -19
- metadata +169 -71
- data/.gitignore +0 -29
- data/doc/styles/allison.css +0 -314
- data/doc/styles/allison.js +0 -316
- data/doc/styles/allison.rb +0 -276
- data/doc/styles/jamis.rb +0 -593
- data/lib/roby/control.rb +0 -746
- data/lib/roby/executives/simple.rb +0 -30
- data/lib/roby/propagation.rb +0 -562
- data/lib/roby/relations/hierarchy.rb +0 -239
- data/lib/roby/transactions/updates.rb +0 -139
- data/test/relations/test_hierarchy.rb +0 -158
- data/test/test_control.rb +0 -399
- data/test/test_propagation.rb +0 -210
data/lib/roby/planning/task.rb
CHANGED
|
@@ -1,34 +1,50 @@
|
|
|
1
|
-
require 'roby/task'
|
|
2
|
-
require 'roby/relations/planned_by'
|
|
3
|
-
require 'roby/control'
|
|
4
|
-
require 'roby/transactions'
|
|
5
|
-
|
|
6
1
|
module Roby
|
|
7
2
|
# An asynchronous planning task using Ruby threads
|
|
8
3
|
class PlanningTask < Roby::Task
|
|
9
4
|
attr_reader :planner, :transaction
|
|
10
5
|
|
|
11
|
-
arguments :planner_model, :method_name,
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
arguments :planner_model, :method_options, :method_name, :planned_model, :planning_owners
|
|
7
|
+
|
|
8
|
+
def planning_method
|
|
9
|
+
arguments[:planning_method]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.validate_planning_options(options)
|
|
13
|
+
options = options.dup
|
|
14
|
+
if options[:method_name]
|
|
15
|
+
method_name = options[:planning_method] = options[:method_name]
|
|
16
|
+
elsif options[:planning_method].respond_to?(:to_str) || options[:planning_method].respond_to?(:to_sym)
|
|
17
|
+
method_name = options[:method_name] = options[:planning_method].to_s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if !options[:planner_model]
|
|
21
|
+
raise ArgumentError, "missing required argument 'planner_model'"
|
|
22
|
+
elsif !options[:planning_method]
|
|
23
|
+
raise ArgumentError, "missing required argument 'planning_method'"
|
|
24
|
+
elsif !method_name
|
|
25
|
+
if options[:planning_method].kind_of?(Roby::Planning::MethodDefinition)
|
|
26
|
+
method_name = options[:method_name] = options[:planning_method].name
|
|
27
|
+
else
|
|
28
|
+
raise ArgumentError, "the planning_method argument is neither a method object nor a name"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
options[:planned_model] ||= nil
|
|
32
|
+
options[:planning_owners] ||= nil
|
|
33
|
+
options
|
|
34
|
+
end
|
|
14
35
|
|
|
15
36
|
def self.filter_options(options)
|
|
16
37
|
task_options, method_options = Kernel.filter_options options,
|
|
17
38
|
:planner_model => nil,
|
|
18
|
-
:
|
|
39
|
+
:planning_method => nil,
|
|
19
40
|
:method_options => {},
|
|
41
|
+
:method_name => nil, # kept for backward compatibility
|
|
20
42
|
:planned_model => nil,
|
|
21
43
|
:planning_owners => nil
|
|
22
44
|
|
|
23
|
-
|
|
24
|
-
raise ArgumentError, "missing required argument 'planner_model'"
|
|
25
|
-
elsif !task_options[:method_name]
|
|
26
|
-
raise ArgumentError, "missing required argument 'method_name'"
|
|
27
|
-
end
|
|
28
|
-
task_options[:planned_model] ||= nil
|
|
45
|
+
task_options = validate_planning_options(task_options)
|
|
29
46
|
task_options[:method_options] ||= Hash.new
|
|
30
47
|
task_options[:method_options].merge! method_options
|
|
31
|
-
task_options[:planning_owners] ||= nil
|
|
32
48
|
task_options
|
|
33
49
|
end
|
|
34
50
|
|
|
@@ -38,12 +54,18 @@ module Roby
|
|
|
38
54
|
end
|
|
39
55
|
|
|
40
56
|
def planned_model
|
|
41
|
-
arguments[:planned_model] ||=
|
|
57
|
+
arguments[:planned_model] ||= if method_name
|
|
58
|
+
planner_model.model_of(method_name, method_options).returns
|
|
59
|
+
else
|
|
60
|
+
planning_method.returns
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
arguments[:planned_model] ||= Roby::Task
|
|
42
64
|
end
|
|
43
65
|
|
|
44
66
|
|
|
45
67
|
def to_s
|
|
46
|
-
"#{super}[#{
|
|
68
|
+
"#{super}[#{planning_method}:#{method_options}] -> #{@planned_task || "nil"}"
|
|
47
69
|
end
|
|
48
70
|
|
|
49
71
|
def planned_task
|
|
@@ -88,7 +110,11 @@ module Roby
|
|
|
88
110
|
end
|
|
89
111
|
|
|
90
112
|
def planning_thread(context)
|
|
91
|
-
result_task =
|
|
113
|
+
result_task = if planning_method.kind_of?(Roby::Planning::MethodDefinition)
|
|
114
|
+
planner.send(:call_planning_methods, Hash.new, method_options.merge(:context => context), planning_method)
|
|
115
|
+
else
|
|
116
|
+
planner.send(method_name, method_options.merge(:context => context))
|
|
117
|
+
end
|
|
92
118
|
|
|
93
119
|
# Don't replace the planning task with ourselves if the
|
|
94
120
|
# transaction specifies another planning task
|
|
@@ -118,7 +144,7 @@ module Roby
|
|
|
118
144
|
|
|
119
145
|
# Check if the transaction has been committed. If it is not the
|
|
120
146
|
# case, assume that the thread failed
|
|
121
|
-
if transaction.
|
|
147
|
+
if transaction.committed?
|
|
122
148
|
emit :success
|
|
123
149
|
else
|
|
124
150
|
error = begin
|
|
@@ -145,6 +171,7 @@ module Roby
|
|
|
145
171
|
class TransactionProxy < Roby::Transactions::Task
|
|
146
172
|
proxy_for PlanningTask
|
|
147
173
|
def_delegator :@__getobj__, :planner
|
|
174
|
+
def_delegator :@__getobj__, :planning_method
|
|
148
175
|
def_delegator :@__getobj__, :method_name
|
|
149
176
|
def_delegator :@__getobj__, :method_options
|
|
150
177
|
end
|
data/lib/roby/query.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
require 'roby/plan'
|
|
2
|
-
require 'roby/transactions'
|
|
3
|
-
require 'roby/state/information'
|
|
4
|
-
|
|
5
1
|
module Roby
|
|
6
2
|
class Task
|
|
7
3
|
# Returns a TaskMatcher object
|
|
8
|
-
def self.match
|
|
9
|
-
TaskMatcher.new
|
|
4
|
+
def self.match(*args)
|
|
5
|
+
matcher = TaskMatcher.new
|
|
6
|
+
if !args.empty?
|
|
7
|
+
matcher.which_fullfills(*args)
|
|
8
|
+
end
|
|
9
|
+
matcher
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -29,11 +29,17 @@ module Roby
|
|
|
29
29
|
@improved_information = ValueSet.new
|
|
30
30
|
@needed_information = ValueSet.new
|
|
31
31
|
@interruptible = nil
|
|
32
|
+
@parents = Hash.new { |h, k| h[k] = Array.new }
|
|
33
|
+
@children = Hash.new { |h, k| h[k] = Array.new }
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
# Shortcut to set both model and argument
|
|
35
37
|
def which_fullfills(model, arguments = nil)
|
|
36
|
-
with_model(model)
|
|
38
|
+
with_model(model)
|
|
39
|
+
if arguments
|
|
40
|
+
with_model_arguments(arguments)
|
|
41
|
+
end
|
|
42
|
+
self
|
|
37
43
|
end
|
|
38
44
|
|
|
39
45
|
# Find by model
|
|
@@ -47,6 +53,11 @@ module Roby
|
|
|
47
53
|
if !model
|
|
48
54
|
raise ArgumentError, "set model first"
|
|
49
55
|
end
|
|
56
|
+
if model.respond_to?(:to_ary)
|
|
57
|
+
valid_arguments = model.inject(Set.new) { |args, m| args | m.arguments.to_set }
|
|
58
|
+
else
|
|
59
|
+
valid_arguments = model.arguments
|
|
60
|
+
end
|
|
50
61
|
with_arguments(arguments.slice(*model.arguments))
|
|
51
62
|
self
|
|
52
63
|
end
|
|
@@ -132,6 +143,53 @@ module Roby
|
|
|
132
143
|
match_predicates :executable, :abstract, :partially_instanciated, :fully_instanciated,
|
|
133
144
|
:pending, :running, :finished, :success, :failed, :interruptible, :finishing
|
|
134
145
|
|
|
146
|
+
# Helper method for #with_child and #with_parent
|
|
147
|
+
def handle_parent_child_arguments(other_query, relation, relation_options) # :nodoc:
|
|
148
|
+
if !other_query.kind_of?(TaskMatcher) && !other_query.kind_of?(Task)
|
|
149
|
+
if relation.kind_of?(Hash)
|
|
150
|
+
arguments = relation
|
|
151
|
+
relation = (arguments.delete(:relation) || arguments.delete('relation'))
|
|
152
|
+
relation_options = (arguments.delete(:relation_options) || arguments.delete('relation_options'))
|
|
153
|
+
else
|
|
154
|
+
arguments = Hash.new
|
|
155
|
+
end
|
|
156
|
+
other_query = TaskMatcher.which_fullfills(other_query, arguments)
|
|
157
|
+
end
|
|
158
|
+
return relation, [other_query, relation_options]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def with_child(other_query, relation = nil, relation_options = nil)
|
|
162
|
+
relation, spec = handle_parent_child_arguments(other_query, relation, relation_options)
|
|
163
|
+
@children[relation] << spec
|
|
164
|
+
self
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def with_parent(other_query, relation = nil, relation_options = nil)
|
|
168
|
+
relation, spec = handle_parent_child_arguments(other_query, relation, relation_options)
|
|
169
|
+
@parents[relation] << spec
|
|
170
|
+
self
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Helper method for handling parent/child matches in #===
|
|
174
|
+
def handle_parent_child_match(task, match_spec) # :nodoc:
|
|
175
|
+
relation, matchers = *match_spec
|
|
176
|
+
return false if !relation && task.relations.empty?
|
|
177
|
+
for match_spec in matchers
|
|
178
|
+
m, relation_options = *match_spec
|
|
179
|
+
if relation
|
|
180
|
+
if !yield(relation, m, relation_options)
|
|
181
|
+
return false
|
|
182
|
+
end
|
|
183
|
+
else
|
|
184
|
+
result = task.relations.any? do |rel|
|
|
185
|
+
yield(rel, m, relation_options)
|
|
186
|
+
end
|
|
187
|
+
return false if !result
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
true
|
|
191
|
+
end
|
|
192
|
+
|
|
135
193
|
# True if +task+ matches all the criteria defined on this object.
|
|
136
194
|
def ===(task)
|
|
137
195
|
return unless task.kind_of?(Roby::Task)
|
|
@@ -142,6 +200,22 @@ module Roby
|
|
|
142
200
|
return unless task.arguments.slice(*arguments.keys) == arguments
|
|
143
201
|
end
|
|
144
202
|
|
|
203
|
+
for parent_spec in @parents
|
|
204
|
+
result = handle_parent_child_match(task, parent_spec) do |relation, m, relation_options|
|
|
205
|
+
task.enum_parent_objects(relation).
|
|
206
|
+
any? { |parent| m === parent && (!relation_options || relation_options === parent[task, relation]) }
|
|
207
|
+
end
|
|
208
|
+
return false if !result
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
for child_spec in @children
|
|
212
|
+
result = handle_parent_child_match(task, child_spec) do |relation, m, relation_options|
|
|
213
|
+
task.enum_child_objects(relation).
|
|
214
|
+
any? { |child| m === child && (!relation_options || relation_options === task[child, relation]) }
|
|
215
|
+
end
|
|
216
|
+
return false if !result
|
|
217
|
+
end
|
|
218
|
+
|
|
145
219
|
for info in improved_information
|
|
146
220
|
return false if !task.improves?(info)
|
|
147
221
|
end
|
|
@@ -159,15 +233,19 @@ module Roby
|
|
|
159
233
|
true
|
|
160
234
|
end
|
|
161
235
|
|
|
162
|
-
STATE_PREDICATES = [:pending?, :running?, :finished?, :success?, :failed?].to_value_set
|
|
163
|
-
|
|
164
236
|
# Filters the tasks in +initial_set+ by using the information in
|
|
165
237
|
# +task_index+, and returns the result. The resulting set must
|
|
166
238
|
# include all tasks in +initial_set+ which match with #===, but can
|
|
167
239
|
# include tasks which do not match #===
|
|
168
240
|
def filter(initial_set, task_index)
|
|
169
241
|
if model
|
|
170
|
-
|
|
242
|
+
if model.respond_to?(:to_ary)
|
|
243
|
+
for m in model
|
|
244
|
+
initial_set &= task_index.by_model[m]
|
|
245
|
+
end
|
|
246
|
+
else
|
|
247
|
+
initial_set &= task_index.by_model[model]
|
|
248
|
+
end
|
|
171
249
|
end
|
|
172
250
|
|
|
173
251
|
if !owners.empty?
|
|
@@ -180,11 +258,11 @@ module Roby
|
|
|
180
258
|
end
|
|
181
259
|
end
|
|
182
260
|
|
|
183
|
-
for pred in (predicates & STATE_PREDICATES)
|
|
261
|
+
for pred in (predicates & TaskIndex::STATE_PREDICATES)
|
|
184
262
|
initial_set &= task_index.by_state[pred]
|
|
185
263
|
end
|
|
186
264
|
|
|
187
|
-
for pred in (neg_predicates & STATE_PREDICATES)
|
|
265
|
+
for pred in (neg_predicates & TaskIndex::STATE_PREDICATES)
|
|
188
266
|
initial_set -= task_index.by_state[pred]
|
|
189
267
|
end
|
|
190
268
|
|
|
@@ -193,7 +271,9 @@ module Roby
|
|
|
193
271
|
|
|
194
272
|
# Enumerates all tasks of +plan+ which match this TaskMatcher object
|
|
195
273
|
def each(plan, &block)
|
|
196
|
-
|
|
274
|
+
plan.each_task do |t|
|
|
275
|
+
yield(t) if self === t
|
|
276
|
+
end
|
|
197
277
|
self
|
|
198
278
|
end
|
|
199
279
|
|
|
@@ -217,15 +297,36 @@ module Roby
|
|
|
217
297
|
class Query < TaskMatcher
|
|
218
298
|
# The plan this query acts on
|
|
219
299
|
attr_reader :plan
|
|
300
|
+
# Search scope for queries on transactions. If equal to :local, the
|
|
301
|
+
# query will apply only on the scope of the searched transaction,
|
|
302
|
+
# otherwise it applies on a virtual plan that is the result of the
|
|
303
|
+
# transaction stack being applied.
|
|
304
|
+
#
|
|
305
|
+
# The default is :global.
|
|
306
|
+
#
|
|
307
|
+
# See #local_scope and #global_scope
|
|
308
|
+
attr_reader :scope
|
|
220
309
|
|
|
221
310
|
# Create a query object on the given plan
|
|
222
311
|
def initialize(plan)
|
|
312
|
+
@scope = :global
|
|
223
313
|
@plan = plan
|
|
224
314
|
super()
|
|
225
315
|
@plan_predicates = Array.new
|
|
226
316
|
@neg_plan_predicates = Array.new
|
|
227
317
|
end
|
|
228
318
|
|
|
319
|
+
# Changes the scope of this query. See #scope.
|
|
320
|
+
def local_scope; @scope = :local end
|
|
321
|
+
# Changes the scope of this query. See #scope.
|
|
322
|
+
def global_scope; @scope = :global end
|
|
323
|
+
|
|
324
|
+
# Changes the plan this query works on
|
|
325
|
+
def plan=(new_plan)
|
|
326
|
+
reset
|
|
327
|
+
@plan = new_plan
|
|
328
|
+
end
|
|
329
|
+
|
|
229
330
|
# The set of tasks which match in plan. This is a cached value, so use
|
|
230
331
|
# #reset to actually recompute this set.
|
|
231
332
|
def result_set
|
|
@@ -304,82 +405,6 @@ module Roby
|
|
|
304
405
|
include Enumerable
|
|
305
406
|
end
|
|
306
407
|
|
|
307
|
-
# TaskIndex objects are used to maintain a set of tasks as classified sets,
|
|
308
|
-
# speeding up query operations. See Plan#task_index.
|
|
309
|
-
class TaskIndex
|
|
310
|
-
# A model => ValueSet map of the tasks for each model
|
|
311
|
-
attr_reader :by_model
|
|
312
|
-
# A state => ValueSet map of tasks given their state. The state is
|
|
313
|
-
# a symbol in [:pending, :starting, :running, :finishing,
|
|
314
|
-
# :finished]
|
|
315
|
-
attr_reader :by_state
|
|
316
|
-
# A peer => ValueSet map of tasks given their owner.
|
|
317
|
-
attr_reader :by_owner
|
|
318
|
-
# The set of tasks which have an event which is being repaired
|
|
319
|
-
attr_reader :repaired_tasks
|
|
320
|
-
|
|
321
|
-
def initialize
|
|
322
|
-
@by_model = Hash.new { |h, k| h[k] = ValueSet.new }
|
|
323
|
-
@by_state = Hash.new
|
|
324
|
-
TaskMatcher::STATE_PREDICATES.each do |state_name|
|
|
325
|
-
by_state[state_name] = ValueSet.new
|
|
326
|
-
end
|
|
327
|
-
@by_owner = Hash.new
|
|
328
|
-
@task_state = Hash.new
|
|
329
|
-
@repaired_tasks = ValueSet.new
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
# Add a new task to this index
|
|
333
|
-
def add(task)
|
|
334
|
-
for klass in task.model.ancestors
|
|
335
|
-
by_model[klass] << task
|
|
336
|
-
end
|
|
337
|
-
by_state[:pending?] << task
|
|
338
|
-
for owner in task.owners
|
|
339
|
-
add_owner(task, owner)
|
|
340
|
-
end
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
# Updates the index to reflect that +new_owner+ now owns +task+
|
|
344
|
-
def add_owner(task, new_owner)
|
|
345
|
-
(by_owner[new_owner] ||= ValueSet.new) << task
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
# Updates the index to reflect that +peer+ no more owns +task+
|
|
349
|
-
def remove_owner(task, peer)
|
|
350
|
-
if set = by_owner[peer]
|
|
351
|
-
set.delete(task)
|
|
352
|
-
if set.empty?
|
|
353
|
-
by_owner.delete(peer)
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
# Updates the index to reflect a change of state for +task+
|
|
359
|
-
def set_state(task, new_state)
|
|
360
|
-
for state_set in by_state
|
|
361
|
-
state_set.last.delete(task)
|
|
362
|
-
end
|
|
363
|
-
by_state[new_state] << task
|
|
364
|
-
if new_state == :success? || new_state == :failed?
|
|
365
|
-
by_state[:finished?] << task
|
|
366
|
-
end
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
# Remove all references of +task+ from the index.
|
|
370
|
-
def remove(task)
|
|
371
|
-
for klass in task.model.ancestors
|
|
372
|
-
by_model[klass].delete(task)
|
|
373
|
-
end
|
|
374
|
-
for state_set in by_state
|
|
375
|
-
state_set.last.delete(task)
|
|
376
|
-
end
|
|
377
|
-
for owner in task.owners
|
|
378
|
-
remove_owner(task, owner)
|
|
379
|
-
end
|
|
380
|
-
end
|
|
381
|
-
end
|
|
382
|
-
|
|
383
408
|
# This task combines multiple task matching predicates through a OR boolean
|
|
384
409
|
# operator.
|
|
385
410
|
class OrTaskMatcher < TaskMatcher
|
|
@@ -472,6 +497,15 @@ module Roby
|
|
|
472
497
|
q
|
|
473
498
|
end
|
|
474
499
|
|
|
500
|
+
# Starts a local query on this plan
|
|
501
|
+
#
|
|
502
|
+
# See Query#scope
|
|
503
|
+
def find_local_tasks(*args, &block)
|
|
504
|
+
query = find_tasks(*args, &block)
|
|
505
|
+
query.local_scope
|
|
506
|
+
query
|
|
507
|
+
end
|
|
508
|
+
|
|
475
509
|
# Called by TaskMatcher#result_set and Query#result_set to get the set
|
|
476
510
|
# of tasks matching +matcher+
|
|
477
511
|
def query_result_set(matcher)
|
|
@@ -563,9 +597,11 @@ module Roby
|
|
|
563
597
|
# tasks matching it. The two sets are disjoint.
|
|
564
598
|
def query_result_set(matcher)
|
|
565
599
|
plan_set = ValueSet.new
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
600
|
+
if matcher.scope == :global
|
|
601
|
+
for task in plan.query_result_set(matcher)
|
|
602
|
+
plan_set << task unless self[task, false]
|
|
603
|
+
end
|
|
604
|
+
end
|
|
569
605
|
|
|
570
606
|
transaction_set = super
|
|
571
607
|
[plan_set, transaction_set]
|
data/lib/roby/relations.rb
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
require 'roby/support'
|
|
2
|
-
require 'roby/graph'
|
|
3
|
-
|
|
4
1
|
module Roby
|
|
5
2
|
# This exception is raised when an edge is being added in a DAG, while this
|
|
6
3
|
# edge would create a cycle.
|
|
7
4
|
class CycleFoundError < RuntimeError; end
|
|
8
5
|
|
|
9
|
-
# Base support for relations. It is mixed
|
|
10
|
-
#
|
|
6
|
+
# Base support for relations. It is mixed in objects on which a
|
|
7
|
+
# RelationSpace applies on, like Task for TaskStructure and EventGenerator
|
|
8
|
+
# for EventStructure.
|
|
9
|
+
#
|
|
10
|
+
# See also the definition of RelationGraph#add_relation and
|
|
11
|
+
# RelationGraph#remove_relation for the possibility to define hooks that
|
|
12
|
+
# get called when a new edge involving +self+ as a vertex gets added and
|
|
13
|
+
# removed
|
|
11
14
|
module DirectedRelationSupport
|
|
12
15
|
include BGL::Vertex
|
|
13
16
|
|
|
@@ -19,8 +22,20 @@ module Roby
|
|
|
19
22
|
alias :each_relation :each_graph
|
|
20
23
|
alias :clear_relations :clear_vertex
|
|
21
24
|
|
|
25
|
+
##
|
|
26
|
+
# :method: enum_relations => enumerator
|
|
27
|
+
# Returns an Enumerator object for the set of relations this object is
|
|
28
|
+
# included in. The same enumerator instance is always returned.
|
|
22
29
|
cached_enum("graph", "relations", false)
|
|
30
|
+
##
|
|
31
|
+
# :method: enum_parent_objects(relation) => enumerator
|
|
32
|
+
# Returns an Enumerator object for the set of parents this object has
|
|
33
|
+
# in +relation+. The same enumerator instance is always returned.
|
|
23
34
|
cached_enum("parent_object", "parent_objects", true)
|
|
35
|
+
##
|
|
36
|
+
# :method: enum_child_objects(relation) => enumerator
|
|
37
|
+
# Returns an Enumerator object for the set of children this object has
|
|
38
|
+
# in +relation+. The same enumerator instance is always returned.
|
|
24
39
|
cached_enum("child_object", "child_objects", true)
|
|
25
40
|
|
|
26
41
|
# The array of relations this object is part of
|
|
@@ -65,24 +80,9 @@ module Roby
|
|
|
65
80
|
parent.add_child_object(self, relation, info)
|
|
66
81
|
end
|
|
67
82
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# super if defined? super
|
|
72
|
-
# end
|
|
73
|
-
# Hook called after a new child has been added in the +relation+ relation
|
|
74
|
-
# def added_child_object(child, relations, info)
|
|
75
|
-
# child.added_parent_object(self, relation, info)
|
|
76
|
-
# super if defined? super
|
|
77
|
-
# end
|
|
78
|
-
|
|
79
|
-
# Hook called after a new parent has been added in the +relation+ relation
|
|
80
|
-
#def added_parent_object(parent, relation, info); super if defined? super end
|
|
81
|
-
## Hook called after a new parent is being added in the +relation+ relation
|
|
82
|
-
#def adding_parent_object(parent, relation, info); super if defined? super end
|
|
83
|
-
|
|
84
|
-
# Remove the relation between +self+ and +child+. If +relation+ is
|
|
85
|
-
# given, remove only a relations in this relation kind.
|
|
83
|
+
# Remove all edges in which +self+ is the source and +child+ the
|
|
84
|
+
# target. If +relation+ is given, it removes only the edge in that
|
|
85
|
+
# relation graph.
|
|
86
86
|
def remove_child_object(child, relation = nil)
|
|
87
87
|
check_is_relation(relation)
|
|
88
88
|
apply_selection(relation, (relation || enum_relations)) do |relation|
|
|
@@ -90,8 +90,8 @@ module Roby
|
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
# Remove all edges in which +self+ is the source. If +relation+
|
|
94
|
+
# is given, it removes only the edges in that relation graph.
|
|
95
95
|
def remove_children(relation = nil)
|
|
96
96
|
apply_selection(relation, (relation || enum_relations)) do |relation|
|
|
97
97
|
self.each_child_object(relation) do |child|
|
|
@@ -100,13 +100,15 @@ module Roby
|
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
# Remove all edges in which +child+ is the source and +self+ the
|
|
104
|
+
# target. If +relation+ is given, it removes only the edge in that
|
|
105
|
+
# relation graph.
|
|
105
106
|
def remove_parent_object(parent, relation = nil)
|
|
106
107
|
parent.remove_child_object(self, relation)
|
|
107
108
|
end
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
|
|
110
|
+
# Remove all edges in which +self+ is the target. If +relation+
|
|
111
|
+
# is given, it removes only the edges in that relation graph.
|
|
110
112
|
def remove_parents(relation = nil)
|
|
111
113
|
check_is_relation(relation)
|
|
112
114
|
apply_selection(relation, (relation || enum_relations)) do |relation|
|
|
@@ -116,24 +118,10 @@ module Roby
|
|
|
116
118
|
end
|
|
117
119
|
end
|
|
118
120
|
|
|
119
|
-
# Hook called after a parent has been removed
|
|
120
|
-
# def removing_parent_object(parent, relation); super if defined? super end
|
|
121
|
-
# Hook called after a child has been removed
|
|
122
|
-
# def removing_child_object(child, relation)
|
|
123
|
-
# child.removing_parent_object(self, relation)
|
|
124
|
-
# super if defined? super
|
|
125
|
-
# end
|
|
126
|
-
|
|
127
|
-
# Hook called after a parent has been removed
|
|
128
|
-
# def removed_parent_object(parent, relation); super if defined? super end
|
|
129
|
-
# Hook called after a child has been removed
|
|
130
|
-
# def removed_child_object(child, relation)
|
|
131
|
-
# child.removed_parent_object(self, relation)
|
|
132
|
-
# super if defined? super
|
|
133
|
-
# end
|
|
134
|
-
|
|
135
121
|
# Remove all relations that point to or come from +to+ If +to+ is nil,
|
|
136
|
-
# it removes all
|
|
122
|
+
# it removes all edges in which +self+ is involved.
|
|
123
|
+
#
|
|
124
|
+
# If +relation+ is not nil, only edges of that relation graph are removed.
|
|
137
125
|
def remove_relations(to = nil, relation = nil)
|
|
138
126
|
check_is_relation(relation)
|
|
139
127
|
if to
|
|
@@ -172,16 +160,28 @@ module Roby
|
|
|
172
160
|
end
|
|
173
161
|
|
|
174
162
|
# This class manages the graph defined by an object relation in Roby.
|
|
163
|
+
#
|
|
175
164
|
# Relation graphs are managed in hierarchies (for instance, in
|
|
176
165
|
# EventStructure, Precedence is a superset of CausalLink, and CausalLink a
|
|
177
166
|
# superset of both Forwarding and Signal). In this hierarchy, at each
|
|
178
167
|
# level, an edge cannot be present in more than one graph. Nonetheless, it
|
|
179
168
|
# is possible for a parent relation to have an edge which is present in
|
|
180
169
|
# none of its children.
|
|
170
|
+
#
|
|
171
|
+
# Each relation define two things:
|
|
172
|
+
# * a graph, which is represented by the RelationGraph instance itself
|
|
173
|
+
# * support methods that are defined on the vertices of the relation. They
|
|
174
|
+
# allow to manage the vertex in its relations easily. Those methods are
|
|
175
|
+
# defined in a separate module (see #support)
|
|
176
|
+
#
|
|
177
|
+
# In general, relations are part of a RelationSpace instance, which manages
|
|
178
|
+
# the set of relations whose vertices are of the same kind (for instance
|
|
179
|
+
# TaskStructure manages all relations whose vertices are Task instances).
|
|
180
|
+
# In these cases, RelationSpace#relation allow to define new relations easily.
|
|
181
181
|
class RelationGraph < BGL::Graph
|
|
182
182
|
# The relation name
|
|
183
183
|
attr_reader :name
|
|
184
|
-
# The relation parent if any
|
|
184
|
+
# The relation parent (if any). See #superset_of.
|
|
185
185
|
attr_accessor :parent
|
|
186
186
|
# The set of graphs
|
|
187
187
|
attr_reader :subsets
|
|
@@ -194,17 +194,18 @@ module Roby
|
|
|
194
194
|
# if the graph is a DAG. If true, add_relation will check that
|
|
195
195
|
# no cycle is created
|
|
196
196
|
# +subsets+::
|
|
197
|
-
# a set of RelationGraph objects that are children of this
|
|
198
|
-
|
|
197
|
+
# a set of RelationGraph objects that are children of this one.
|
|
198
|
+
# See #superset_of.
|
|
199
199
|
# +distributed+::
|
|
200
200
|
# if this relation graph should be seen by remote hosts
|
|
201
201
|
def initialize(name, options = {})
|
|
202
|
-
@name
|
|
202
|
+
@name = name
|
|
203
203
|
@options = options
|
|
204
204
|
@subsets = ValueSet.new
|
|
205
205
|
@distribute = options[:distribute]
|
|
206
|
-
@dag
|
|
207
|
-
@weak
|
|
206
|
+
@dag = options[:dag]
|
|
207
|
+
@weak = options[:weak]
|
|
208
|
+
@embeds_info = !options[:noinfo]
|
|
208
209
|
|
|
209
210
|
if options[:subsets]
|
|
210
211
|
options[:subsets].each(&method(:superset_of))
|
|
@@ -220,17 +221,38 @@ module Roby
|
|
|
220
221
|
# break cross-relations cycles (cycles which exist in the graph union
|
|
221
222
|
# of all the relation graphs).
|
|
222
223
|
attr_predicate :weak
|
|
224
|
+
# If this relation embeds some additional information
|
|
225
|
+
attr_predicate :embeds_info?
|
|
223
226
|
|
|
224
227
|
def to_s; name end
|
|
225
228
|
|
|
226
229
|
# True if this relation does not have a parent
|
|
227
230
|
def root_relation?; !parent end
|
|
228
231
|
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
232
|
+
# Remove +vertex+ from this graph. It removes all relations that
|
|
233
|
+
# +vertex+ is part of, and calls the corresponding hooks
|
|
234
|
+
def remove(vertex)
|
|
235
|
+
vertex.remove_relations(nil, self)
|
|
236
|
+
super
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Add an edge between +from+ and +to+. The relation is added on all
|
|
240
|
+
# parent relation graphs as well. If #dag? is true on +self+ or on one
|
|
241
|
+
# of its parents, the method will raise CycleFoundError in case the new
|
|
242
|
+
# edge would create a cycle.
|
|
243
|
+
#
|
|
244
|
+
# If +from+ or +to+ define the following hooks:
|
|
245
|
+
# adding_parent_object(parent, relations, info)
|
|
246
|
+
# adding_child_object(child, relations, info)
|
|
247
|
+
# added_parent_object(parent, relations, info)
|
|
248
|
+
# added_child_object(child, relations, info)
|
|
249
|
+
#
|
|
250
|
+
# then these hooks get respectively called before and after having
|
|
251
|
+
# added the relation, where +relations+ is the set of RelationGraph
|
|
252
|
+
# instances where the edge has been added. It can be either [+self+] if
|
|
253
|
+
# the edge does not already exist in it, or [+self+, +parent+,
|
|
254
|
+
# <tt>parent.parent</tt>, ...] if the parent, grandparent, ... graphs
|
|
255
|
+
# do not include the edge either.
|
|
234
256
|
def add_relation(from, to, info = nil)
|
|
235
257
|
# Get the toplevel DAG in our relation hierarchy. We only test for the
|
|
236
258
|
# DAG property on this one, as it is the union of all its children
|
|
@@ -239,37 +261,25 @@ module Roby
|
|
|
239
261
|
rel = self
|
|
240
262
|
while rel
|
|
241
263
|
top_dag = rel if rel.dag?
|
|
242
|
-
|
|
264
|
+
if !rel.linked?(from, to)
|
|
265
|
+
new_relations << rel
|
|
266
|
+
end
|
|
243
267
|
rel = rel.parent
|
|
244
268
|
end
|
|
245
269
|
if top_dag && !top_dag.linked?(from, to) && top_dag.reachable?(to, from)
|
|
246
270
|
raise CycleFoundError, "cannot add a #{from} -> #{to} relation since it would create a cycle"
|
|
247
271
|
end
|
|
248
272
|
|
|
249
|
-
# Now
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
changed_info = [new_relations.pop]
|
|
260
|
-
|
|
261
|
-
while !new_relations.empty?
|
|
262
|
-
if new_relations.last.linked?(from, to)
|
|
263
|
-
changed_info << new_relations.pop
|
|
264
|
-
else
|
|
265
|
-
break
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
for rel in changed_info
|
|
270
|
-
from[to, rel] = info
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
|
+
# Now check that we're not changing the edge info. This is ignored
|
|
274
|
+
# if +self+ has the noinfo flag set.
|
|
275
|
+
if linked?(from, to)
|
|
276
|
+
if !(old_info = from[to, self]).nil?
|
|
277
|
+
if old_info != info && !(info = merge_info(from, to, old_info, info))
|
|
278
|
+
raise ArgumentError, "trying to change edge information in #{self} for #{from} => #{to}: old was #{old_info} and new is #{info}"
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
from[to, self] = info
|
|
282
|
+
end
|
|
273
283
|
|
|
274
284
|
unless new_relations.empty?
|
|
275
285
|
if from.respond_to?(:adding_child_object)
|
|
@@ -280,7 +290,7 @@ module Roby
|
|
|
280
290
|
end
|
|
281
291
|
|
|
282
292
|
for rel in new_relations
|
|
283
|
-
rel.__bgl_link(from, to, info)
|
|
293
|
+
rel.__bgl_link(from, to, (info if self == rel))
|
|
284
294
|
end
|
|
285
295
|
|
|
286
296
|
if from.respond_to?(:added_child_object)
|
|
@@ -292,22 +302,43 @@ module Roby
|
|
|
292
302
|
end
|
|
293
303
|
end
|
|
294
304
|
|
|
305
|
+
def merge_info(from, to, old, new)
|
|
306
|
+
end
|
|
307
|
+
|
|
295
308
|
alias :__bgl_link :link
|
|
296
309
|
# Reimplemented from BGL::Graph. Unlike this implementation, it is
|
|
297
310
|
# possible to add an already existing edge if the +info+ parameter
|
|
298
311
|
# matches.
|
|
299
312
|
def link(from, to, info)
|
|
300
313
|
if linked?(from, to)
|
|
301
|
-
|
|
302
|
-
|
|
314
|
+
old_info = from[to, self]
|
|
315
|
+
if info != old_info
|
|
316
|
+
if info = merge_info(from, to, old_info, info)
|
|
317
|
+
from[to, self] = info
|
|
318
|
+
return
|
|
319
|
+
else
|
|
320
|
+
raise ArgumentError, "trying to change edge information"
|
|
321
|
+
end
|
|
303
322
|
end
|
|
304
323
|
return
|
|
305
324
|
end
|
|
306
|
-
super
|
|
325
|
+
super(from, to, info)
|
|
307
326
|
end
|
|
308
327
|
|
|
309
|
-
|
|
310
|
-
|
|
328
|
+
# Remove the relation between +from+ and +to+, in this graph and in its
|
|
329
|
+
# parent graphs as well.
|
|
330
|
+
#
|
|
331
|
+
# If +from+ or +to+ define the following hooks:
|
|
332
|
+
# removing_parent_object(parent, relations)
|
|
333
|
+
# removing_child_object(child, relations)
|
|
334
|
+
# removed_parent_object(parent, relations)
|
|
335
|
+
# removed_child_object(child, relations)
|
|
336
|
+
#
|
|
337
|
+
# then these hooks get respectively called once before and once after
|
|
338
|
+
# having removed the relation, where +relations+ is the set of
|
|
339
|
+
# RelationGraph instances where the edge has been removed. It is always
|
|
340
|
+
# <tt>[self, parent, parent.parent, ...]</tt> up to the root relation
|
|
341
|
+
# which is a superset of +self+.
|
|
311
342
|
def remove_relation(from, to)
|
|
312
343
|
rel = self
|
|
313
344
|
relations = []
|
|
@@ -337,17 +368,31 @@ module Roby
|
|
|
337
368
|
|
|
338
369
|
# Returns true if +relation+ is included in this relation (i.e. it is
|
|
339
370
|
# either the same relation or one of its children)
|
|
371
|
+
#
|
|
372
|
+
# See also #superset_of
|
|
340
373
|
def subset?(relation)
|
|
341
374
|
self.eql?(relation) || subsets.any? { |subrel| subrel.subset?(relation) }
|
|
342
375
|
end
|
|
343
376
|
|
|
344
377
|
# Returns +true+ if there is an edge +source+ -> +target+ in this graph
|
|
345
378
|
# or in one of its parents
|
|
346
|
-
|
|
379
|
+
#
|
|
380
|
+
# See #superset_of for a description of the parent mechanism
|
|
381
|
+
def linked_in_hierarchy?(source, target)
|
|
347
382
|
linked?(source, target) || (parent.linked?(source, target) if parent)
|
|
348
383
|
end
|
|
349
384
|
|
|
350
|
-
# Declare that +
|
|
385
|
+
# Declare that +self+ is a superset of +relation+. Once this is done,
|
|
386
|
+
# the system manages two constraints:
|
|
387
|
+
# * all new relations added in +relation+ are also added in +self+
|
|
388
|
+
# * it is not allowed for an edge to exist in two different subsets of
|
|
389
|
+
# +self+
|
|
390
|
+
# * of course, if +self+ is a DAG, then in effect +relation+ is constrained
|
|
391
|
+
# to be one as well.
|
|
392
|
+
#
|
|
393
|
+
# One single graph can be the superset of multiple subgraphs (these are
|
|
394
|
+
# stored in the #subsets attribute), but one graph can have only one
|
|
395
|
+
# parent (#parent).
|
|
351
396
|
def superset_of(relation)
|
|
352
397
|
relation.each_edge do |source, target, info|
|
|
353
398
|
if linked_in_hierarchy?(source, target)
|
|
@@ -368,27 +413,44 @@ module Roby
|
|
|
368
413
|
attr_accessor :support
|
|
369
414
|
end
|
|
370
415
|
|
|
371
|
-
# A relation space is a module which handles a list of relations
|
|
372
|
-
# applies them to a set of classes.
|
|
373
|
-
#
|
|
374
|
-
#
|
|
416
|
+
# A relation space is a module which handles a list of relations
|
|
417
|
+
# (RelationGraph instances) and applies them to a set of classes.
|
|
418
|
+
# For instance, the TaskStructure relation space is defined by
|
|
419
|
+
# TaskStructure = RelationSpace(Task)
|
|
420
|
+
#
|
|
421
|
+
# See the files in roby/relations to see example definitions of new
|
|
422
|
+
# relations
|
|
423
|
+
#
|
|
424
|
+
# Use RelationSpace#relation allow to define a new relation in a given
|
|
425
|
+
# space. For instance, one can either do
|
|
426
|
+
#
|
|
427
|
+
# TaskStructure.relation :NewRelation
|
|
375
428
|
#
|
|
376
|
-
#
|
|
429
|
+
# or
|
|
430
|
+
#
|
|
431
|
+
# module TaskStructure
|
|
432
|
+
# relation :NewRelation
|
|
433
|
+
# end
|
|
434
|
+
#
|
|
435
|
+
# This relation can then be referenced by
|
|
436
|
+
# <tt>TaskStructure::NewRelation</tt>
|
|
377
437
|
class RelationSpace < Module
|
|
378
438
|
# The set of relations included in this relation space
|
|
379
439
|
attr_reader :relations
|
|
380
|
-
# The set of
|
|
440
|
+
# The set of classes on which the relations have been applied
|
|
381
441
|
attr_reader :applied
|
|
382
442
|
|
|
383
|
-
def initialize
|
|
443
|
+
def initialize # :nodoc:
|
|
384
444
|
@relations = Array.new
|
|
385
445
|
@applied = Array.new
|
|
386
446
|
super
|
|
387
447
|
end
|
|
388
448
|
|
|
389
|
-
# This relation applies on klass
|
|
449
|
+
# This relation applies on +klass+. It mainly means that a relation
|
|
390
450
|
# defined on this RelationSpace will define the relation-access methods
|
|
391
|
-
# and include its support module (if any) in +klass+.
|
|
451
|
+
# and include its support module (if any) in +klass+. Note that the
|
|
452
|
+
# DirectedRelationSupport module is automatically included in +klass+
|
|
453
|
+
# as well.
|
|
392
454
|
def apply_on(klass)
|
|
393
455
|
klass.include DirectedRelationSupport
|
|
394
456
|
each_relation do |graph|
|
|
@@ -405,18 +467,20 @@ module Roby
|
|
|
405
467
|
end
|
|
406
468
|
end
|
|
407
469
|
|
|
408
|
-
# Yields the root relations that are defined on this space
|
|
470
|
+
# Yields the root relations that are defined on this space. A relation
|
|
471
|
+
# is a root relation when it has no parent relation (i.e. it is the
|
|
472
|
+
# subset of no other relations).
|
|
409
473
|
def each_root_relation
|
|
410
474
|
for rel in relations
|
|
411
475
|
yield(rel) unless rel.parent
|
|
412
476
|
end
|
|
413
477
|
end
|
|
414
478
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
479
|
+
# Returns the set of objects that are reachable from +obj+ in the union
|
|
480
|
+
# graph of all the relations defined in this space. In other words, it
|
|
481
|
+
# returns the set of vertices so that it exists a path starting at
|
|
482
|
+
# +obj+ and ending at +v+ in the union graph of all the relations.
|
|
483
|
+
#
|
|
420
484
|
# If +strict+ is true, +obj+ is not included in the returned set
|
|
421
485
|
def children_of(obj, strict = true, relations = nil)
|
|
422
486
|
set = compute_children_of([obj].to_value_set, relations || self.relations)
|
|
@@ -445,40 +509,77 @@ module Roby
|
|
|
445
509
|
|
|
446
510
|
# Defines a relation in this relation space. This defines a relation
|
|
447
511
|
# graph, and various iteration methods on the vertices. If a block is
|
|
448
|
-
# given, it defines a set of functions which should be
|
|
449
|
-
# vertex objects.
|
|
512
|
+
# given, it defines a set of functions which should additionally be
|
|
513
|
+
# defined on the vertex objects.
|
|
514
|
+
#
|
|
515
|
+
# The valid options are:
|
|
450
516
|
#
|
|
451
|
-
# = Options
|
|
452
517
|
# child_name::
|
|
453
518
|
# define a <tt>each_#{child_name}</tt> method to iterate
|
|
454
519
|
# on the vertex children. Uses the relation name by default (a Child
|
|
455
520
|
# relation would define a <tt>each_child</tt> method)
|
|
456
521
|
# parent_name::
|
|
457
522
|
# define a <tt>each_#{parent_name}</tt> method to iterate
|
|
458
|
-
# on the
|
|
459
|
-
# subsets:: a list of subgraphs. See RelationGraph#superset_of
|
|
523
|
+
# on the parent vertices. If none is given, no method is defined.
|
|
524
|
+
# subsets:: a list of subgraphs. See RelationGraph#superset_of [empty set by default]
|
|
460
525
|
# noinfo::
|
|
461
|
-
#
|
|
526
|
+
# wether the relation embeds some additional information. If false,
|
|
462
527
|
# the child iterator method (<tt>each_#{child_name}</tt>) will yield (child,
|
|
463
|
-
# info) instead of only child [false]
|
|
464
|
-
# graph:: the relation graph class
|
|
465
|
-
# distribute:: if true, the relation can be seen by remote peers [true]
|
|
528
|
+
# info) instead of only child [false by default]
|
|
529
|
+
# graph:: the relation graph class [RelationGraph by default]
|
|
530
|
+
# distribute:: if true, the relation can be seen by remote peers [true by default]
|
|
466
531
|
# single_child::
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
532
|
+
# if the relations accepts only one child per vertex. If this option
|
|
533
|
+
# is set, defines a <tt>#{child_name}</tt> method which returns the
|
|
534
|
+
# only child (or nil if there is no child at all) [false by default]
|
|
535
|
+
# dag::
|
|
536
|
+
# if true, CycleFoundError will be raised if a new vertex would
|
|
537
|
+
# create a cycle in this relation [true by default]
|
|
538
|
+
#
|
|
539
|
+
# For instance,
|
|
540
|
+
# relation :Children
|
|
541
|
+
#
|
|
542
|
+
# defines an instance of RelationGraph which is a DAG, defining the
|
|
543
|
+
# following methods on its vertices:
|
|
544
|
+
# each_children { |v, info| ... } => graph
|
|
545
|
+
# find_children { |v, info| ... } => object or nil
|
|
546
|
+
# add_children(v, info = nil) => graph
|
|
547
|
+
# remove_children(v) => graph
|
|
548
|
+
#
|
|
549
|
+
# and
|
|
550
|
+
#
|
|
551
|
+
# relation :Children, :child_name => :child
|
|
552
|
+
#
|
|
553
|
+
# would define
|
|
554
|
+
#
|
|
555
|
+
# each_child { |v, info| ... } => graph
|
|
556
|
+
# find_child { |v, info| ... } => object or nil
|
|
557
|
+
# add_child(v, info = nil) => graph
|
|
558
|
+
# remove_child(v) => graph
|
|
559
|
+
#
|
|
560
|
+
# * the DirectedRelationSupport module gets included in the vertex classes at the
|
|
561
|
+
# construction of the RelationSpace instance. See #apply_on.
|
|
562
|
+
# * the <tt>:noinfo</tt> option would then remove the 'info' parameter
|
|
563
|
+
# to the various blocks.
|
|
564
|
+
# * if <tt>:single_child</tt> is set to true, then an additional method is defined:
|
|
565
|
+
# child => object or nil
|
|
566
|
+
# * and finally if the following is used
|
|
567
|
+
# relation :Children, :child_name => :child, :parent_name => :parent
|
|
568
|
+
# then the following method is additionally defined
|
|
569
|
+
# each_parent { |v| ... }
|
|
570
|
+
#
|
|
470
571
|
def relation(relation_name, options = {}, &block)
|
|
471
572
|
options = validate_options options,
|
|
472
|
-
:child_name
|
|
473
|
-
:const_name
|
|
573
|
+
:child_name => relation_name.to_s.snakecase,
|
|
574
|
+
:const_name => relation_name,
|
|
474
575
|
:parent_name => nil,
|
|
475
|
-
:subsets
|
|
476
|
-
:noinfo
|
|
477
|
-
:graph
|
|
478
|
-
:distribute
|
|
479
|
-
:dag
|
|
576
|
+
:subsets => ValueSet.new,
|
|
577
|
+
:noinfo => false,
|
|
578
|
+
:graph => RelationGraph,
|
|
579
|
+
:distribute => true,
|
|
580
|
+
:dag => true,
|
|
480
581
|
:single_child => false,
|
|
481
|
-
:weak
|
|
582
|
+
:weak => false
|
|
482
583
|
|
|
483
584
|
# Check if this relation is already defined. If it is the case, reuse it.
|
|
484
585
|
# This is needed mostly by the reloading code
|
|
@@ -503,6 +604,10 @@ module Roby
|
|
|
503
604
|
if parent_enumerator = options[:parent_name]
|
|
504
605
|
mod.class_eval <<-EOD
|
|
505
606
|
def each_#{parent_enumerator}(&iterator)
|
|
607
|
+
if !block_given?
|
|
608
|
+
return enum_parent_objects(@@__r_#{relation_name}__)
|
|
609
|
+
end
|
|
610
|
+
|
|
506
611
|
self.each_parent_object(@@__r_#{relation_name}__, &iterator)
|
|
507
612
|
end
|
|
508
613
|
EOD
|
|
@@ -511,6 +616,10 @@ module Roby
|
|
|
511
616
|
if options[:noinfo]
|
|
512
617
|
mod.class_eval <<-EOD
|
|
513
618
|
def each_#{options[:child_name]}
|
|
619
|
+
if !block_given?
|
|
620
|
+
return enum_child_objects(@@__r_#{relation_name}__)
|
|
621
|
+
end
|
|
622
|
+
|
|
514
623
|
each_child_object(@@__r_#{relation_name}__) { |child| yield(child) }
|
|
515
624
|
end
|
|
516
625
|
def find_#{options[:child_name]}
|
|
@@ -522,10 +631,21 @@ module Roby
|
|
|
522
631
|
EOD
|
|
523
632
|
else
|
|
524
633
|
mod.class_eval <<-EOD
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
634
|
+
cached_enum("#{options[:child_name]}", "#{options[:child_name]}", true)
|
|
635
|
+
def each_#{options[:child_name]}(with_info = true)
|
|
636
|
+
if !block_given?
|
|
637
|
+
return enum_#{options[:child_name]}(with_info)
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
if with_info
|
|
641
|
+
each_child_object(@@__r_#{relation_name}__) do |child|
|
|
642
|
+
yield(child, self[child, @@__r_#{relation_name}__])
|
|
643
|
+
end
|
|
644
|
+
else
|
|
645
|
+
each_child_object(@@__r_#{relation_name}__) do |child|
|
|
646
|
+
yield(child)
|
|
647
|
+
end
|
|
648
|
+
end
|
|
529
649
|
end
|
|
530
650
|
def find_#{options[:child_name]}
|
|
531
651
|
each_child_object(@@__r_#{relation_name}__) do |child|
|
|
@@ -562,6 +682,11 @@ module Roby
|
|
|
562
682
|
|
|
563
683
|
graph
|
|
564
684
|
end
|
|
685
|
+
|
|
686
|
+
# Remove +rel+ from the set of relations managed in this space
|
|
687
|
+
def remove_relation(rel)
|
|
688
|
+
relations.delete(rel)
|
|
689
|
+
end
|
|
565
690
|
end
|
|
566
691
|
|
|
567
692
|
# Creates a new relation space which applies on +klass+. If a block is
|
|
@@ -572,12 +697,5 @@ module Roby
|
|
|
572
697
|
relation_space.apply_on klass
|
|
573
698
|
relation_space
|
|
574
699
|
end
|
|
575
|
-
|
|
576
|
-
# Requires all Roby relation files (all files in roby/relations/)
|
|
577
|
-
def self.load_all_relations
|
|
578
|
-
Dir.glob("#{File.dirname(__FILE__)}/relations/*.rb").each do |file|
|
|
579
|
-
require "roby/relations/#{File.basename(file, '.rb')}"
|
|
580
|
-
end
|
|
581
|
-
end
|
|
582
700
|
end
|
|
583
701
|
|