roby 0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (240) hide show
  1. data/.gitignore +29 -0
  2. data/History.txt +4 -0
  3. data/License-fr.txt +519 -0
  4. data/License.txt +515 -0
  5. data/Manifest.txt +245 -0
  6. data/NOTES +4 -0
  7. data/README.txt +163 -0
  8. data/Rakefile +161 -0
  9. data/TODO.txt +146 -0
  10. data/app/README.txt +24 -0
  11. data/app/Rakefile +8 -0
  12. data/app/config/ROBOT.rb +5 -0
  13. data/app/config/app.yml +91 -0
  14. data/app/config/init.rb +7 -0
  15. data/app/config/roby.yml +3 -0
  16. data/app/controllers/.gitattributes +0 -0
  17. data/app/controllers/ROBOT.rb +2 -0
  18. data/app/data/.gitattributes +0 -0
  19. data/app/planners/ROBOT/main.rb +6 -0
  20. data/app/planners/main.rb +5 -0
  21. data/app/scripts/distributed +3 -0
  22. data/app/scripts/generate/bookmarks +3 -0
  23. data/app/scripts/replay +3 -0
  24. data/app/scripts/results +3 -0
  25. data/app/scripts/run +3 -0
  26. data/app/scripts/server +3 -0
  27. data/app/scripts/shell +3 -0
  28. data/app/scripts/test +3 -0
  29. data/app/tasks/.gitattributes +0 -0
  30. data/app/tasks/ROBOT/.gitattributes +0 -0
  31. data/bin/roby +210 -0
  32. data/bin/roby-log +168 -0
  33. data/bin/roby-shell +25 -0
  34. data/doc/images/event_generalization.png +0 -0
  35. data/doc/images/exception_propagation_1.png +0 -0
  36. data/doc/images/exception_propagation_2.png +0 -0
  37. data/doc/images/exception_propagation_3.png +0 -0
  38. data/doc/images/exception_propagation_4.png +0 -0
  39. data/doc/images/exception_propagation_5.png +0 -0
  40. data/doc/images/replay_handler_error.png +0 -0
  41. data/doc/images/replay_handler_error_0.png +0 -0
  42. data/doc/images/replay_handler_error_1.png +0 -0
  43. data/doc/images/roby_cycle_overview.png +0 -0
  44. data/doc/images/roby_replay_02.png +0 -0
  45. data/doc/images/roby_replay_03.png +0 -0
  46. data/doc/images/roby_replay_04.png +0 -0
  47. data/doc/images/roby_replay_event_representation.png +0 -0
  48. data/doc/images/roby_replay_first_state.png +0 -0
  49. data/doc/images/roby_replay_relations.png +0 -0
  50. data/doc/images/roby_replay_startup.png +0 -0
  51. data/doc/images/task_event_generalization.png +0 -0
  52. data/doc/papers.rdoc +11 -0
  53. data/doc/styles/allison.css +314 -0
  54. data/doc/styles/allison.js +316 -0
  55. data/doc/styles/allison.rb +276 -0
  56. data/doc/styles/jamis.rb +593 -0
  57. data/doc/tutorials/01-GettingStarted.rdoc +86 -0
  58. data/doc/tutorials/02-GoForward.rdoc +220 -0
  59. data/doc/tutorials/03-PlannedPath.rdoc +268 -0
  60. data/doc/tutorials/04-EventPropagation.rdoc +236 -0
  61. data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
  62. data/doc/tutorials/06-Overview.rdoc +40 -0
  63. data/doc/videos.rdoc +69 -0
  64. data/ext/droby/dump.cc +175 -0
  65. data/ext/droby/extconf.rb +3 -0
  66. data/ext/graph/algorithm.cc +746 -0
  67. data/ext/graph/extconf.rb +7 -0
  68. data/ext/graph/graph.cc +529 -0
  69. data/ext/graph/graph.hh +183 -0
  70. data/ext/graph/iterator_sequence.hh +102 -0
  71. data/ext/graph/undirected_dfs.hh +226 -0
  72. data/ext/graph/undirected_graph.hh +421 -0
  73. data/lib/roby.rb +41 -0
  74. data/lib/roby/app.rb +870 -0
  75. data/lib/roby/app/rake.rb +56 -0
  76. data/lib/roby/app/run.rb +14 -0
  77. data/lib/roby/app/scripts/distributed.rb +13 -0
  78. data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
  79. data/lib/roby/app/scripts/replay.rb +31 -0
  80. data/lib/roby/app/scripts/results.rb +15 -0
  81. data/lib/roby/app/scripts/run.rb +26 -0
  82. data/lib/roby/app/scripts/server.rb +18 -0
  83. data/lib/roby/app/scripts/shell.rb +88 -0
  84. data/lib/roby/app/scripts/test.rb +40 -0
  85. data/lib/roby/basic_object.rb +151 -0
  86. data/lib/roby/config.rb +5 -0
  87. data/lib/roby/control.rb +747 -0
  88. data/lib/roby/decision_control.rb +17 -0
  89. data/lib/roby/distributed.rb +32 -0
  90. data/lib/roby/distributed/base.rb +440 -0
  91. data/lib/roby/distributed/communication.rb +871 -0
  92. data/lib/roby/distributed/connection_space.rb +592 -0
  93. data/lib/roby/distributed/distributed_object.rb +206 -0
  94. data/lib/roby/distributed/drb.rb +62 -0
  95. data/lib/roby/distributed/notifications.rb +539 -0
  96. data/lib/roby/distributed/peer.rb +550 -0
  97. data/lib/roby/distributed/protocol.rb +529 -0
  98. data/lib/roby/distributed/proxy.rb +343 -0
  99. data/lib/roby/distributed/subscription.rb +311 -0
  100. data/lib/roby/distributed/transaction.rb +498 -0
  101. data/lib/roby/event.rb +897 -0
  102. data/lib/roby/exceptions.rb +234 -0
  103. data/lib/roby/executives/simple.rb +30 -0
  104. data/lib/roby/graph.rb +166 -0
  105. data/lib/roby/interface.rb +390 -0
  106. data/lib/roby/log.rb +3 -0
  107. data/lib/roby/log/chronicle.rb +303 -0
  108. data/lib/roby/log/console.rb +72 -0
  109. data/lib/roby/log/data_stream.rb +197 -0
  110. data/lib/roby/log/dot.rb +279 -0
  111. data/lib/roby/log/event_stream.rb +151 -0
  112. data/lib/roby/log/file.rb +340 -0
  113. data/lib/roby/log/gui/basic_display.ui +83 -0
  114. data/lib/roby/log/gui/chronicle.rb +26 -0
  115. data/lib/roby/log/gui/chronicle_view.rb +40 -0
  116. data/lib/roby/log/gui/chronicle_view.ui +70 -0
  117. data/lib/roby/log/gui/data_displays.rb +172 -0
  118. data/lib/roby/log/gui/data_displays.ui +155 -0
  119. data/lib/roby/log/gui/notifications.rb +26 -0
  120. data/lib/roby/log/gui/relations.rb +248 -0
  121. data/lib/roby/log/gui/relations.ui +123 -0
  122. data/lib/roby/log/gui/relations_view.rb +185 -0
  123. data/lib/roby/log/gui/relations_view.ui +149 -0
  124. data/lib/roby/log/gui/replay.rb +327 -0
  125. data/lib/roby/log/gui/replay_controls.rb +200 -0
  126. data/lib/roby/log/gui/replay_controls.ui +259 -0
  127. data/lib/roby/log/gui/runtime.rb +130 -0
  128. data/lib/roby/log/hooks.rb +185 -0
  129. data/lib/roby/log/logger.rb +202 -0
  130. data/lib/roby/log/notifications.rb +244 -0
  131. data/lib/roby/log/plan_rebuilder.rb +470 -0
  132. data/lib/roby/log/relations.rb +1056 -0
  133. data/lib/roby/log/server.rb +550 -0
  134. data/lib/roby/log/sqlite.rb +47 -0
  135. data/lib/roby/log/timings.rb +164 -0
  136. data/lib/roby/plan-object.rb +247 -0
  137. data/lib/roby/plan.rb +762 -0
  138. data/lib/roby/planning.rb +13 -0
  139. data/lib/roby/planning/loops.rb +302 -0
  140. data/lib/roby/planning/model.rb +906 -0
  141. data/lib/roby/planning/task.rb +151 -0
  142. data/lib/roby/propagation.rb +562 -0
  143. data/lib/roby/query.rb +619 -0
  144. data/lib/roby/relations.rb +583 -0
  145. data/lib/roby/relations/conflicts.rb +70 -0
  146. data/lib/roby/relations/ensured.rb +20 -0
  147. data/lib/roby/relations/error_handling.rb +23 -0
  148. data/lib/roby/relations/events.rb +9 -0
  149. data/lib/roby/relations/executed_by.rb +193 -0
  150. data/lib/roby/relations/hierarchy.rb +239 -0
  151. data/lib/roby/relations/influence.rb +10 -0
  152. data/lib/roby/relations/planned_by.rb +63 -0
  153. data/lib/roby/robot.rb +7 -0
  154. data/lib/roby/standard_errors.rb +218 -0
  155. data/lib/roby/state.rb +5 -0
  156. data/lib/roby/state/events.rb +221 -0
  157. data/lib/roby/state/information.rb +55 -0
  158. data/lib/roby/state/pos.rb +110 -0
  159. data/lib/roby/state/shapes.rb +32 -0
  160. data/lib/roby/state/state.rb +353 -0
  161. data/lib/roby/support.rb +92 -0
  162. data/lib/roby/task-operations.rb +182 -0
  163. data/lib/roby/task.rb +1618 -0
  164. data/lib/roby/test/common.rb +399 -0
  165. data/lib/roby/test/distributed.rb +214 -0
  166. data/lib/roby/test/tasks/empty_task.rb +9 -0
  167. data/lib/roby/test/tasks/goto.rb +36 -0
  168. data/lib/roby/test/tasks/simple_task.rb +23 -0
  169. data/lib/roby/test/testcase.rb +519 -0
  170. data/lib/roby/test/tools.rb +160 -0
  171. data/lib/roby/thread_task.rb +87 -0
  172. data/lib/roby/transactions.rb +462 -0
  173. data/lib/roby/transactions/proxy.rb +292 -0
  174. data/lib/roby/transactions/updates.rb +139 -0
  175. data/plugins/fault_injection/History.txt +4 -0
  176. data/plugins/fault_injection/README.txt +37 -0
  177. data/plugins/fault_injection/Rakefile +18 -0
  178. data/plugins/fault_injection/TODO.txt +0 -0
  179. data/plugins/fault_injection/app.rb +52 -0
  180. data/plugins/fault_injection/fault_injection.rb +89 -0
  181. data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
  182. data/plugins/subsystems/README.txt +40 -0
  183. data/plugins/subsystems/Rakefile +18 -0
  184. data/plugins/subsystems/app.rb +171 -0
  185. data/plugins/subsystems/test/app/README +24 -0
  186. data/plugins/subsystems/test/app/Rakefile +8 -0
  187. data/plugins/subsystems/test/app/config/app.yml +71 -0
  188. data/plugins/subsystems/test/app/config/init.rb +9 -0
  189. data/plugins/subsystems/test/app/config/roby.yml +3 -0
  190. data/plugins/subsystems/test/app/planners/main.rb +20 -0
  191. data/plugins/subsystems/test/app/scripts/distributed +3 -0
  192. data/plugins/subsystems/test/app/scripts/replay +3 -0
  193. data/plugins/subsystems/test/app/scripts/results +3 -0
  194. data/plugins/subsystems/test/app/scripts/run +3 -0
  195. data/plugins/subsystems/test/app/scripts/server +3 -0
  196. data/plugins/subsystems/test/app/scripts/shell +3 -0
  197. data/plugins/subsystems/test/app/scripts/test +3 -0
  198. data/plugins/subsystems/test/app/tasks/services.rb +15 -0
  199. data/plugins/subsystems/test/test_subsystems.rb +71 -0
  200. data/test/distributed/test_communication.rb +178 -0
  201. data/test/distributed/test_connection.rb +282 -0
  202. data/test/distributed/test_execution.rb +373 -0
  203. data/test/distributed/test_mixed_plan.rb +341 -0
  204. data/test/distributed/test_plan_notifications.rb +238 -0
  205. data/test/distributed/test_protocol.rb +516 -0
  206. data/test/distributed/test_query.rb +102 -0
  207. data/test/distributed/test_remote_plan.rb +491 -0
  208. data/test/distributed/test_transaction.rb +463 -0
  209. data/test/mockups/tasks.rb +27 -0
  210. data/test/planning/test_loops.rb +380 -0
  211. data/test/planning/test_model.rb +427 -0
  212. data/test/planning/test_task.rb +106 -0
  213. data/test/relations/test_conflicts.rb +42 -0
  214. data/test/relations/test_ensured.rb +38 -0
  215. data/test/relations/test_executed_by.rb +149 -0
  216. data/test/relations/test_hierarchy.rb +158 -0
  217. data/test/relations/test_planned_by.rb +54 -0
  218. data/test/suite_core.rb +24 -0
  219. data/test/suite_distributed.rb +9 -0
  220. data/test/suite_planning.rb +3 -0
  221. data/test/suite_relations.rb +8 -0
  222. data/test/test_bgl.rb +508 -0
  223. data/test/test_control.rb +399 -0
  224. data/test/test_event.rb +894 -0
  225. data/test/test_exceptions.rb +592 -0
  226. data/test/test_interface.rb +37 -0
  227. data/test/test_log.rb +114 -0
  228. data/test/test_log_server.rb +132 -0
  229. data/test/test_plan.rb +584 -0
  230. data/test/test_propagation.rb +210 -0
  231. data/test/test_query.rb +266 -0
  232. data/test/test_relations.rb +180 -0
  233. data/test/test_state.rb +414 -0
  234. data/test/test_support.rb +16 -0
  235. data/test/test_task.rb +938 -0
  236. data/test/test_testcase.rb +122 -0
  237. data/test/test_thread_task.rb +73 -0
  238. data/test/test_transactions.rb +569 -0
  239. data/test/test_transactions_proxy.rb +198 -0
  240. 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
+