roby 0.7
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/.gitignore +29 -0
- data/History.txt +4 -0
- data/License-fr.txt +519 -0
- data/License.txt +515 -0
- data/Manifest.txt +245 -0
- data/NOTES +4 -0
- data/README.txt +163 -0
- data/Rakefile +161 -0
- data/TODO.txt +146 -0
- data/app/README.txt +24 -0
- data/app/Rakefile +8 -0
- data/app/config/ROBOT.rb +5 -0
- data/app/config/app.yml +91 -0
- data/app/config/init.rb +7 -0
- data/app/config/roby.yml +3 -0
- data/app/controllers/.gitattributes +0 -0
- data/app/controllers/ROBOT.rb +2 -0
- data/app/data/.gitattributes +0 -0
- data/app/planners/ROBOT/main.rb +6 -0
- data/app/planners/main.rb +5 -0
- data/app/scripts/distributed +3 -0
- data/app/scripts/generate/bookmarks +3 -0
- data/app/scripts/replay +3 -0
- data/app/scripts/results +3 -0
- data/app/scripts/run +3 -0
- data/app/scripts/server +3 -0
- data/app/scripts/shell +3 -0
- data/app/scripts/test +3 -0
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby +210 -0
- data/bin/roby-log +168 -0
- data/bin/roby-shell +25 -0
- data/doc/images/event_generalization.png +0 -0
- data/doc/images/exception_propagation_1.png +0 -0
- data/doc/images/exception_propagation_2.png +0 -0
- data/doc/images/exception_propagation_3.png +0 -0
- data/doc/images/exception_propagation_4.png +0 -0
- data/doc/images/exception_propagation_5.png +0 -0
- data/doc/images/replay_handler_error.png +0 -0
- data/doc/images/replay_handler_error_0.png +0 -0
- data/doc/images/replay_handler_error_1.png +0 -0
- data/doc/images/roby_cycle_overview.png +0 -0
- data/doc/images/roby_replay_02.png +0 -0
- data/doc/images/roby_replay_03.png +0 -0
- data/doc/images/roby_replay_04.png +0 -0
- data/doc/images/roby_replay_event_representation.png +0 -0
- data/doc/images/roby_replay_first_state.png +0 -0
- data/doc/images/roby_replay_relations.png +0 -0
- data/doc/images/roby_replay_startup.png +0 -0
- data/doc/images/task_event_generalization.png +0 -0
- data/doc/papers.rdoc +11 -0
- data/doc/styles/allison.css +314 -0
- data/doc/styles/allison.js +316 -0
- data/doc/styles/allison.rb +276 -0
- data/doc/styles/jamis.rb +593 -0
- data/doc/tutorials/01-GettingStarted.rdoc +86 -0
- data/doc/tutorials/02-GoForward.rdoc +220 -0
- data/doc/tutorials/03-PlannedPath.rdoc +268 -0
- data/doc/tutorials/04-EventPropagation.rdoc +236 -0
- data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
- data/doc/tutorials/06-Overview.rdoc +40 -0
- data/doc/videos.rdoc +69 -0
- data/ext/droby/dump.cc +175 -0
- data/ext/droby/extconf.rb +3 -0
- data/ext/graph/algorithm.cc +746 -0
- data/ext/graph/extconf.rb +7 -0
- data/ext/graph/graph.cc +529 -0
- data/ext/graph/graph.hh +183 -0
- data/ext/graph/iterator_sequence.hh +102 -0
- data/ext/graph/undirected_dfs.hh +226 -0
- data/ext/graph/undirected_graph.hh +421 -0
- data/lib/roby.rb +41 -0
- data/lib/roby/app.rb +870 -0
- data/lib/roby/app/rake.rb +56 -0
- data/lib/roby/app/run.rb +14 -0
- data/lib/roby/app/scripts/distributed.rb +13 -0
- data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
- data/lib/roby/app/scripts/replay.rb +31 -0
- data/lib/roby/app/scripts/results.rb +15 -0
- data/lib/roby/app/scripts/run.rb +26 -0
- data/lib/roby/app/scripts/server.rb +18 -0
- data/lib/roby/app/scripts/shell.rb +88 -0
- data/lib/roby/app/scripts/test.rb +40 -0
- data/lib/roby/basic_object.rb +151 -0
- data/lib/roby/config.rb +5 -0
- data/lib/roby/control.rb +747 -0
- data/lib/roby/decision_control.rb +17 -0
- data/lib/roby/distributed.rb +32 -0
- data/lib/roby/distributed/base.rb +440 -0
- data/lib/roby/distributed/communication.rb +871 -0
- data/lib/roby/distributed/connection_space.rb +592 -0
- data/lib/roby/distributed/distributed_object.rb +206 -0
- data/lib/roby/distributed/drb.rb +62 -0
- data/lib/roby/distributed/notifications.rb +539 -0
- data/lib/roby/distributed/peer.rb +550 -0
- data/lib/roby/distributed/protocol.rb +529 -0
- data/lib/roby/distributed/proxy.rb +343 -0
- data/lib/roby/distributed/subscription.rb +311 -0
- data/lib/roby/distributed/transaction.rb +498 -0
- data/lib/roby/event.rb +897 -0
- data/lib/roby/exceptions.rb +234 -0
- data/lib/roby/executives/simple.rb +30 -0
- data/lib/roby/graph.rb +166 -0
- data/lib/roby/interface.rb +390 -0
- data/lib/roby/log.rb +3 -0
- data/lib/roby/log/chronicle.rb +303 -0
- data/lib/roby/log/console.rb +72 -0
- data/lib/roby/log/data_stream.rb +197 -0
- data/lib/roby/log/dot.rb +279 -0
- data/lib/roby/log/event_stream.rb +151 -0
- data/lib/roby/log/file.rb +340 -0
- data/lib/roby/log/gui/basic_display.ui +83 -0
- data/lib/roby/log/gui/chronicle.rb +26 -0
- data/lib/roby/log/gui/chronicle_view.rb +40 -0
- data/lib/roby/log/gui/chronicle_view.ui +70 -0
- data/lib/roby/log/gui/data_displays.rb +172 -0
- data/lib/roby/log/gui/data_displays.ui +155 -0
- data/lib/roby/log/gui/notifications.rb +26 -0
- data/lib/roby/log/gui/relations.rb +248 -0
- data/lib/roby/log/gui/relations.ui +123 -0
- data/lib/roby/log/gui/relations_view.rb +185 -0
- data/lib/roby/log/gui/relations_view.ui +149 -0
- data/lib/roby/log/gui/replay.rb +327 -0
- data/lib/roby/log/gui/replay_controls.rb +200 -0
- data/lib/roby/log/gui/replay_controls.ui +259 -0
- data/lib/roby/log/gui/runtime.rb +130 -0
- data/lib/roby/log/hooks.rb +185 -0
- data/lib/roby/log/logger.rb +202 -0
- data/lib/roby/log/notifications.rb +244 -0
- data/lib/roby/log/plan_rebuilder.rb +470 -0
- data/lib/roby/log/relations.rb +1056 -0
- data/lib/roby/log/server.rb +550 -0
- data/lib/roby/log/sqlite.rb +47 -0
- data/lib/roby/log/timings.rb +164 -0
- data/lib/roby/plan-object.rb +247 -0
- data/lib/roby/plan.rb +762 -0
- data/lib/roby/planning.rb +13 -0
- data/lib/roby/planning/loops.rb +302 -0
- data/lib/roby/planning/model.rb +906 -0
- data/lib/roby/planning/task.rb +151 -0
- data/lib/roby/propagation.rb +562 -0
- data/lib/roby/query.rb +619 -0
- data/lib/roby/relations.rb +583 -0
- data/lib/roby/relations/conflicts.rb +70 -0
- data/lib/roby/relations/ensured.rb +20 -0
- data/lib/roby/relations/error_handling.rb +23 -0
- data/lib/roby/relations/events.rb +9 -0
- data/lib/roby/relations/executed_by.rb +193 -0
- data/lib/roby/relations/hierarchy.rb +239 -0
- data/lib/roby/relations/influence.rb +10 -0
- data/lib/roby/relations/planned_by.rb +63 -0
- data/lib/roby/robot.rb +7 -0
- data/lib/roby/standard_errors.rb +218 -0
- data/lib/roby/state.rb +5 -0
- data/lib/roby/state/events.rb +221 -0
- data/lib/roby/state/information.rb +55 -0
- data/lib/roby/state/pos.rb +110 -0
- data/lib/roby/state/shapes.rb +32 -0
- data/lib/roby/state/state.rb +353 -0
- data/lib/roby/support.rb +92 -0
- data/lib/roby/task-operations.rb +182 -0
- data/lib/roby/task.rb +1618 -0
- data/lib/roby/test/common.rb +399 -0
- data/lib/roby/test/distributed.rb +214 -0
- data/lib/roby/test/tasks/empty_task.rb +9 -0
- data/lib/roby/test/tasks/goto.rb +36 -0
- data/lib/roby/test/tasks/simple_task.rb +23 -0
- data/lib/roby/test/testcase.rb +519 -0
- data/lib/roby/test/tools.rb +160 -0
- data/lib/roby/thread_task.rb +87 -0
- data/lib/roby/transactions.rb +462 -0
- data/lib/roby/transactions/proxy.rb +292 -0
- data/lib/roby/transactions/updates.rb +139 -0
- data/plugins/fault_injection/History.txt +4 -0
- data/plugins/fault_injection/README.txt +37 -0
- data/plugins/fault_injection/Rakefile +18 -0
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +52 -0
- data/plugins/fault_injection/fault_injection.rb +89 -0
- data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
- data/plugins/subsystems/README.txt +40 -0
- data/plugins/subsystems/Rakefile +18 -0
- data/plugins/subsystems/app.rb +171 -0
- data/plugins/subsystems/test/app/README +24 -0
- data/plugins/subsystems/test/app/Rakefile +8 -0
- data/plugins/subsystems/test/app/config/app.yml +71 -0
- data/plugins/subsystems/test/app/config/init.rb +9 -0
- data/plugins/subsystems/test/app/config/roby.yml +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +20 -0
- data/plugins/subsystems/test/app/scripts/distributed +3 -0
- data/plugins/subsystems/test/app/scripts/replay +3 -0
- data/plugins/subsystems/test/app/scripts/results +3 -0
- data/plugins/subsystems/test/app/scripts/run +3 -0
- data/plugins/subsystems/test/app/scripts/server +3 -0
- data/plugins/subsystems/test/app/scripts/shell +3 -0
- data/plugins/subsystems/test/app/scripts/test +3 -0
- data/plugins/subsystems/test/app/tasks/services.rb +15 -0
- data/plugins/subsystems/test/test_subsystems.rb +71 -0
- data/test/distributed/test_communication.rb +178 -0
- data/test/distributed/test_connection.rb +282 -0
- data/test/distributed/test_execution.rb +373 -0
- data/test/distributed/test_mixed_plan.rb +341 -0
- data/test/distributed/test_plan_notifications.rb +238 -0
- data/test/distributed/test_protocol.rb +516 -0
- data/test/distributed/test_query.rb +102 -0
- data/test/distributed/test_remote_plan.rb +491 -0
- data/test/distributed/test_transaction.rb +463 -0
- data/test/mockups/tasks.rb +27 -0
- data/test/planning/test_loops.rb +380 -0
- data/test/planning/test_model.rb +427 -0
- data/test/planning/test_task.rb +106 -0
- data/test/relations/test_conflicts.rb +42 -0
- data/test/relations/test_ensured.rb +38 -0
- data/test/relations/test_executed_by.rb +149 -0
- data/test/relations/test_hierarchy.rb +158 -0
- data/test/relations/test_planned_by.rb +54 -0
- data/test/suite_core.rb +24 -0
- data/test/suite_distributed.rb +9 -0
- data/test/suite_planning.rb +3 -0
- data/test/suite_relations.rb +8 -0
- data/test/test_bgl.rb +508 -0
- data/test/test_control.rb +399 -0
- data/test/test_event.rb +894 -0
- data/test/test_exceptions.rb +592 -0
- data/test/test_interface.rb +37 -0
- data/test/test_log.rb +114 -0
- data/test/test_log_server.rb +132 -0
- data/test/test_plan.rb +584 -0
- data/test/test_propagation.rb +210 -0
- data/test/test_query.rb +266 -0
- data/test/test_relations.rb +180 -0
- data/test/test_state.rb +414 -0
- data/test/test_support.rb +16 -0
- data/test/test_task.rb +938 -0
- data/test/test_testcase.rb +122 -0
- data/test/test_thread_task.rb +73 -0
- data/test/test_transactions.rb +569 -0
- data/test/test_transactions_proxy.rb +198 -0
- metadata +570 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
require 'roby/support'
|
|
2
|
+
require 'roby/graph'
|
|
3
|
+
|
|
4
|
+
module Roby
|
|
5
|
+
# This exception is raised when an edge is being added in a DAG, while this
|
|
6
|
+
# edge would create a cycle.
|
|
7
|
+
class CycleFoundError < RuntimeError; end
|
|
8
|
+
|
|
9
|
+
# Base support for relations. It is mixed-in objects that are part of
|
|
10
|
+
# relation networks (like Task and EventGenerator)
|
|
11
|
+
module DirectedRelationSupport
|
|
12
|
+
include BGL::Vertex
|
|
13
|
+
|
|
14
|
+
alias :child_object? :child_vertex?
|
|
15
|
+
alias :parent_object? :parent_vertex?
|
|
16
|
+
alias :related_object? :related_vertex?
|
|
17
|
+
alias :each_child_object :each_child_vertex
|
|
18
|
+
alias :each_parent_object :each_parent_vertex
|
|
19
|
+
alias :each_relation :each_graph
|
|
20
|
+
alias :clear_relations :clear_vertex
|
|
21
|
+
|
|
22
|
+
cached_enum("graph", "relations", false)
|
|
23
|
+
cached_enum("parent_object", "parent_objects", true)
|
|
24
|
+
cached_enum("child_object", "child_objects", true)
|
|
25
|
+
|
|
26
|
+
# The array of relations this object is part of
|
|
27
|
+
def relations; enum_relations.to_a end
|
|
28
|
+
|
|
29
|
+
# Computes and returns the set of objects related with this one (parent
|
|
30
|
+
# or child). If +relation+ is given, enumerate only for this relation,
|
|
31
|
+
# otherwise enumerate for all relations. If +result+ is given, it is a
|
|
32
|
+
# ValueSet in which the related objects are added
|
|
33
|
+
def related_objects(relation = nil, result = nil)
|
|
34
|
+
result ||= ValueSet.new
|
|
35
|
+
if relation
|
|
36
|
+
result.merge(parent_objects(relation).to_value_set)
|
|
37
|
+
result.merge(child_objects(relation).to_value_set)
|
|
38
|
+
else
|
|
39
|
+
each_relation { |rel| related_objects(rel, result) }
|
|
40
|
+
end
|
|
41
|
+
result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Set of all parent objects in +relation+
|
|
45
|
+
alias :parent_objects :enum_parent_objects
|
|
46
|
+
# Set of all child object in +relation+
|
|
47
|
+
alias :child_objects :enum_child_objects
|
|
48
|
+
|
|
49
|
+
# Add a new child object in the +relation+ relation. This calls
|
|
50
|
+
# * #adding_child_object on +self+ and #adding_parent_object on +child+
|
|
51
|
+
# just before the relation is added
|
|
52
|
+
# * #added_child_object on +self+ and #added_parent_object on +child+
|
|
53
|
+
# just after
|
|
54
|
+
def add_child_object(child, relation, info = nil)
|
|
55
|
+
check_is_relation(relation)
|
|
56
|
+
relation.add_relation(self, child, info)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Add a new parent object in the +relation+ relation
|
|
60
|
+
# * #adding_child_object on +parent+ and #adding_parent_object on
|
|
61
|
+
# +self+ just before the relation is added
|
|
62
|
+
# * #added_child_object on +parent+ and #added_child_object on +self+
|
|
63
|
+
# just after
|
|
64
|
+
def add_parent_object(parent, relation, info = nil)
|
|
65
|
+
parent.add_child_object(self, relation, info)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Hook called before a new child is added in the +relation+ relation
|
|
69
|
+
# def adding_child_object(child, relation, info)
|
|
70
|
+
# child.adding_parent_object(self, relations, info)
|
|
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.
|
|
86
|
+
def remove_child_object(child, relation = nil)
|
|
87
|
+
check_is_relation(relation)
|
|
88
|
+
apply_selection(relation, (relation || enum_relations)) do |relation|
|
|
89
|
+
relation.remove_relation(self, child)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Remove relations where self is a parent. If +relation+ is given,
|
|
94
|
+
# remove only the relations in this relation graph.
|
|
95
|
+
def remove_children(relation = nil)
|
|
96
|
+
apply_selection(relation, (relation || enum_relations)) do |relation|
|
|
97
|
+
self.each_child_object(relation) do |child|
|
|
98
|
+
remove_child_object(child, relation)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Remove relations where +self+ is a child. If +relation+ is given,
|
|
104
|
+
# remove only the relations in this relation graph
|
|
105
|
+
def remove_parent_object(parent, relation = nil)
|
|
106
|
+
parent.remove_child_object(self, relation)
|
|
107
|
+
end
|
|
108
|
+
# Remove all parents of +self+. If +relation+ is given, remove only the
|
|
109
|
+
# parents in this relation graph
|
|
110
|
+
def remove_parents(relation = nil)
|
|
111
|
+
check_is_relation(relation)
|
|
112
|
+
apply_selection(relation, (relation || enum_relations)) do |relation|
|
|
113
|
+
relation.each_parent_object(self) do |parent|
|
|
114
|
+
remove_parent_object(relation, parent)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
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
|
+
# Remove all relations that point to or come from +to+ If +to+ is nil,
|
|
136
|
+
# it removes all relations of +self+
|
|
137
|
+
def remove_relations(to = nil, relation = nil)
|
|
138
|
+
check_is_relation(relation)
|
|
139
|
+
if to
|
|
140
|
+
remove_parent_object(to, relation)
|
|
141
|
+
remove_child_object(to, relation)
|
|
142
|
+
else
|
|
143
|
+
apply_selection(relation, (relation || enum_relations)) do |relation|
|
|
144
|
+
each_parent_object(relation) { |parent| remove_parent_object(parent, relation) }
|
|
145
|
+
each_child_object(relation) { |child| remove_child_object(child, relation) }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Raises if +type+ does not look like a relation
|
|
151
|
+
def check_is_relation(type) # :nodoc:
|
|
152
|
+
if type && !(RelationGraph === type)
|
|
153
|
+
raise ArgumentError, "#{type} (of class #{type.class}) is not a relation type"
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# If +object+ is given, yields object or returns +object+ (if a block
|
|
158
|
+
# is given or not). If +object+ is nil, either yields the elements of
|
|
159
|
+
# +enumerator+ or returns enumerator.
|
|
160
|
+
def apply_selection(object, enumerator) # :nodoc:
|
|
161
|
+
if block_given?
|
|
162
|
+
if object; yield(object)
|
|
163
|
+
else enumerator.each { |o| yield(o) }
|
|
164
|
+
end
|
|
165
|
+
else
|
|
166
|
+
if object; [object]
|
|
167
|
+
else; enumerator
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
private :apply_selection
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# This class manages the graph defined by an object relation in Roby.
|
|
175
|
+
# Relation graphs are managed in hierarchies (for instance, in
|
|
176
|
+
# EventStructure, Precedence is a superset of CausalLink, and CausalLink a
|
|
177
|
+
# superset of both Forwarding and Signal). In this hierarchy, at each
|
|
178
|
+
# level, an edge cannot be present in more than one graph. Nonetheless, it
|
|
179
|
+
# is possible for a parent relation to have an edge which is present in
|
|
180
|
+
# none of its children.
|
|
181
|
+
class RelationGraph < BGL::Graph
|
|
182
|
+
# The relation name
|
|
183
|
+
attr_reader :name
|
|
184
|
+
# The relation parent if any
|
|
185
|
+
attr_accessor :parent
|
|
186
|
+
# The set of graphs
|
|
187
|
+
attr_reader :subsets
|
|
188
|
+
# The graph options as given to RelationSpace#relation
|
|
189
|
+
attr_reader :options
|
|
190
|
+
|
|
191
|
+
# Creates a relation graph with the given name and options. The
|
|
192
|
+
# following options are recognized:
|
|
193
|
+
# +dag+::
|
|
194
|
+
# if the graph is a DAG. If true, add_relation will check that
|
|
195
|
+
# no cycle is created
|
|
196
|
+
# +subsets+::
|
|
197
|
+
# a set of RelationGraph objects that are children of this
|
|
198
|
+
# one
|
|
199
|
+
# +distributed+::
|
|
200
|
+
# if this relation graph should be seen by remote hosts
|
|
201
|
+
def initialize(name, options = {})
|
|
202
|
+
@name = name
|
|
203
|
+
@options = options
|
|
204
|
+
@subsets = ValueSet.new
|
|
205
|
+
@distribute = options[:distribute]
|
|
206
|
+
@dag = options[:dag]
|
|
207
|
+
@weak = options[:weak]
|
|
208
|
+
|
|
209
|
+
if options[:subsets]
|
|
210
|
+
options[:subsets].each(&method(:superset_of))
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# True if this relation graph is a DAG
|
|
215
|
+
attr_predicate :dag
|
|
216
|
+
# True if this relation should be seen by remote peers
|
|
217
|
+
attr_predicate :distribute
|
|
218
|
+
# If this relation is weak. Weak relations can be removed without major
|
|
219
|
+
# consequences. This is mainly used during plan garbage collection to
|
|
220
|
+
# break cross-relations cycles (cycles which exist in the graph union
|
|
221
|
+
# of all the relation graphs).
|
|
222
|
+
attr_predicate :weak
|
|
223
|
+
|
|
224
|
+
def to_s; name end
|
|
225
|
+
|
|
226
|
+
# True if this relation does not have a parent
|
|
227
|
+
def root_relation?; !parent end
|
|
228
|
+
|
|
229
|
+
# Add a relation between +from+ and +to+. The relation is added on all
|
|
230
|
+
# parent relation graphs as well.
|
|
231
|
+
#
|
|
232
|
+
# If #dag? is true, it checks that the new relation does not create a
|
|
233
|
+
# cycle
|
|
234
|
+
def add_relation(from, to, info = nil)
|
|
235
|
+
# Get the toplevel DAG in our relation hierarchy. We only test for the
|
|
236
|
+
# DAG property on this one, as it is the union of all its children
|
|
237
|
+
top_dag = nil
|
|
238
|
+
new_relations = []
|
|
239
|
+
rel = self
|
|
240
|
+
while rel
|
|
241
|
+
top_dag = rel if rel.dag?
|
|
242
|
+
new_relations << rel
|
|
243
|
+
rel = rel.parent
|
|
244
|
+
end
|
|
245
|
+
if top_dag && !top_dag.linked?(from, to) && top_dag.reachable?(to, from)
|
|
246
|
+
raise CycleFoundError, "cannot add a #{from} -> #{to} relation since it would create a cycle"
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Now compute the set of relations in which we really have to add a
|
|
250
|
+
# new relation
|
|
251
|
+
top_rel = new_relations.last
|
|
252
|
+
if top_rel.linked?(from, to)
|
|
253
|
+
if !(old_info = from[to, top_rel]).nil?
|
|
254
|
+
if old_info != info
|
|
255
|
+
raise ArgumentError, "trying to change edge information"
|
|
256
|
+
end
|
|
257
|
+
end
|
|
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
|
+
|
|
274
|
+
unless new_relations.empty?
|
|
275
|
+
if from.respond_to?(:adding_child_object)
|
|
276
|
+
from.adding_child_object(to, new_relations, info)
|
|
277
|
+
end
|
|
278
|
+
if to.respond_to?(:adding_parent_object)
|
|
279
|
+
to.adding_parent_object(from, new_relations, info)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
for rel in new_relations
|
|
283
|
+
rel.__bgl_link(from, to, info)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
if from.respond_to?(:added_child_object)
|
|
287
|
+
from.added_child_object(to, new_relations, info)
|
|
288
|
+
end
|
|
289
|
+
if to.respond_to?(:added_parent_object)
|
|
290
|
+
to.added_parent_object(from, new_relations, info)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
alias :__bgl_link :link
|
|
296
|
+
# Reimplemented from BGL::Graph. Unlike this implementation, it is
|
|
297
|
+
# possible to add an already existing edge if the +info+ parameter
|
|
298
|
+
# matches.
|
|
299
|
+
def link(from, to, info)
|
|
300
|
+
if linked?(from, to)
|
|
301
|
+
if info != from[to, self]
|
|
302
|
+
raise ArgumentError, "trying to change edge information"
|
|
303
|
+
end
|
|
304
|
+
return
|
|
305
|
+
end
|
|
306
|
+
super
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Remove the relation between +from+ and +to+, in this graph and in its
|
|
310
|
+
# parent graphs as well
|
|
311
|
+
def remove_relation(from, to)
|
|
312
|
+
rel = self
|
|
313
|
+
relations = []
|
|
314
|
+
while rel
|
|
315
|
+
relations << rel
|
|
316
|
+
rel = rel.parent
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
if from.respond_to?(:removing_child_object)
|
|
320
|
+
from.removing_child_object(to, relations)
|
|
321
|
+
end
|
|
322
|
+
if to.respond_to?(:removing_parent_object)
|
|
323
|
+
to.removing_parent_object(from, relations)
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
for rel in relations
|
|
327
|
+
rel.unlink(from, to)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
if from.respond_to?(:removed_child_object)
|
|
331
|
+
from.removed_child_object(to, relations)
|
|
332
|
+
end
|
|
333
|
+
if to.respond_to?(:removed_parent_object)
|
|
334
|
+
to.removed_parent_object(from, relations)
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Returns true if +relation+ is included in this relation (i.e. it is
|
|
339
|
+
# either the same relation or one of its children)
|
|
340
|
+
def subset?(relation)
|
|
341
|
+
self.eql?(relation) || subsets.any? { |subrel| subrel.subset?(relation) }
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Returns +true+ if there is an edge +source+ -> +target+ in this graph
|
|
345
|
+
# or in one of its parents
|
|
346
|
+
def linked_in_hierarchy?(source, target) # :nodoc:
|
|
347
|
+
linked?(source, target) || (parent.linked?(source, target) if parent)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Declare that +relation+ is a superset of this relation
|
|
351
|
+
def superset_of(relation)
|
|
352
|
+
relation.each_edge do |source, target, info|
|
|
353
|
+
if linked_in_hierarchy?(source, target)
|
|
354
|
+
raise ArgumentError, "relation and self already share an edge"
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
relation.parent = self
|
|
359
|
+
subsets << relation
|
|
360
|
+
|
|
361
|
+
# Copy the relations of the child into this graph
|
|
362
|
+
relation.each_edge do |source, target, info|
|
|
363
|
+
source.add_child_object(target, self, info)
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# The Ruby module that gets included in graph objects
|
|
368
|
+
attr_accessor :support
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# A relation space is a module which handles a list of relations and
|
|
372
|
+
# applies them to a set of classes. In this context, a relation is both a
|
|
373
|
+
# Ruby module which gets included in the classes this space is applied on,
|
|
374
|
+
# and a RelationGraph object which holds the object graphs.
|
|
375
|
+
#
|
|
376
|
+
# See the files in roby/relations to see definitions of new relations
|
|
377
|
+
class RelationSpace < Module
|
|
378
|
+
# The set of relations included in this relation space
|
|
379
|
+
attr_reader :relations
|
|
380
|
+
# The set of klasses on which the relations have been applied
|
|
381
|
+
attr_reader :applied
|
|
382
|
+
|
|
383
|
+
def initialize
|
|
384
|
+
@relations = Array.new
|
|
385
|
+
@applied = Array.new
|
|
386
|
+
super
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# This relation applies on klass. It mainly means that a relation
|
|
390
|
+
# defined on this RelationSpace will define the relation-access methods
|
|
391
|
+
# and include its support module (if any) in +klass+.
|
|
392
|
+
def apply_on(klass)
|
|
393
|
+
klass.include DirectedRelationSupport
|
|
394
|
+
each_relation do |graph|
|
|
395
|
+
klass.include graph.support
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
applied << klass
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Yields the relations that are defined on this space
|
|
402
|
+
def each_relation
|
|
403
|
+
for rel in relations
|
|
404
|
+
yield(rel)
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Yields the root relations that are defined on this space
|
|
409
|
+
def each_root_relation
|
|
410
|
+
for rel in relations
|
|
411
|
+
yield(rel) unless rel.parent
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# Returns the set of objects that are reachable from +obj+ through any
|
|
416
|
+
# of the relations. Note that +b+ will be included in the result if
|
|
417
|
+
# there is an edge <tt>obj => a</tt> in one relation and another edge
|
|
418
|
+
# <tt>a => b</tt> in another relation
|
|
419
|
+
#
|
|
420
|
+
# If +strict+ is true, +obj+ is not included in the returned set
|
|
421
|
+
def children_of(obj, strict = true, relations = nil)
|
|
422
|
+
set = compute_children_of([obj].to_value_set, relations || self.relations)
|
|
423
|
+
set.delete(obj) if strict
|
|
424
|
+
set
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# Internal implementation method for +children_of+
|
|
428
|
+
def compute_children_of(current, relations) # :nodoc:
|
|
429
|
+
old_size = current.size
|
|
430
|
+
for rel in relations
|
|
431
|
+
next if (rel.parent && relations.include?(rel.parent))
|
|
432
|
+
|
|
433
|
+
components = rel.generated_subgraphs(current, false)
|
|
434
|
+
for c in components
|
|
435
|
+
current.merge c
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
if current.size == old_size
|
|
440
|
+
return current
|
|
441
|
+
else
|
|
442
|
+
return compute_children_of(current, relations)
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# Defines a relation in this relation space. This defines a relation
|
|
447
|
+
# graph, and various iteration methods on the vertices. If a block is
|
|
448
|
+
# given, it defines a set of functions which should be defined on the
|
|
449
|
+
# vertex objects.
|
|
450
|
+
#
|
|
451
|
+
# = Options
|
|
452
|
+
# child_name::
|
|
453
|
+
# define a <tt>each_#{child_name}</tt> method to iterate
|
|
454
|
+
# on the vertex children. Uses the relation name by default (a Child
|
|
455
|
+
# relation would define a <tt>each_child</tt> method)
|
|
456
|
+
# parent_name::
|
|
457
|
+
# define a <tt>each_#{parent_name}</tt> method to iterate
|
|
458
|
+
# on the vertex parents. If none is given, no method is defined
|
|
459
|
+
# subsets:: a list of subgraphs. See RelationGraph#superset_of
|
|
460
|
+
# noinfo::
|
|
461
|
+
# if the relation embeds some additional information. If true,
|
|
462
|
+
# 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]
|
|
466
|
+
# single_child::
|
|
467
|
+
# if the relations accepts only one child per vertex
|
|
468
|
+
# [false]. If this option is set, defines a <tt>#{child_name}</tt>
|
|
469
|
+
# method which returns the only child or nil
|
|
470
|
+
def relation(relation_name, options = {}, &block)
|
|
471
|
+
options = validate_options options,
|
|
472
|
+
:child_name => relation_name.to_s.underscore,
|
|
473
|
+
:const_name => relation_name,
|
|
474
|
+
:parent_name => nil,
|
|
475
|
+
:subsets => ValueSet.new,
|
|
476
|
+
:noinfo => false,
|
|
477
|
+
:graph => RelationGraph,
|
|
478
|
+
:distribute => true,
|
|
479
|
+
:dag => true,
|
|
480
|
+
:single_child => false,
|
|
481
|
+
:weak => false
|
|
482
|
+
|
|
483
|
+
# Check if this relation is already defined. If it is the case, reuse it.
|
|
484
|
+
# This is needed mostly by the reloading code
|
|
485
|
+
if const_defined?(options[:const_name])
|
|
486
|
+
graph = const_get(options[:const_name])
|
|
487
|
+
mod = graph.support
|
|
488
|
+
|
|
489
|
+
else
|
|
490
|
+
graph = options[:graph].new "#{self.name}::#{options[:const_name]}", options
|
|
491
|
+
mod = Module.new do
|
|
492
|
+
singleton_class.class_eval do
|
|
493
|
+
define_method("__r_#{relation_name}__") { graph }
|
|
494
|
+
end
|
|
495
|
+
class_eval "@@__r_#{relation_name}__ = __r_#{relation_name}__"
|
|
496
|
+
end
|
|
497
|
+
const_set(options[:const_name], graph)
|
|
498
|
+
relations << graph
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
mod.class_eval(&block) if block_given?
|
|
502
|
+
|
|
503
|
+
if parent_enumerator = options[:parent_name]
|
|
504
|
+
mod.class_eval <<-EOD
|
|
505
|
+
def each_#{parent_enumerator}(&iterator)
|
|
506
|
+
self.each_parent_object(@@__r_#{relation_name}__, &iterator)
|
|
507
|
+
end
|
|
508
|
+
EOD
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
if options[:noinfo]
|
|
512
|
+
mod.class_eval <<-EOD
|
|
513
|
+
def each_#{options[:child_name]}
|
|
514
|
+
each_child_object(@@__r_#{relation_name}__) { |child| yield(child) }
|
|
515
|
+
end
|
|
516
|
+
def find_#{options[:child_name]}
|
|
517
|
+
each_child_object(@@__r_#{relation_name}__) do |child|
|
|
518
|
+
return child if yield(child)
|
|
519
|
+
end
|
|
520
|
+
nil
|
|
521
|
+
end
|
|
522
|
+
EOD
|
|
523
|
+
else
|
|
524
|
+
mod.class_eval <<-EOD
|
|
525
|
+
def each_#{options[:child_name]}
|
|
526
|
+
each_child_object(@@__r_#{relation_name}__) do |child|
|
|
527
|
+
yield(child, self[child, @@__r_#{relation_name}__])
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
def find_#{options[:child_name]}
|
|
531
|
+
each_child_object(@@__r_#{relation_name}__) do |child|
|
|
532
|
+
return child if yield(child, self[child, @@__r_#{relation_name}__])
|
|
533
|
+
end
|
|
534
|
+
nil
|
|
535
|
+
end
|
|
536
|
+
EOD
|
|
537
|
+
end
|
|
538
|
+
mod.class_eval <<-EOD
|
|
539
|
+
def add_#{options[:child_name]}(to, info = nil)
|
|
540
|
+
add_child_object(to, @@__r_#{relation_name}__, info)
|
|
541
|
+
self
|
|
542
|
+
end
|
|
543
|
+
def remove_#{options[:child_name]}(to)
|
|
544
|
+
remove_child_object(to, @@__r_#{relation_name}__)
|
|
545
|
+
self
|
|
546
|
+
end
|
|
547
|
+
EOD
|
|
548
|
+
|
|
549
|
+
if options[:single_child]
|
|
550
|
+
mod.class_eval <<-EOD
|
|
551
|
+
def #{options[:child_name]}
|
|
552
|
+
each_child_object(@@__r_#{relation_name}__) do |child_task|
|
|
553
|
+
return child_task
|
|
554
|
+
end
|
|
555
|
+
nil
|
|
556
|
+
end
|
|
557
|
+
EOD
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
graph.support = mod
|
|
561
|
+
applied.each { |klass| klass.include mod }
|
|
562
|
+
|
|
563
|
+
graph
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# Creates a new relation space which applies on +klass+. If a block is
|
|
568
|
+
# given, it is eval'd in the context of the new relation space instance
|
|
569
|
+
def self.RelationSpace(klass)
|
|
570
|
+
klass.include DirectedRelationSupport
|
|
571
|
+
relation_space = RelationSpace.new
|
|
572
|
+
relation_space.apply_on klass
|
|
573
|
+
relation_space
|
|
574
|
+
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
|
+
end
|
|
583
|
+
|