roby 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. data/History.txt +7 -5
  2. data/Manifest.txt +91 -16
  3. data/README.txt +24 -24
  4. data/Rakefile +92 -64
  5. data/app/config/app.yml +42 -43
  6. data/app/config/init.rb +26 -0
  7. data/benchmark/alloc_misc.rb +123 -0
  8. data/benchmark/discovery_latency.rb +67 -0
  9. data/benchmark/garbage_collection.rb +48 -0
  10. data/benchmark/genom.rb +31 -0
  11. data/benchmark/transactions.rb +62 -0
  12. data/bin/roby +1 -1
  13. data/bin/roby-log +16 -6
  14. data/doc/guide/.gitignore +2 -0
  15. data/doc/guide/config.yaml +34 -0
  16. data/doc/guide/ext/init.rb +14 -0
  17. data/doc/guide/ext/previous_next.rb +40 -0
  18. data/doc/guide/ext/rdoc_links.rb +33 -0
  19. data/doc/guide/index.rdoc +16 -0
  20. data/doc/guide/overview.rdoc +62 -0
  21. data/doc/guide/plan_modifications.rdoc +67 -0
  22. data/doc/guide/src/abstraction/achieve_with.page +8 -0
  23. data/doc/guide/src/abstraction/forwarding.page +8 -0
  24. data/doc/guide/src/abstraction/hierarchy.page +19 -0
  25. data/doc/guide/src/abstraction/index.page +28 -0
  26. data/doc/guide/src/abstraction/task_models.page +13 -0
  27. data/doc/guide/src/basics.template +6 -0
  28. data/doc/guide/src/basics/app.page +139 -0
  29. data/doc/guide/src/basics/code_examples.page +33 -0
  30. data/doc/guide/src/basics/dry.page +69 -0
  31. data/doc/guide/src/basics/errors.page +443 -0
  32. data/doc/guide/src/basics/events.page +179 -0
  33. data/doc/guide/src/basics/hierarchy.page +275 -0
  34. data/doc/guide/src/basics/index.page +11 -0
  35. data/doc/guide/src/basics/log_replay/goForward_1.png +0 -0
  36. data/doc/guide/src/basics/log_replay/goForward_2.png +0 -0
  37. data/doc/guide/src/basics/log_replay/goForward_3.png +0 -0
  38. data/doc/guide/src/basics/log_replay/goForward_4.png +0 -0
  39. data/doc/guide/src/basics/log_replay/goForward_5.png +0 -0
  40. data/doc/guide/src/basics/log_replay/hierarchy_error_1.png +0 -0
  41. data/doc/guide/src/basics/log_replay/hierarchy_error_2.png +0 -0
  42. data/doc/guide/src/basics/log_replay/hierarchy_error_3.png +0 -0
  43. data/doc/guide/src/basics/log_replay/plan_repair_1.png +0 -0
  44. data/doc/guide/src/basics/log_replay/plan_repair_2.png +0 -0
  45. data/doc/guide/src/basics/log_replay/plan_repair_3.png +0 -0
  46. data/doc/guide/src/basics/log_replay/plan_repair_4.png +0 -0
  47. data/doc/guide/src/basics/log_replay/roby_log_main_window.png +0 -0
  48. data/doc/guide/src/basics/log_replay/roby_log_relation_window.png +0 -0
  49. data/doc/guide/src/basics/log_replay/roby_replay_event_representation.png +0 -0
  50. data/doc/guide/src/basics/plan_objects.page +71 -0
  51. data/doc/guide/src/basics/relations_display.page +203 -0
  52. data/doc/guide/src/basics/roby_cycle_overview.png +0 -0
  53. data/doc/guide/src/basics/shell.page +102 -0
  54. data/doc/guide/src/basics/summary.page +32 -0
  55. data/doc/guide/src/basics/tasks.page +357 -0
  56. data/doc/guide/src/basics_shell_header.txt +16 -0
  57. data/doc/guide/src/cycle/cycle-overview.png +0 -0
  58. data/doc/guide/src/cycle/cycle-overview.svg +208 -0
  59. data/doc/guide/src/cycle/error_handling.page +168 -0
  60. data/doc/guide/src/cycle/error_instantaneous_repair.png +0 -0
  61. data/doc/guide/src/cycle/error_instantaneous_repair.svg +1224 -0
  62. data/doc/guide/src/cycle/garbage_collection.page +10 -0
  63. data/doc/guide/src/cycle/index.page +23 -0
  64. data/doc/guide/src/cycle/propagation.page +154 -0
  65. data/doc/guide/src/cycle/propagation_diamond.png +0 -0
  66. data/doc/guide/src/cycle/propagation_diamond.svg +1279 -0
  67. data/doc/guide/src/default.css +319 -0
  68. data/doc/guide/src/default.template +74 -0
  69. data/doc/guide/src/htmldoc.metainfo +20 -0
  70. data/doc/guide/src/htmldoc.virtual +18 -0
  71. data/doc/guide/src/images/bodybg.png +0 -0
  72. data/doc/guide/src/images/contbg.png +0 -0
  73. data/doc/guide/src/images/footerbg.png +0 -0
  74. data/doc/guide/src/images/gradient1.png +0 -0
  75. data/doc/guide/src/images/gradient2.png +0 -0
  76. data/doc/guide/src/index.page +7 -0
  77. data/doc/guide/src/introduction/index.page +29 -0
  78. data/doc/guide/src/introduction/install.page +133 -0
  79. data/doc/{papers.rdoc → guide/src/introduction/publications.page} +5 -2
  80. data/doc/{videos.rdoc → guide/src/introduction/videos.page} +4 -2
  81. data/doc/guide/src/plugins/fault_tolerance.page +44 -0
  82. data/doc/guide/src/plugins/index.page +11 -0
  83. data/doc/guide/src/plugins/subsystems.page +45 -0
  84. data/doc/guide/src/relations/dependency.page +89 -0
  85. data/doc/guide/src/relations/index.page +12 -0
  86. data/doc/misc/update_github +24 -0
  87. data/doc/tutorials/02-GoForward.rdoc +3 -3
  88. data/ext/graph/graph.cc +46 -0
  89. data/lib/roby.rb +57 -22
  90. data/lib/roby/app.rb +132 -112
  91. data/lib/roby/app/plugins/rake.rb +21 -0
  92. data/lib/roby/app/rake.rb +0 -7
  93. data/lib/roby/app/run.rb +1 -1
  94. data/lib/roby/app/scripts/distributed.rb +1 -2
  95. data/lib/roby/app/scripts/generate/bookmarks.rb +1 -1
  96. data/lib/roby/app/scripts/results.rb +2 -1
  97. data/lib/roby/app/scripts/run.rb +6 -2
  98. data/lib/roby/app/scripts/shell.rb +11 -11
  99. data/lib/roby/config.rb +1 -1
  100. data/lib/roby/decision_control.rb +62 -3
  101. data/lib/roby/distributed.rb +4 -0
  102. data/lib/roby/distributed/base.rb +8 -0
  103. data/lib/roby/distributed/communication.rb +12 -8
  104. data/lib/roby/distributed/connection_space.rb +61 -44
  105. data/lib/roby/distributed/distributed_object.rb +1 -1
  106. data/lib/roby/distributed/notifications.rb +22 -30
  107. data/lib/roby/distributed/peer.rb +13 -8
  108. data/lib/roby/distributed/proxy.rb +5 -5
  109. data/lib/roby/distributed/subscription.rb +4 -4
  110. data/lib/roby/distributed/transaction.rb +3 -3
  111. data/lib/roby/event.rb +176 -110
  112. data/lib/roby/exceptions.rb +12 -4
  113. data/lib/roby/execution_engine.rb +1604 -0
  114. data/lib/roby/external_process_task.rb +225 -0
  115. data/lib/roby/graph.rb +0 -6
  116. data/lib/roby/interface.rb +221 -137
  117. data/lib/roby/log/console.rb +5 -3
  118. data/lib/roby/log/data_stream.rb +94 -16
  119. data/lib/roby/log/dot.rb +8 -8
  120. data/lib/roby/log/event_stream.rb +13 -3
  121. data/lib/roby/log/file.rb +43 -18
  122. data/lib/roby/log/gui/basic_display_ui.rb +89 -0
  123. data/lib/roby/log/gui/chronicle_view_ui.rb +90 -0
  124. data/lib/roby/log/gui/data_displays.rb +4 -5
  125. data/lib/roby/log/gui/data_displays_ui.rb +146 -0
  126. data/lib/roby/log/gui/relations.rb +18 -18
  127. data/lib/roby/log/gui/relations_ui.rb +120 -0
  128. data/lib/roby/log/gui/relations_view_ui.rb +144 -0
  129. data/lib/roby/log/gui/replay.rb +41 -13
  130. data/lib/roby/log/gui/replay_controls.rb +3 -0
  131. data/lib/roby/log/gui/replay_controls.ui +133 -110
  132. data/lib/roby/log/gui/replay_controls_ui.rb +249 -0
  133. data/lib/roby/log/hooks.rb +19 -18
  134. data/lib/roby/log/logger.rb +7 -6
  135. data/lib/roby/log/notifications.rb +4 -4
  136. data/lib/roby/log/plan_rebuilder.rb +20 -22
  137. data/lib/roby/log/relations.rb +44 -16
  138. data/lib/roby/log/server.rb +1 -4
  139. data/lib/roby/log/timings.rb +88 -19
  140. data/lib/roby/plan-object.rb +135 -11
  141. data/lib/roby/plan.rb +408 -224
  142. data/lib/roby/planning/loops.rb +32 -25
  143. data/lib/roby/planning/model.rb +157 -51
  144. data/lib/roby/planning/task.rb +47 -20
  145. data/lib/roby/query.rb +128 -92
  146. data/lib/roby/relations.rb +254 -136
  147. data/lib/roby/relations/conflicts.rb +6 -9
  148. data/lib/roby/relations/dependency.rb +358 -0
  149. data/lib/roby/relations/ensured.rb +0 -1
  150. data/lib/roby/relations/error_handling.rb +0 -1
  151. data/lib/roby/relations/events.rb +0 -2
  152. data/lib/roby/relations/executed_by.rb +26 -11
  153. data/lib/roby/relations/planned_by.rb +14 -14
  154. data/lib/roby/robot.rb +46 -0
  155. data/lib/roby/schedulers/basic.rb +34 -0
  156. data/lib/roby/standalone.rb +4 -0
  157. data/lib/roby/standard_errors.rb +21 -15
  158. data/lib/roby/state/events.rb +5 -4
  159. data/lib/roby/support.rb +107 -6
  160. data/lib/roby/task-operations.rb +23 -19
  161. data/lib/roby/task.rb +522 -148
  162. data/lib/roby/task_index.rb +80 -0
  163. data/lib/roby/test/common.rb +283 -44
  164. data/lib/roby/test/distributed.rb +53 -37
  165. data/lib/roby/test/testcase.rb +9 -204
  166. data/lib/roby/test/tools.rb +3 -3
  167. data/lib/roby/transactions.rb +154 -111
  168. data/lib/roby/transactions/proxy.rb +40 -7
  169. data/manifest.xml +20 -0
  170. data/plugins/fault_injection/README.txt +0 -3
  171. data/plugins/fault_injection/Rakefile +2 -8
  172. data/plugins/fault_injection/app.rb +1 -1
  173. data/plugins/fault_injection/fault_injection.rb +3 -3
  174. data/plugins/fault_injection/test/test_fault_injection.rb +19 -25
  175. data/plugins/subsystems/README.txt +0 -3
  176. data/plugins/subsystems/Rakefile +2 -7
  177. data/plugins/subsystems/app.rb +27 -16
  178. data/plugins/subsystems/test/app/config/init.rb +3 -0
  179. data/plugins/subsystems/test/app/planners/main.rb +1 -1
  180. data/plugins/subsystems/test/app/tasks/services.rb +1 -1
  181. data/plugins/subsystems/test/test_subsystems.rb +23 -16
  182. data/test/distributed/test_communication.rb +32 -15
  183. data/test/distributed/test_connection.rb +28 -26
  184. data/test/distributed/test_execution.rb +59 -54
  185. data/test/distributed/test_mixed_plan.rb +34 -34
  186. data/test/distributed/test_plan_notifications.rb +26 -26
  187. data/test/distributed/test_protocol.rb +57 -48
  188. data/test/distributed/test_query.rb +11 -7
  189. data/test/distributed/test_remote_plan.rb +71 -71
  190. data/test/distributed/test_transaction.rb +50 -47
  191. data/test/mockups/external_process +28 -0
  192. data/test/planning/test_loops.rb +163 -119
  193. data/test/planning/test_model.rb +3 -3
  194. data/test/planning/test_task.rb +27 -7
  195. data/test/relations/test_conflicts.rb +3 -3
  196. data/test/relations/test_dependency.rb +324 -0
  197. data/test/relations/test_ensured.rb +2 -2
  198. data/test/relations/test_executed_by.rb +94 -19
  199. data/test/relations/test_planned_by.rb +11 -9
  200. data/test/suite_core.rb +6 -3
  201. data/test/suite_distributed.rb +1 -0
  202. data/test/suite_planning.rb +1 -0
  203. data/test/suite_relations.rb +2 -2
  204. data/test/tasks/test_external_process.rb +126 -0
  205. data/test/{test_thread_task.rb → tasks/test_thread_task.rb} +17 -20
  206. data/test/test_bgl.rb +21 -1
  207. data/test/test_event.rb +229 -155
  208. data/test/test_exceptions.rb +79 -80
  209. data/test/test_execution_engine.rb +987 -0
  210. data/test/test_gui.rb +1 -1
  211. data/test/test_interface.rb +11 -5
  212. data/test/test_log.rb +18 -7
  213. data/test/test_log_server.rb +1 -0
  214. data/test/test_plan.rb +229 -395
  215. data/test/test_query.rb +193 -35
  216. data/test/test_relations.rb +88 -8
  217. data/test/test_state.rb +55 -37
  218. data/test/test_support.rb +1 -1
  219. data/test/test_task.rb +371 -218
  220. data/test/test_testcase.rb +32 -16
  221. data/test/test_transactions.rb +211 -170
  222. data/test/test_transactions_proxy.rb +37 -19
  223. metadata +169 -71
  224. data/.gitignore +0 -29
  225. data/doc/styles/allison.css +0 -314
  226. data/doc/styles/allison.js +0 -316
  227. data/doc/styles/allison.rb +0 -276
  228. data/doc/styles/jamis.rb +0 -593
  229. data/lib/roby/control.rb +0 -746
  230. data/lib/roby/executives/simple.rb +0 -30
  231. data/lib/roby/propagation.rb +0 -562
  232. data/lib/roby/relations/hierarchy.rb +0 -239
  233. data/lib/roby/transactions/updates.rb +0 -139
  234. data/test/relations/test_hierarchy.rb +0 -158
  235. data/test/test_control.rb +0 -399
  236. data/test/test_propagation.rb +0 -210
@@ -1,4 +1,3 @@
1
-
2
1
  module Roby
3
2
  module TaskStructure
4
3
  Roby::Task.inherited_enumerable(:conflicting_model, :conflicting_models) { ValueSet.new }
@@ -10,7 +9,7 @@ module Roby
10
9
 
11
10
  def conflicts_with?(model)
12
11
  each_conflicting_model do |m|
13
- return true if m == model
12
+ return true if model <= m
14
13
  end
15
14
  false
16
15
  end
@@ -19,7 +18,7 @@ module Roby
19
18
 
20
19
  relation :Conflicts, :noinfo => true do
21
20
  def conflicts_with(task)
22
- task.event(:stop).add_precedence event(:start)
21
+ # task.event(:stop).add_precedence event(:start)
23
22
  add_conflicts(task)
24
23
  end
25
24
 
@@ -43,16 +42,14 @@ module Roby
43
42
  end
44
43
 
45
44
  if result
46
- Roby.decision_control.conflict(task, result)
45
+ plan.control.conflict(task, result)
47
46
  end
48
47
 
49
48
  # Add the needed conflict relations
50
49
  models = task.class.conflicting_models
51
- for model in models
52
- if candidates = plan.task_index.by_model[model]
53
- for t in candidates
54
- t.conflicts_with task if t.pending?
55
- end
50
+ for model in models
51
+ for t in plan.find_tasks(model)
52
+ t.conflicts_with task if t.pending? && t != task
56
53
  end
57
54
  end
58
55
  end
@@ -0,0 +1,358 @@
1
+ module Roby::TaskStructure
2
+ relation :Dependency, :child_name => :child, :parent_name => :parent_task do
3
+ ##
4
+ # :method: add_child(v, info)
5
+ # Adds a new child to +v+. You should use #realized_by instead.
6
+
7
+ def realizes?(obj)
8
+ Roby.warn_deprecated "#realizes? is deprecated. Use #depended_upon_by? instead"
9
+ depended_upon_by?(obj)
10
+ end
11
+ def realized_by?(obj)
12
+ Roby.warn_deprecated "#realized_by? is deprecated. Use #depends_on?(obj, false) instead"
13
+ depends_on?(obj, false)
14
+ end
15
+
16
+ # True if +obj+ is a parent of this object in the hierarchy relation
17
+ # (+obj+ is realized by +self+)
18
+ def depended_upon_by?(obj); parent_object?(obj, Dependency) end
19
+
20
+ # True if +obj+ is a child of this object in the hierarchy relation.
21
+ # If +recursive+ is true, take into account the whole subgraph.
22
+ # Otherwise, only direct children are checked.
23
+ def depends_on?(obj, recursive = true)
24
+ if recursive
25
+ generated_subgraph(Dependency).include?(obj)
26
+ else
27
+ child_object?(obj, Dependency)
28
+ end
29
+ end
30
+ # The set of parent objects in the Dependency relation
31
+ def parents; parent_objects(Dependency) end
32
+ # The set of child objects in the Dependency relation
33
+ def children; child_objects(Dependency) end
34
+
35
+ def roles_of(child)
36
+ info = self[child, Dependency]
37
+ info[:roles]
38
+ end
39
+
40
+ def realized_by(task, options = {})
41
+ Roby.warn_deprecated "#realized_by is deprecated. Use #depends_on instead"
42
+ depends_on(task, options)
43
+ end
44
+
45
+ # Adds +task+ as a child of +self+ in the Dependency relation. The
46
+ # following options are allowed:
47
+ #
48
+ # success:: the list of success events. The default is [:success]
49
+ # failure:: the list of failing events. The default is [:failed]
50
+ # model::
51
+ # a <tt>[task_model, arguments]</tt> pair which defines the task
52
+ # model the parent is expecting. The default value is to get these
53
+ # parameters from +task+
54
+ #
55
+ # The +success+ set describes the events of the child task that are
56
+ # _required_ by the parent task. More specifically, the child task
57
+ # remains useful for the parent task as long as none of these events are
58
+ # emitted. By default, it is the +success+ event. Of course, an error
59
+ # condition is encountered when all events of +success+ become
60
+ # unreachable. In addition, the relation is removed if the
61
+ # +remove_when_done+ flag is set to true (false by default).
62
+ #
63
+ # The +failure+ set describes the events of the child task which are an
64
+ # error condition from the parent task point of view.
65
+ #
66
+ # In both error cases, a +ChildFailedError+ exception is raised.
67
+ def depends_on(task, options = {})
68
+ options = validate_options options,
69
+ :model => [task.model, task.meaningful_arguments],
70
+ :success => [:success],
71
+ :failure => [],
72
+ :remove_when_done => true,
73
+ :roles => nil,
74
+ :role => nil
75
+
76
+ roles = options[:roles] || ValueSet.new
77
+ if role = options.delete(:role)
78
+ roles << role.to_str
79
+ end
80
+ roles = roles.map { |r| r.to_str }
81
+ options[:roles] = roles.to_set
82
+
83
+ options[:success] = Array[*options[:success]]
84
+ options[:failure] = Array[*options[:failure]]
85
+ options[:success] -= options[:failure]
86
+
87
+ # Validate failure and success event names
88
+ options[:success].each { |ev| task.event(ev) }
89
+ options[:failure].each { |ev| task.event(ev) }
90
+
91
+ options[:model] = [options[:model], {}] unless Array === options[:model]
92
+ required_model, required_args = *options[:model]
93
+ if !required_args.respond_to?(:to_hash)
94
+ raise ArgumentError, "argument specification must be a hash, got #{required_args} (#{required_args.class})"
95
+ elsif !task.fullfills?(required_model, required_args)
96
+ raise ArgumentError, "task #{task} does not fullfill the provided model #{options[:model]}"
97
+ end
98
+
99
+ # Check if there is already a dependency link. If it is the case,
100
+ # merge the options. Otherwise, just add.
101
+ add_child(task, options)
102
+ self
103
+ end
104
+
105
+ # Set up the event gathering needed by Dependency.check_structure
106
+ def added_child_object(child, relations, info) # :nodoc:
107
+ super if defined? super
108
+ if relations.include?(Dependency) && !respond_to?(:__getobj__) && !child.respond_to?(:__getobj__)
109
+ events = info[:success].map do |ev|
110
+ ev = child.event(ev)
111
+ ev.if_unreachable { Dependency.interesting_events << ev }
112
+ ev
113
+ end
114
+ events.concat info[:failure].map { |ev| child.event(ev) }
115
+ Roby::EventGenerator.gather_events(Dependency.interesting_events, events)
116
+ end
117
+ end
118
+
119
+ # Return the set of this task children for which the :start event has
120
+ # no parent in CausalLinks
121
+ def first_children
122
+ result = ValueSet.new
123
+
124
+ generated_subgraph(Dependency).each do |task|
125
+ next if task == self
126
+ if task.event(:start).root?(Roby::EventStructure::CausalLink)
127
+ result << task
128
+ end
129
+ end
130
+ result
131
+ end
132
+
133
+ # The set of events that are needed by the parent tasks
134
+ def fullfilled_events
135
+ needed = ValueSet.new
136
+ merged_relations(:each_parent_task, false) do |myself, parent|
137
+ needed.merge(parent[myself, Dependency][:success])
138
+ end
139
+ needed
140
+ end
141
+
142
+ # Return [tags, arguments] where +tags+ is a list of task models which
143
+ # are required by the parent tasks of this task, and arguments the
144
+ # required arguments
145
+ #
146
+ # If there is a task class in the required models, it is always the
147
+ # first element of +tags+
148
+ def fullfilled_model
149
+ model, tags, arguments = Roby::Task, [], {}
150
+
151
+ has_parent = false
152
+ merged_relations(:each_parent_task, false) do |myself, parent|
153
+ has_parent = true
154
+
155
+ required_models, required_arguments = parent[myself, Dependency][:model]
156
+ required_models = [required_models] if !required_models.respond_to?(:to_ary)
157
+
158
+ for m in required_models
159
+ if m.kind_of?(Roby::TaskModelTag)
160
+ tags << m
161
+ elsif m.has_ancestor?(model)
162
+ model = m
163
+ elsif !model.has_ancestor?(m)
164
+ raise Roby::ModelViolation, "inconsistency in fullfilled models: #{model} and #{m} are incompatible"
165
+ end
166
+ end
167
+
168
+ arguments.merge!(required_arguments) do |name, old, new|
169
+ if old != new
170
+ raise Roby::ModelViolation, "inconsistency in fullfilled models: #{old} and #{new}"
171
+ end
172
+ old
173
+ end
174
+ end
175
+
176
+ if !has_parent
177
+ [[self.model], self.arguments]
178
+ else
179
+ tags.unshift(model)
180
+ [tags, arguments]
181
+ end
182
+ end
183
+
184
+ # Remove all children that have successfully finished
185
+ def remove_finished_children
186
+ # We call #to_a to get a copy of children, since we will remove
187
+ # children in the block. Note that we can't use #delete_if here
188
+ # since #children is a relation enumerator (not the relation list
189
+ # itself)
190
+ children.to_a.each do |child|
191
+ success_events = self[child, Dependency][:success]
192
+ if success_events.any? { |ev| child.event(ev).happened? }
193
+ remove_child(child)
194
+ end
195
+ end
196
+ end
197
+ end
198
+ Hierarchy = Dependency
199
+
200
+ def Dependency.merge_info(parent, child, opt1, opt2)
201
+ if opt1[:remove_when_done] != opt2[:remove_when_done]
202
+ raise Roby::ModelViolation, "incompatible dependency specification: trying to change the value of +remove_when_done+"
203
+ end
204
+
205
+ result = { :remove_when_done => opt1[:remove_when_done] }
206
+
207
+ # Remove from :success the events listed in :failure. We can't remove
208
+ # the events in opt1[:success] that would lead to having opt2[:success]
209
+ # unreachable though ...
210
+ result[:success] = (opt1[:success] - opt2[:failure]) | (opt2[:success] - opt1[:failure])
211
+ result[:failure] = opt1[:failure] | opt2[:failure]
212
+ if result[:success].empty? && (!opt1[:success].empty? || !opt2[:success].empty?)
213
+ raise Roby::ModelViolation, "incompatibility between the :success and :failure sets of #{opt1} and #{opt2}"
214
+ end
215
+
216
+ # Check model compatibility
217
+ model1, arguments1 = opt1[:model]
218
+ model2, arguments2 = opt2[:model]
219
+ if model1 <= model2
220
+ result[:model] = [model1, {}]
221
+ elsif model2 < model1
222
+ result[:model] = [model2, {}]
223
+ else
224
+ # Find the most generic model that +task+ fullfills and that
225
+ # includes both +model1+ and +model2+
226
+ klass = child.model
227
+ while klass != Roby::Task && (klass <= model1 && klass <= model2)
228
+ candidate = klass
229
+ klass = klass.superclass
230
+ end
231
+ # We should always have a solution, as +task+ fullfills both model1 and model2
232
+ result[:model] = [candidate, []]
233
+ end
234
+
235
+ # Merge arguments
236
+ result[:model][1] = arguments1.merge(arguments2) do |key, old_value, new_value|
237
+ if old_value != new_value
238
+ raise Roby::ModelViolation, "incompatible argument constraint #{old_value} and #{new_value} for #{key}"
239
+ end
240
+ old_value
241
+ end
242
+
243
+ # Finally, merge the roles (the easy part ;-))
244
+ result[:roles] = opt1[:roles] | opt2[:roles]
245
+
246
+ result
247
+ end
248
+
249
+ # Checks the structure of +plan+ w.r.t. the constraints of the hierarchy
250
+ # relations. It returns an array of ChildFailedError for all failed
251
+ # hierarchy relations
252
+ def Dependency.check_structure(plan)
253
+ result = []
254
+
255
+ events = Hierarchy.interesting_events
256
+ return result if events.empty? && failing_tasks.empty?
257
+
258
+ # Get the set of tasks for which a possible failure has been
259
+ # registered The tasks that are failing the hierarchy requirements
260
+ # are registered in Hierarchy.failing_tasks. The interesting_events
261
+ # set is cleared at cycle end (see below)
262
+ tasks = events.inject(failing_tasks) do |set, event|
263
+ if event.respond_to?(:generator)
264
+ set << event.generator.task
265
+ else
266
+ set << event.task
267
+ end
268
+ set
269
+ end
270
+
271
+ @failing_tasks = ValueSet.new
272
+ tasks.each do |child|
273
+ # Check if the task has been removed from the plan
274
+ next unless child.plan
275
+
276
+ has_error = false
277
+ child.each_parent_task do |parent|
278
+ next unless parent.self_owned?
279
+ next if parent.finished? || parent.finishing?
280
+
281
+ options = parent[child, Hierarchy]
282
+ success = options[:success]
283
+ failure = options[:failure]
284
+
285
+ if success.any? { |e| child.event(e).happened? }
286
+ if options[:remove_when_done]
287
+ parent.remove_child child
288
+ end
289
+ elsif failing_event = failure.find { |e| child.event(e).happened? }
290
+ result << Roby::ChildFailedError.new(parent, child.event(failing_event).last)
291
+ failing_tasks << child
292
+ elsif success.all? { |e| child.event(e).unreachable? }
293
+ failing_event = success.find { |e| child.event(e).unreachability_reason }
294
+ failing_event = child.event(failing_event).unreachability_reason
295
+ if !failing_event
296
+ failing_event = child.event(success.find { |e| child.event(e) })
297
+ end
298
+ result << Roby::ChildFailedError.new(parent, failing_event)
299
+ failing_tasks << child
300
+ end
301
+ end
302
+ end
303
+
304
+ events.clear
305
+ result
306
+ end
307
+
308
+ class << Dependency
309
+ # The set of events that have been fired in this cycle and are involved in a Hierarchy relation
310
+ attribute(:interesting_events) { Array.new }
311
+
312
+ # The set of tasks that are currently failing
313
+ attribute(:failing_tasks) { ValueSet.new }
314
+ end
315
+ end
316
+
317
+ module Roby
318
+ # This exception is raised when a {hierarchy relation}[classes/Roby/TaskStructure/Hierarchy.html] fails
319
+ class ChildFailedError < LocalizedError
320
+ # The parent in the relation
321
+ attr_reader :parent
322
+ # The child in the relation
323
+ def child; failed_task end
324
+ # The relation parameters (i.e. the hash given to #depends_on)
325
+ attr_reader :relation
326
+
327
+ # The event which is the cause of this error. This is either the task
328
+ # source of a failure event, or the reason why a positive event has
329
+ # become unreachable (if there is one)
330
+ def initialize(parent, event)
331
+ super(event)
332
+ @parent = parent
333
+ @relation = parent[child, TaskStructure::Dependency]
334
+ end
335
+
336
+ def pretty_print(pp) # :nodoc:
337
+ super
338
+ pp.breakable
339
+ pp.breakable
340
+ pp.text "The failed relation is"
341
+ pp.breakable
342
+ pp.nest(2) do
343
+ pp.text " "
344
+ parent.pretty_print pp
345
+ pp.breakable
346
+ pp.text "depends_on "
347
+ child.pretty_print pp
348
+ end
349
+ end
350
+ def backtrace; [] end
351
+
352
+ # True if +obj+ is involved in this exception
353
+ def involved_plan_object?(obj)
354
+ super || obj == parent
355
+ end
356
+ end
357
+ end
358
+
@@ -1,4 +1,3 @@
1
- require 'roby/event'
2
1
  module Roby::EventStructure
3
2
  relation :EnsuredEvent, :noinfo => true do
4
3
  def calling(context)
@@ -1,4 +1,3 @@
1
-
2
1
  module Roby::TaskStructure
3
2
  class Roby::TaskEventGenerator
4
3
  # Mark this event as being handled by the task +task+
@@ -1,5 +1,3 @@
1
- require 'roby/event'
2
-
3
1
  module Roby::EventStructure
4
2
  relation :Signal, :noinfo => true
5
3
  relation :Forwarding, :noinfo => true
@@ -1,5 +1,3 @@
1
- require 'roby/task'
2
-
3
1
  module Roby::TaskStructure
4
2
  # This module defines model-level definition of execution agent, for
5
3
  # instance to Roby::Task
@@ -27,8 +25,8 @@ module Roby::TaskStructure
27
25
  #
28
26
  # for all instances of TaskModel. The actual job is done in the
29
27
  # ExecutionAgentSpawn module
30
- def executed_by(agent)
31
- @execution_agent = agent
28
+ def executed_by(agent_model, arguments = Hash.new)
29
+ @execution_agent = [agent_model, arguments]
32
30
  end
33
31
  end
34
32
 
@@ -67,7 +65,7 @@ module Roby::TaskStructure
67
65
  # are already set up
68
66
  if running?
69
67
  Roby::Distributed.update(self) do
70
- agent.forward(:stop, self, :aborted)
68
+ agent.forward_to(:stop, self, :aborted)
71
69
  end
72
70
  else
73
71
  on(:start) do
@@ -76,7 +74,7 @@ module Roby::TaskStructure
76
74
  # actually an execution agent
77
75
  if execution_agent
78
76
  Roby::Distributed.update(self) do
79
- execution_agent.forward(:stop, self, :aborted)
77
+ execution_agent.forward_to(:stop, self, :aborted)
80
78
  end
81
79
  end
82
80
  end
@@ -109,9 +107,10 @@ module Roby::TaskStructure
109
107
  # agent model (see ModelLevelExecutionAgent), either by reusing one
110
108
  # that is already in the plan, or by creating a new one.
111
109
  def ExecutionAgent.spawn(task)
112
- agent_model = task.model.execution_agent
110
+ agent_model, arguments = task.model.execution_agent
113
111
  candidates = task.plan.find_tasks.
114
112
  with_model(agent_model).
113
+ with_arguments(arguments).
115
114
  self_owned.
116
115
  not_finished
117
116
 
@@ -119,7 +118,7 @@ module Roby::TaskStructure
119
118
 
120
119
  if candidates.empty?
121
120
  begin
122
- agent = agent_model.new
121
+ agent = agent_model.new(arguments)
123
122
  agent.on(:stop) do
124
123
  agent.each_executed_task do |task|
125
124
  if task.running?
@@ -131,7 +130,7 @@ module Roby::TaskStructure
131
130
  end
132
131
  end
133
132
  rescue Exception => e
134
- Roby::Propagation.add_error(ExecutionAgentSpawningFailed.new(task, agent_model, e))
133
+ task.plan.engine.add_error(ExecutionAgentSpawningFailed.new(task, agent_model, e))
135
134
  end
136
135
  else
137
136
  running, pending = candidates.partition { |t| t.running? }
@@ -150,7 +149,23 @@ module Roby::TaskStructure
150
149
  def calling(context)
151
150
  super if defined? super
152
151
  return unless symbol == :start
153
- return unless agent = task.execution_agent
152
+
153
+ agent = task.execution_agent
154
+ if !agent
155
+ if task.model.execution_agent
156
+ raise CommandFailed.new(nil, self), "the model of #{task} requires an execution agent, but the task has none"
157
+ else
158
+ return
159
+ end
160
+ end
161
+
162
+ # Check that the agent matches the model
163
+ agent_model, arguments = task.model.execution_agent
164
+ if agent_model
165
+ if !agent.fullfills?(agent_model, arguments)
166
+ raise CommandFailed.new(nil, self), "the execution agent #{agent} does not match the required model #{agent_model}, #{arguments}"
167
+ end
168
+ end
154
169
 
155
170
  if agent.finished? || agent.finishing?
156
171
  raise CommandFailed.new(nil, self), "task #{task} has an execution agent but it is dead"
@@ -173,7 +188,7 @@ module Roby::TaskStructure
173
188
  module ExecutionAgentSpawn
174
189
  # Hook into plan discovery to add execution agents to new tasks.
175
190
  # See ExecutionAgentSpawn.spawn
176
- def discovered_tasks(tasks)
191
+ def added_tasks(tasks)
177
192
  # For now, settle on adding the execution agents only in the
178
193
  # main plan. Otherwise, it is possible that two transactions
179
194
  # will try to add two different agents