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.
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
+