roby 0.7.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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,17 +1,14 @@
1
- require 'roby/plan-object'
2
- require 'roby/exceptions'
3
- require 'roby/event'
4
- require 'utilrb/module/attr_predicate'
5
-
6
1
  module Roby
7
2
  class TaskModelTag < Module
8
3
  module ClassExtension
9
4
  # Returns the list of static arguments required by this task model
10
5
  def arguments(*new_arguments)
11
6
  new_arguments.each do |arg_name|
7
+ arg_name = arg_name.to_sym
12
8
  argument_set << arg_name.to_sym
13
9
  unless method_defined?(arg_name)
14
10
  define_method(arg_name) { arguments[arg_name] }
11
+ define_method("#{arg_name}=") { |value| arguments[arg_name] = value }
15
12
  end
16
13
  end
17
14
 
@@ -19,6 +16,27 @@ module Roby
19
16
  end
20
17
  # Declares a set of arguments required by this task model
21
18
  def argument(*args); arguments(*args) end
19
+ # The part of +arguments+ that is meaningful for this task model
20
+ def meaningful_arguments(arguments)
21
+ self_arguments = self.arguments.to_set
22
+ arguments.to_hash.delete_if do |key, _|
23
+ !self_arguments.include?(key)
24
+ end
25
+ end
26
+
27
+ # Checks if this model fullfills everything in +models+
28
+ def fullfills?(models)
29
+ if !models.respond_to?(:each)
30
+ models = [models]
31
+ end
32
+
33
+ for tag in models
34
+ if !has_ancestor?(tag)
35
+ return false
36
+ end
37
+ end
38
+ true
39
+ end
22
40
  end
23
41
  include TaskModelTag::ClassExtension
24
42
 
@@ -46,6 +64,8 @@ module Roby
46
64
  # The task which fired this event
47
65
  attr_reader :task
48
66
 
67
+ def model; self.class end
68
+
49
69
  def initialize(task, generator, propagation_id, context, time = Time.now)
50
70
  @task = task
51
71
  @terminal_flag = generator.terminal_flag
@@ -88,7 +108,7 @@ module Roby
88
108
  # responds to #call
89
109
  def self.controlable?; respond_to?(:call) end
90
110
  # If the event is controlable
91
- def controlable?; self.class.controlable? end
111
+ def controlable?; model.controlable? end
92
112
  class << self
93
113
  # Called by Task.update_terminal_flag to update the flag
94
114
  attr_writer :terminal
@@ -104,7 +124,7 @@ module Roby
104
124
  # The event symbol
105
125
  def self.symbol; @symbol end
106
126
  # The event symbol
107
- def symbol; self.class.symbol end
127
+ def symbol; model.symbol end
108
128
  end
109
129
 
110
130
  # A task event model bound to a particular task instance
@@ -140,9 +160,22 @@ module Roby
140
160
  # by task.plan=. It is redefined here for performance reasons.
141
161
  attr_accessor :plan
142
162
 
143
- # Fire the event
163
+ # Check that the event can be emitted
164
+ def emitting(context)
165
+ task.emitting_event(self, context)
166
+ super if defined? super
167
+ end
168
+
144
169
  def fire(event)
145
170
  task.fire_event(event)
171
+ super if defined? super
172
+ end
173
+
174
+ def emit_failed(*reason)
175
+ if symbol == :start
176
+ task.failed_to_start = true
177
+ task.plan.task_index.set_state(task, :failed?)
178
+ end
146
179
  super
147
180
  end
148
181
 
@@ -153,13 +186,13 @@ module Roby
153
186
  super if defined? super
154
187
  if task.finished? && !terminal?
155
188
  raise CommandFailed.new(nil, self),
156
- "#{symbol}!(#{context})) called by #{Propagation.sources} but the task has finished. Task has been terminated by #{task.event(:stop).history.first.sources}."
189
+ "#{symbol}!(#{context}) called by #{plan.engine.propagation_sources.to_a} but the task has finished. Task has been terminated by #{task.event(:stop).history.first.sources}."
157
190
  elsif task.pending? && symbol != :start
158
191
  raise CommandFailed.new(nil, self),
159
- "#{symbol}!(#{context})) called by #{Propagation.sources} but the task is not running"
192
+ "#{symbol}!(#{context}) called by #{plan.engine.propagation_sources.to_a} but the task has never been started"
160
193
  elsif task.running? && symbol == :start
161
194
  raise CommandFailed.new(nil, self),
162
- "#{symbol}!(#{context})) called by #{Propagation.sources} but the task is already running. Task has been started by #{task.event(:start).history.first.sources}."
195
+ "#{symbol}!(#{context}) called by #{plan.engine.propagation_sources.to_a} but the task is already running. Task has been started by #{task.event(:start).history.first.sources}."
163
196
  end
164
197
  end
165
198
 
@@ -219,7 +252,7 @@ module Roby
219
252
  task.update_terminal_flag
220
253
  end
221
254
  end
222
- def new(context); event_model.new(task, self, Propagation.propagation_id, context) end
255
+ def new(context); event_model.new(task, self, plan.engine.propagation_id, context) end
223
256
 
224
257
  def to_s
225
258
  "#{task}/#{symbol}"
@@ -238,8 +271,8 @@ module Roby
238
271
  end
239
272
 
240
273
  if child_task
241
- unless task.realized_by?(child_task)
242
- task.realized_by child_task,
274
+ unless task.depends_on?(child_task, false)
275
+ task.depends_on child_task,
243
276
  :success => [child_event.symbol],
244
277
  :remove_when_done => true
245
278
  end
@@ -249,6 +282,13 @@ module Roby
249
282
  end
250
283
  end
251
284
 
285
+ # Refines exceptions that may be thrown by #call_without_propagation
286
+ def call_without_propagation(context)
287
+ super
288
+ rescue EventNotExecutable => e
289
+ refine_exception(e)
290
+ end
291
+
252
292
  # Checks that the event can be called. Raises various exception
253
293
  # when it is not the case.
254
294
  def check_call_validity
@@ -268,23 +308,29 @@ module Roby
268
308
 
269
309
  def refine_exception (e)
270
310
  if task.partially_instanciated?
271
- raise EventNotExecutable.new(self), "#{name}! called on #{task} which is partially instanciated\n" +
311
+ raise EventNotExecutable.new(self), "#{symbol}! called on #{task} which is partially instanciated\n" +
272
312
  "The following arguments were not set: \n" +
273
313
  task.list_unset_arguments.map {|n| "\t#{n}"}.join("\n")+"\n"
274
314
  #
275
315
  elsif !plan
276
- raise EventNotExecutable.new(self), "#{name}! called on #{task} but the task is in no plan"
316
+ raise EventNotExecutable.new(self), "#{symbol}! called on #{task} but the task is in no plan"
277
317
  elsif !plan.executable?
278
- raise EventNotExecutable.new(self), "#{name}! called on #{task} but the plan is not executable"
318
+ raise EventNotExecutable.new(self), "#{symbol}! called on #{task} but the plan is not executable"
279
319
  elsif task.abstract?
280
- raise EventNotExecutable.new(self), "#{name}! called on #{task} but the task is abstract"
320
+ raise EventNotExecutable.new(self), "#{symbol}! called on #{task} but the task is abstract"
281
321
  else
282
- raise EventNotExecutable.new(self), "#{name}! called on #{task} which is not executable: #{e.message}"
322
+ raise EventNotExecutable.new(self), "#{symbol}! called on #{task} which is not executable: #{e.message}"
283
323
  end
284
324
  end
285
325
 
286
326
  end
287
327
 
328
+ # Class that handles task arguments. They are handled specially as the
329
+ # arguments cannot be overwritten and can not be changed by a task that is
330
+ # not owned.
331
+ #
332
+ # Moreover, two hooks #updating and #updated allow to hook into the argument
333
+ # update system.
288
334
  class TaskArguments < Hash
289
335
  private :delete, :delete_if
290
336
 
@@ -305,6 +351,7 @@ module Roby
305
351
 
306
352
  alias :update! :[]=
307
353
  def []=(key, value)
354
+ key = key.to_sym if key.respond_to?(:to_str)
308
355
  if writable?(key)
309
356
  if !task.read_write?
310
357
  raise OwnershipError, "cannot change the argument set of a task which is not owned #{task} is owned by #{task.owners} and #{task.plan} by #{task.plan.owners}"
@@ -320,6 +367,11 @@ module Roby
320
367
  def updating; super if defined? super end
321
368
  def updated; super if defined? super end
322
369
 
370
+ def [](key)
371
+ key = key.to_sym if key.respond_to?(:to_str)
372
+ super(key)
373
+ end
374
+
323
375
  alias :do_merge! :merge!
324
376
  def merge!(hash)
325
377
  super do |key, old, new|
@@ -363,7 +415,7 @@ module Roby
363
415
  # end
364
416
  #
365
417
  # event :other_event do |context|
366
- # Roby.once { emit :other_event }
418
+ # engine.once { emit :other_event }
367
419
  # end
368
420
  # end
369
421
  #
@@ -371,6 +423,13 @@ module Roby
371
423
  # immediately emitted, and in the second case it will be emitted at the
372
424
  # beginning of the next execution cycle.
373
425
  #
426
+ # === Task relations
427
+ #
428
+ # Task relations are defined in the TaskStructure RelationSpace instance.
429
+ # See TaskStructure documentation for the list of special methods defined
430
+ # by the various graphs, and the TaskStructure namespace for the name and
431
+ # purpose of the various relation graphs themselves.
432
+ #
374
433
  # === Executability
375
434
  #
376
435
  # By default, a task is not executable, which means that no event command
@@ -420,7 +479,7 @@ module Roby
420
479
  class_eval do
421
480
  # Remove event models
422
481
  events.each_key do |ev_symbol|
423
- remove_const ev_symbol.to_s.camelize
482
+ remove_const ev_symbol.to_s.camelcase(true)
424
483
  end
425
484
 
426
485
  [@events, @signal_sets, @forwarding_sets, @causal_link_sets,
@@ -461,6 +520,123 @@ module Roby
461
520
  EOD
462
521
  end
463
522
 
523
+ ##
524
+ # :singleton-method: signals
525
+ # :call-seq:
526
+ # task_model.signals(event_model) => [target_event_models]
527
+ #
528
+ # Returns the set of model-level signal targets for the given event.
529
+
530
+ ##
531
+ # :singleton-method: each_signal
532
+ # :call-seq:
533
+ # task_model.each_signal(event_model) do |target_event_model|
534
+ # end
535
+ #
536
+ # Enumerates the set of model-level causal links that are defined for
537
+ # the given event. It enumerates all the ones defined on this model
538
+ # (using Task::signal) and also on its parent classes.
539
+
540
+ ##
541
+ # :method: each_signal
542
+ # :call-seq:
543
+ # task.each_signal(event_model) do |target_event_model|
544
+ # end
545
+ #
546
+ # Enumerates the set of model-level causal links that are defined for
547
+ # the given event. It enumerates all the ones defined on this task's model
548
+ # (using Task::signal) and also on its parent classes.
549
+
550
+
551
+
552
+
553
+ ##
554
+ # :singleton-method: forwardings
555
+ # :call-seq:
556
+ # task_model.forwardings(event_model) => [target_event_models]
557
+ #
558
+ # Returns the set of model-level forwarding targets for the given event.
559
+
560
+ ##
561
+ # :singleton-method: each_forwarding
562
+ # :call-seq:
563
+ # task_model.each_forwarding(event_model) do |target_event_model|
564
+ # end
565
+ #
566
+ # Enumerates the set of model-level causal links that are defined for
567
+ # the given event. It enumerates all the ones defined on this model
568
+ # (using Task::forward) and also on its parent classes.
569
+
570
+ ##
571
+ # :method: each_forwarding
572
+ # :call-seq:
573
+ # task.each_forwarding(event_model) do |target_event_model|
574
+ # end
575
+ #
576
+ # Enumerates the set of model-level causal links that are defined for
577
+ # the given event. It enumerates all the ones defined on this task's
578
+ # model (using Task::forward) and also on its parent classes.
579
+
580
+
581
+
582
+
583
+ ##
584
+ # :singleton-method: causal_links
585
+ # :call-seq:
586
+ # task_model.causal_links(event_model) => [target_event_models]
587
+ #
588
+ # Returns the set of model-level causal_link targets for the given event.
589
+
590
+ ##
591
+ # :singleton-method: each_causal_link
592
+ # :call-seq:
593
+ # task_model.each_causal_link(event_model) do |target_event_model|
594
+ # end
595
+ #
596
+ # Enumerates the set of model-level causal links that are defined for
597
+ # the given event. It enumerates all the ones defined on this model
598
+ # (using Task::causal_link) and also on its parent classes.
599
+
600
+ ##
601
+ # :method: each_causal_link
602
+ # :call-seq:
603
+ # task.each_causal_link(event_model) do |target_event_model|
604
+ # end
605
+ #
606
+ # Enumerates the set of model-level causal links that are defined for
607
+ # the given event. It enumerates all the ones defined on this task's model
608
+ # (using Task::causal_link) and also on its parent classes.
609
+
610
+
611
+
612
+
613
+ ##
614
+ # :singleton-method: handlers
615
+ # :call-seq:
616
+ # task_model.handlers(event_model) => [target_event_models]
617
+ #
618
+ # Returns the set of model-level event handlers for the given event.
619
+
620
+ ##
621
+ # :singleton-method: each_handler
622
+ # :call-seq:
623
+ # task_model.each_handler(event_model) do |target_event_model|
624
+ # end
625
+ #
626
+ # Enumerates the set of model-level event handlers that are defined for
627
+ # the given event. It enumerates all handlers defined on the instance's
628
+ # task model and its parent classes.
629
+
630
+ ##
631
+ # :method: each_handler
632
+ # :call-seq:
633
+ # task.each_handler(event_model) do |target_event_model|
634
+ # end
635
+ #
636
+ # Enumerates the set of model-level event handlers that are defined for
637
+ # the given event. It enumerates all handlers defined on the instance's
638
+ # task model and its parent classes.
639
+
464
640
  model_attribute_list('signal')
465
641
  model_attribute_list('forwarding')
466
642
  model_attribute_list('causal_link')
@@ -469,13 +645,17 @@ module Roby
469
645
 
470
646
  # The task arguments as symbol => value associative container
471
647
  attr_reader :arguments
472
- # The part of +arguments+ that is meaningful for this task model
648
+
649
+ # The part of +arguments+ that is meaningful for this task model. I.e.
650
+ # it returns the set of elements in the +arguments+ property that define
651
+ # arguments listed in the task model
473
652
  def meaningful_arguments(task_model = self.model)
474
653
  arguments.slice(*task_model.arguments)
475
654
  end
655
+
476
656
  # The task name
477
657
  def name
478
- @name ||= "#{model.name || self.class.name}#{arguments.to_s}:0x#{address.to_s(16)}"
658
+ @name ||= "#{model.name || self.class.name}:0x#{address.to_s(16)}"
479
659
  end
480
660
 
481
661
  # This predicate is true if this task is a mission for its owners. If
@@ -484,6 +664,7 @@ module Roby
484
664
 
485
665
  def inspect
486
666
  state = if pending? then 'pending'
667
+ elsif failed_to_start? then 'failed to start'
487
668
  elsif starting? then 'starting'
488
669
  elsif running? then 'running'
489
670
  elsif finishing? then 'finishing'
@@ -499,23 +680,34 @@ module Roby
499
680
  # * the task shall have a +start+ event
500
681
  # * the task shall have at least one terminal event. If no +stop+ event
501
682
  # is defined, then all terminal events are aliased to +stop+
502
- def initialize(arguments = nil) #:yields: task_object
683
+ def initialize(arguments = Hash.new) #:yields: task_object
503
684
  super() if defined? super
504
685
 
505
686
  @arguments = TaskArguments.new(self)
506
- @arguments.merge!(arguments) if arguments
687
+ arguments.each do |key, value|
688
+ if self.respond_to?("#{key}=")
689
+ self.send("#{key}=", value)
690
+ else
691
+ @arguments[key] = value
692
+ end
693
+ end
507
694
 
508
695
  @model = self.class
509
696
 
510
697
  yield(self) if block_given?
698
+ # Create the EventGenerator instances that represent this task's
699
+ # events. Note that the event relations are instanciated by
700
+ # Plan#discover when this task is included in a plan, thus avoiding
701
+ # filling up the relation graphs with unused relations.
511
702
  initialize_events
512
703
  end
513
704
 
514
705
 
515
706
  # Lists all arguments, that are set to be needed via the :argument
516
707
  # syntax but are not set.
708
+ #
517
709
  # This is needed for debugging purposes.
518
- def list_unset_arguments
710
+ def list_unset_arguments # :nodoc:
519
711
  ret = Array.new
520
712
  model.arguments.each { |name|
521
713
  if !arguments.has_key?(name) then
@@ -523,7 +715,6 @@ module Roby
523
715
  end }
524
716
  ret
525
717
  end
526
-
527
718
 
528
719
  # Helper methods which creates all the necessary TaskEventGenerator
529
720
  # objects and stores them in the #bound_events map
@@ -540,6 +731,25 @@ module Roby
540
731
 
541
732
  def model; self.class end
542
733
 
734
+ # Returns for how many seconds this task is running. Returns nil if
735
+ # the task is not running.
736
+ def lifetime
737
+ if running?
738
+ Time.now - history.first.time
739
+ end
740
+ end
741
+
742
+ # Returns when this task has been started
743
+ def start_time
744
+ if running?
745
+ history.first.time
746
+ end
747
+ end
748
+
749
+ def create_fresh_copy
750
+ model.new(arguments.dup)
751
+ end
752
+
543
753
  def initialize_copy(old) # :nodoc:
544
754
  super
545
755
 
@@ -551,7 +761,7 @@ module Roby
551
761
  arguments.instance_variable_set(:@task, self)
552
762
 
553
763
  initialize_events
554
- plan.discover(self)
764
+ plan.add(self)
555
765
  end
556
766
 
557
767
  def instantiate_model_event_relations
@@ -569,7 +779,7 @@ module Roby
569
779
 
570
780
  for signalled in signalled_events
571
781
  signalled = bound_events[signalled]
572
- generator.signal signalled
782
+ generator.signals signalled
573
783
  left_border.delete(signalled)
574
784
  end
575
785
  end
@@ -582,7 +792,7 @@ module Roby
582
792
 
583
793
  for signalled in signalled_events
584
794
  signalled = bound_events[signalled]
585
- generator.forward signalled
795
+ generator.forward_to signalled
586
796
  left_border.delete(signalled)
587
797
  end
588
798
  end
@@ -646,29 +856,44 @@ module Roby
646
856
  end
647
857
 
648
858
  class << self
649
- # If this task is an abstract task
650
- # Abstract tasks are not executable. This attribute is
651
- # not inherited in the task hierarchy
652
- attr_reader :abstract
653
- alias :abstract? :abstract
654
-
655
- # Mark this task model as an abstract model
859
+ ##
860
+ # :singleton-method: abstract?
861
+ #
862
+ # True if this task is an abstract task.
863
+ #
864
+ # See Task::abstract() for more information.
865
+ attr_predicate :abstract
866
+
867
+ # Declare that this task model defines abstract tasks. Abstract
868
+ # tasks can be used to represent an action, without specifically
869
+ # representing how this action should be done.
870
+ #
871
+ # Instances of abstract task models are not executable, i.e. they
872
+ # cannot be started.
873
+ #
874
+ # See also #abstract? and #executable?
656
875
  def abstract
657
876
  @abstract = true
658
877
  end
659
878
 
660
- # Declare that nothing special is required to stop this task.
661
- # This makes +failed+ and +stop+ controlable events, and
662
- # makes the interruption sequence be stop! => calls failed! =>
663
- # emits +failed+ => emits +stop+.
879
+ # Declare that tasks of this model can finish by simply emitting
880
+ # +stop+. Use it this way:
881
+ #
882
+ # class MyTask < Roby::Task
883
+ # terminates
884
+ # end
885
+ #
886
+ # It adds a +stop!+ command that emits the +failed+ event.
664
887
  def terminates
665
888
  event :failed, :command => true, :terminal => true
666
889
  interruptible
667
890
  end
668
891
 
669
- # Sets up a command for +stop+ in the case where +failed+ is also
670
- # controllable, if the command of +failed+ should be used to stop
671
- # the task.
892
+ # Declare that tasks of this model can be interrupted. It does so by
893
+ # defining a command for +stop+, which in effect calls the command
894
+ # for +failed+.
895
+ #
896
+ # Raises ArgumentError if failed is not controlable.
672
897
  def interruptible
673
898
  if !has_event?(:failed) || !event_model(:failed).controlable?
674
899
  raise ArgumentError, "failed is not controlable"
@@ -683,7 +908,7 @@ module Roby
683
908
  end
684
909
 
685
910
  def setup_poll_method(block) # :nodoc:
686
- define_method(:poll) do
911
+ define_method(:poll) do |plan|
687
912
  return unless self_owned?
688
913
  begin
689
914
  poll_handler
@@ -695,9 +920,17 @@ module Roby
695
920
  define_method(:poll_handler, &block)
696
921
  end
697
922
 
698
- # Defines a block which will be called at each execution cycle for
699
- # each running task of this model. The block is called in the
700
- # instance context of the target task (i.e. using instance_eval)
923
+ # Declares that the given block should be called at each execution
924
+ # cycle, when the task is running. Use it that way:
925
+ #
926
+ # class MyTask < Roby::Task
927
+ # poll do
928
+ # ... do something ...
929
+ # end
930
+ # end
931
+ #
932
+ # If the given polling block raises an exception, the task will be
933
+ # terminated by emitting its +failed+ event.
701
934
  def poll(&block)
702
935
  if !block_given?
703
936
  raise "no block given"
@@ -705,8 +938,8 @@ module Roby
705
938
 
706
939
  setup_poll_method(block)
707
940
 
708
- on(:start) { Control.event_processing << method(:poll) }
709
- on(:stop) { Control.event_processing.delete(method(:poll)) }
941
+ on(:start) { |ev| ev.task.plan.engine.propagation_handlers << method(:poll) }
942
+ on(:stop) { |ev| ev.task.plan.engine.propagation_handlers.delete(method(:poll)) }
710
943
  end
711
944
  end
712
945
 
@@ -715,8 +948,13 @@ module Roby
715
948
 
716
949
  # Returns true if this task is from an abstract model. If it is the
717
950
  # case, the task is not executable.
718
- def abstract?; self.class.abstract? end
719
- # Check if this task is executable
951
+ #
952
+ # See Task::abstract for more details.
953
+ def abstract?; model.abstract? end
954
+ # True if this task is executable. A task is not executable if it is
955
+ # abstract or partially instanciated.
956
+ #
957
+ # See #abstract? and #partially_instanciated?
720
958
  def executable?; !abstract? && !partially_instanciated? && super end
721
959
  # Returns true if this task's stop event is controlable
722
960
  def interruptible?; event(:stop).controlable? end
@@ -745,14 +983,14 @@ module Roby
745
983
  # can either be a event class or an event name.
746
984
  def has_event?(event_model)
747
985
  bound_events.has_key?(event_model) ||
748
- self.class.has_event?(event_model)
986
+ model.has_event?(event_model)
749
987
  end
750
988
 
751
989
  # True if this task is starting, i.e. if its start event is pending
752
990
  # (has been called, but is not emitted yet)
753
991
  def starting?; event(:start).pending? end
754
992
  # True if this task has never been started
755
- def pending?; !starting? && !started? end
993
+ def pending?; !failed_to_start? && !starting? && !started? end
756
994
  # True if this task is currently running (i.e. is has already started,
757
995
  # and is not finished)
758
996
  def running?; started? && !finished? end
@@ -767,14 +1005,24 @@ module Roby
767
1005
  attr_predicate :started?, true
768
1006
  attr_predicate :finished?, true
769
1007
  attr_predicate :success?, true
1008
+ attr_predicate :failed_to_start?, true
770
1009
 
771
1010
  # True if the +failed+ event of this task has been fired
772
- def failed?; finished? && @success == false end
1011
+ def failed?; failed_to_start? || (finished? && @success == false) end
773
1012
 
774
- # Remove all relations in which +self+ or its event are involved
775
- def clear_relations
776
- each_event { |ev| ev.clear_relations }
777
- super
1013
+ # call-seq:
1014
+ # task.clear_relations => task
1015
+ #
1016
+ # Remove all relations in which +self+ or its event are involved
1017
+ #--
1018
+ # The including_events flag is here for the benefit of
1019
+ # Transactions::Proxy::Task only
1020
+ def clear_relations(including_events = true)
1021
+ if including_events
1022
+ each_event { |ev| ev.clear_relations }
1023
+ end
1024
+ super()
1025
+ self
778
1026
  end
779
1027
 
780
1028
  # Update the terminal flag for the event models that are defined in
@@ -863,8 +1111,8 @@ module Roby
863
1111
  end
864
1112
  end
865
1113
 
866
- # Returns a sorted list of Event objects, for all events that have been
867
- # fired by this task
1114
+ # Returns a list of Event objects, for all events that have been fired
1115
+ # by this task. The list is sorted by emission times.
868
1116
  def history
869
1117
  history = []
870
1118
  each_event do |event|
@@ -874,9 +1122,9 @@ module Roby
874
1122
  history.sort_by { |ev| ev.time }
875
1123
  end
876
1124
 
877
- # Returns the set of tasks directly related to this task, either
878
- # because of task relations or because of task events that are related
879
- # to other task events
1125
+ # Returns the set of tasks directly related to this task, either because
1126
+ # of task relations or because of task events that are related to other
1127
+ # task events
880
1128
  def related_tasks(result = nil)
881
1129
  result = related_objects(nil, result)
882
1130
  each_event do |ev|
@@ -898,29 +1146,41 @@ module Roby
898
1146
 
899
1147
  # This method is called by TaskEventGenerator#fire just before the event handlers
900
1148
  # and commands are called
901
- def fire_event(event) # :nodoc:
1149
+ def emitting_event(event, context) # :nodoc:
902
1150
  if !executable?
903
- raise TaskNotExecutable.new(self), "trying to fire #{event.generator.symbol} on #{self} but #{self} is not executable"
1151
+ raise TaskNotExecutable.new(self), "trying to emit #{symbol} on #{self} but #{self} is not executable"
904
1152
  end
905
1153
 
906
1154
  if finished? && !event.terminal?
907
- raise EmissionFailed.new(nil, self),
908
- "emit(#{event.symbol}: #{event.model}[#{event.context}]) called @#{event.propagation_id} by #{Propagation.sources} but the task has finished. Task has been terminated by #{event(:stop).history.first.sources}."
1155
+ raise EmissionFailed.new(nil, self),
1156
+ "emit(#{event.symbol}, #{context}) called by #{plan.engine.propagation_sources.to_a} but the task has finished. Task has been terminated by #{event(:stop).history.first.sources}."
909
1157
  elsif pending? && event.symbol != :start
910
- raise EmissionFailed.new(nil, self),
911
- "emit(#{event.symbol}: #{event.model}[#{event.context}]) called @#{event.propagation_id} by #{Propagation.sources} but the task is not running"
1158
+ raise EmissionFailed.new(nil, self),
1159
+ "emit(#{event.symbol}, #{context}) called by #{plan.engine.propagation_sources.to_a} but the task has never been started"
912
1160
  elsif running? && event.symbol == :start
913
- raise EmissionFailed.new(nil, self),
914
- "emit(#{event.symbol}: #{event.model}[#{event.context}]) called @#{event.propagation_id} by #{Propagation.sources} but the task is already running. Task has been started by #{event(:start).history.first.sources}."
1161
+ raise EmissionFailed.new(nil, self),
1162
+ "emit(#{event.symbol}, #{context}) called by #{plan.engine.propagation_sources.to_a} but the task is already running. Task has been started by #{event(:start).history.first.sources}."
915
1163
  end
916
1164
 
917
- update_task_status(event)
1165
+ super if defined? super
1166
+ end
918
1167
 
1168
+ # Hook called by TaskEventGenerator#fired when one of this task's events
1169
+ # is fired.
1170
+ def fire_event(event)
1171
+ update_task_status(event)
919
1172
  super if defined? super
920
1173
  end
921
1174
 
922
1175
  # The event which has finished the task (if there is one)
923
1176
  attr_reader :terminal_event
1177
+
1178
+ # The event that caused this task to fail. This is equivalent to taking
1179
+ # the first emitted element of
1180
+ # task.event(:failed).last.task_sources
1181
+ #
1182
+ # It is only much more efficient
1183
+ attr_reader :failure_event
924
1184
 
925
1185
  # Call to update the task status because of +event+
926
1186
  def update_task_status(event) # :nodoc:
@@ -934,6 +1194,7 @@ module Roby
934
1194
  self.success = false
935
1195
  self.finished = true
936
1196
  @terminal_event ||= event
1197
+ @failure_event ||= event
937
1198
  elsif event.terminal? && !finished?
938
1199
  plan.task_index.set_state(self, :finished?)
939
1200
  self.finished = true
@@ -974,22 +1235,42 @@ module Roby
974
1235
  end
975
1236
 
976
1237
  # call-seq:
977
- # on(event, task[, event1, event2, ...])
978
1238
  # on(event) { |event| ... }
979
- # on(event[, task, event1, event2, ...]) { |event| ... }
980
- # on(event, task[, event1, event2, ...], delay)
981
- #
982
- # Adds a signal from the given event to the specified targets, and/or
983
- # defines an event handler. Note that <tt>on(event, task)</tt> is
984
- # equivalent to <tt>on(event, task, event)</tt>
985
1239
  #
986
- # +delay+, if given, specifies that the signal must be postponed for as
987
- # much time as specified. See EventGenerator#signal for valid values.
1240
+ # Defines an event handler for the given event.
988
1241
  def on(event_model, to = nil, *to_task_events, &user_handler)
989
- unless to || user_handler
1242
+ if to
1243
+ Roby.warn_deprecated "on(event_name, task, target_events) has been replaced by #signals"
1244
+ elsif !(to || user_handler)
990
1245
  raise ArgumentError, "you must provide either a task or an event handler (got nil for both)"
991
1246
  end
992
1247
 
1248
+ if to
1249
+ signals(event_model, to, *to_task_events)
1250
+ end
1251
+ if user_handler
1252
+ generator = event(event_model)
1253
+ generator.on(&user_handler)
1254
+ end
1255
+ self
1256
+ end
1257
+
1258
+ # call-seq:
1259
+ # signals source_event, dest_task, ev1, ev2, ev3, ...
1260
+ # signals source_event, dest_task, ev1, ev2, ev3, delay_options
1261
+ #
1262
+ # Creates a signal from +source_event+, which is an event name of
1263
+ # +self+, to the listed events of +dest_task+. The destination events
1264
+ # will be called when the source event is emitted.
1265
+ #
1266
+ # To simply emit target events (i.e. not calling the event's commands),
1267
+ # use #forwards
1268
+ #
1269
+ # Optionally, a delay can be added to the signal. +delay_options+ can be
1270
+ # either:
1271
+ # :delay => relative_delay_in_seconds
1272
+ # :at => absolute_time
1273
+ def signals(event_model, to, *to_task_events)
993
1274
  generator = event(event_model)
994
1275
  if Hash === to_task_events.last
995
1276
  delay = to_task_events.pop
@@ -997,6 +1278,7 @@ module Roby
997
1278
  to_events = case to
998
1279
  when Task
999
1280
  if to_task_events.empty?
1281
+ Roby.warn_deprecated "signals(event_name, target_task) is deprecated. You must now always specify the target event name"
1000
1282
  [to.event(generator.symbol)]
1001
1283
  else
1002
1284
  to_task_events.map { |ev_model| to.event(ev_model) }
@@ -1006,27 +1288,35 @@ module Roby
1006
1288
  end
1007
1289
 
1008
1290
  to_events.push delay if delay
1009
- generator.on(*to_events, &user_handler)
1291
+ generator.signals(*to_events)
1010
1292
  self
1011
1293
  end
1012
1294
 
1295
+ def forward(name, to, *to_task_events)
1296
+ Roby.warn_deprecated "Task#forward has been renamed into Task#forward_to"
1297
+ if to_task_events.empty?
1298
+ Roby.warn_deprecated "the Task#forward(event_name, target_task) form is deprecated. Use Task#forward_to and specify the target event name"
1299
+ end
1300
+
1301
+ forward_to(name, to, *to_task_events)
1302
+ end
1303
+
1013
1304
  # call-seq:
1014
- # forward source_event, dest_task, ev1, ev2, ev3, ...
1015
- # forward source_event, dest_task, ev1, ev2, ev3, delay_options
1305
+ # forward_to source_event, dest_task, ev1, ev2, ev3, ...
1306
+ # forward_to source_event, dest_task, ev1, ev2, ev3, delay_options
1016
1307
  #
1017
- # Fowards +name+ to the events in +to_task_events+ on task +to+. The
1018
- # target events will be emitted as soon as the +name+ event is emitted
1019
- # on the receiving task, without calling any command.
1308
+ # Fowards the +source_event+, which is the name of an event of +self+,
1309
+ # to the listed events in +dest_task+. The target events will be emitted
1310
+ # as soon as the source event is emitted,
1020
1311
  #
1021
1312
  # To call an event whenever other events are emitted, use the Signal
1022
- # relation. See Task#on, Task.on and EventGenerator#on. As for Task#on,
1023
- # <tt>forward(:start, task)</tt> is a shortcut to <tt>forward(:start,
1024
- # task, :start)</tt>.
1313
+ # relation. See Task#signals, Task.signal and EventGenerator#signals.
1025
1314
  #
1026
- # If a +delay_options+ hash is provided, the forwarding is not performed
1027
- # immediately, but with a given delay. See EventGenerator#forward for
1028
- # the delay specification.
1029
- def forward(name, to, *to_task_events)
1315
+ # Optionally, a delay can be added to the signal. +delay_options+ can be
1316
+ # either:
1317
+ # :delay => relative_delay_in_seconds
1318
+ # :at => absolute_time
1319
+ def forward_to(name, to, *to_task_events)
1030
1320
  generator = event(name)
1031
1321
  if Hash === to_task_events.last
1032
1322
  delay = to_task_events.pop
@@ -1034,10 +1324,11 @@ module Roby
1034
1324
 
1035
1325
  to_events = if to.respond_to?(:event)
1036
1326
  if to_task_events.empty?
1327
+ Roby.warn_deprecated "forward_to(event_name, target_task) is deprecated. You must now always specify the target event name"
1037
1328
  [to.event(generator.symbol)]
1038
- else
1039
- to_task_events.map { |ev| to.event(ev) }
1040
- end
1329
+ else
1330
+ to_task_events.map { |ev| to.event(ev) }
1331
+ end
1041
1332
  elsif to.kind_of?(EventGenerator)
1042
1333
  [to]
1043
1334
  else
@@ -1045,31 +1336,44 @@ module Roby
1045
1336
  end
1046
1337
 
1047
1338
  to_events.each do |ev|
1048
- generator.forward ev, delay
1339
+ generator.forward_to ev, delay
1049
1340
  end
1050
1341
  end
1051
1342
 
1343
+ # :stopdoc:
1052
1344
  attr_accessor :calling_event
1345
+
1053
1346
  def method_missing(name, *args, &block) # :nodoc:
1054
1347
  if calling_event && calling_event.respond_to?(name)
1055
1348
  calling_event.send(name, *args, &block)
1056
1349
  else
1057
1350
  super
1058
1351
  end
1059
- rescue
1060
- raise $!, $!.message, $!.backtrace[1..-1]
1352
+ rescue NameError => e
1353
+ raise e, e.message, caller(1)
1354
+ rescue NoMethodError => e
1355
+ raise e, e.message, caller(1)
1061
1356
  end
1062
1357
 
1358
+ # Declares that this task model provides the given interface. +model+
1359
+ # must be an instance of TaskModelTag
1360
+ def self.provides(model)
1361
+ include model
1362
+ end
1363
+
1364
+
1063
1365
  @@event_command_id = 0
1064
1366
  def self.allocate_event_command_id # :nodoc:
1065
1367
  @@event_command_id += 1
1066
1368
  end
1369
+ # :startdoc:
1370
+
1067
1371
  # call-seq:
1068
- # self.event(name, options = nil) { ... } -> event class or nil
1372
+ # self.event(name, options = nil) { ... } => event class or nil
1069
1373
  #
1070
1374
  # Define a new event in this task.
1071
1375
  #
1072
- # ==== Available options
1376
+ # <b>Available options</b>
1073
1377
  #
1074
1378
  # <tt>command</tt>::
1075
1379
  # either true, false or an event command for the new event. In that
@@ -1086,7 +1390,7 @@ module Roby
1086
1390
  # base class for the event model (see "Event models" below). The default is the
1087
1391
  # TaskEvent class
1088
1392
  #
1089
- # ==== Event models
1393
+ # <b>Event models</b>
1090
1394
  #
1091
1395
  # When a task event (for instance +start+) is emitted, a Roby::Event
1092
1396
  # object is created to describe the information related to this
@@ -1130,10 +1434,10 @@ module Roby
1130
1434
  @symbol = ev
1131
1435
  @command_handler = command_handler
1132
1436
 
1133
- define_method(:name) { "#{task.name}::#{ev_s.camelize}" }
1437
+ define_method(:name) { "#{task.name}::#{ev_s.camelcase(true)}" }
1134
1438
  singleton_class.class_eval do
1135
1439
  attr_reader :command_handler
1136
- define_method(:name) { "#{task_klass.name}::#{ev_s.camelize}" }
1440
+ define_method(:name) { "#{task_klass.name}::#{ev_s.camelcase(true)}" }
1137
1441
  def to_s; name end
1138
1442
  end
1139
1443
  end
@@ -1148,7 +1452,7 @@ module Roby
1148
1452
  if setup_terminal_handler
1149
1453
  forward(new_event => :stop)
1150
1454
  end
1151
- const_set(ev_s.camelize, new_event)
1455
+ const_set(ev_s.camelcase(true), new_event)
1152
1456
 
1153
1457
  if options[:command]
1154
1458
  # check that the supplied command handler can take two arguments
@@ -1172,6 +1476,17 @@ module Roby
1172
1476
  end
1173
1477
  end
1174
1478
 
1479
+ if !method_defined?("#{ev_s}_event")
1480
+ define_method("#{ev_s}_event") do
1481
+ event(ev)
1482
+ end
1483
+ end
1484
+ if !method_defined?("#{ev_s}?")
1485
+ define_method("#{ev_s}?") do
1486
+ event(ev).happened?
1487
+ end
1488
+ end
1489
+
1175
1490
  new_event
1176
1491
  end
1177
1492
 
@@ -1201,15 +1516,23 @@ module Roby
1201
1516
 
1202
1517
  # Events defined by the task model
1203
1518
  inherited_enumerable(:event, :events, :map => true) { Hash.new }
1204
- def self.enum_events
1519
+
1520
+ def self.enum_events # :nodoc
1205
1521
  @__enum_events__ ||= enum_for(:each_event)
1206
1522
  end
1207
1523
 
1524
+ # call-seq:
1525
+ # task.each_event { |event_object| ... } => task
1526
+ #
1208
1527
  # Iterates on all the events defined for this task
1209
- def each_event # :yield:bound_event
1528
+ #--
1529
+ # The +only_wrapped+ flag is here for consistency with transaction
1530
+ # proxies, and should probably not be used in user code.
1531
+ def each_event(only_wrapped = true) # :yield:bound_event
1210
1532
  for _, ev in bound_events
1211
1533
  yield(ev)
1212
1534
  end
1535
+ self
1213
1536
  end
1214
1537
  alias :each_plan_child :each_event
1215
1538
 
@@ -1271,8 +1594,34 @@ module Roby
1271
1594
  end
1272
1595
 
1273
1596
  # call-seq:
1274
- # on(event_model) { |event| ... }
1275
- # on(event_model => ev1, ev2 => [ ev3, ev4 ]) { |event| ... }
1597
+ # signal(name1 => name2, name3 => [name4, name5])
1598
+ #
1599
+ # Establish model-level signals between events of that task. These
1600
+ # signals will be established on all the instances of this task model
1601
+ # (and its subclasses).
1602
+ def self.signal(mappings)
1603
+ mappings.each do |from, to|
1604
+ from = event_model(from)
1605
+ targets = Array[*to].map { |ev| event_model(ev) }
1606
+
1607
+ if from.terminal?
1608
+ non_terminal = targets.find_all { |ev| !ev.terminal? }
1609
+ if !non_terminal.empty?
1610
+ raise ArgumentError, "trying to establish a forwarding relation from the terminal event #{from} to the non-terminal events #{non_terminal}"
1611
+ end
1612
+ end
1613
+ non_controlable = targets.find_all { |ev| !ev.controlable? }
1614
+ if !non_controlable.empty?
1615
+ raise ArgumentError, "trying to signal #{non_controlable.join(" ")} which is/are not controlable"
1616
+ end
1617
+
1618
+ signal_sets[from.symbol].merge targets.map { |ev| ev.symbol }.to_value_set
1619
+ end
1620
+ update_terminal_flag
1621
+ end
1622
+
1623
+ # call-seq:
1624
+ # on(event_name) { |event| ... }
1276
1625
  #
1277
1626
  # Adds an event handler for the given event model. When the event is fired,
1278
1627
  # all events given in argument will be called. If they are controlable,
@@ -1282,21 +1631,14 @@ module Roby
1282
1631
  check_arity(user_handler, 1)
1283
1632
  end
1284
1633
 
1634
+ if mappings.kind_of?(Hash)
1635
+ Roby.warn_deprecated "the on(event => event) form of Task.on is deprecated. Use #signal to establish signals"
1636
+ signal(mappings)
1637
+ end
1638
+
1285
1639
  mappings = [*mappings].zip([]) unless Hash === mappings
1286
- mappings.each do |from, to|
1640
+ mappings.each do |from, _|
1287
1641
  from = event_model(from).symbol
1288
- to = if to
1289
- Array[*to].map do |ev|
1290
- model = event_model(ev)
1291
- raise ArgumentError, "trying to signal #{ev} which is not controlable" unless model.controlable?
1292
- model.symbol
1293
- end
1294
- else; []
1295
- end
1296
-
1297
- signal_sets[from].merge to.to_value_set
1298
- update_terminal_flag
1299
-
1300
1642
  if user_handler
1301
1643
  method_name = "event_handler_#{from}_#{Object.address_from_id(user_handler.object_id).to_s(16)}"
1302
1644
  define_method(method_name, &user_handler)
@@ -1329,8 +1671,17 @@ module Roby
1329
1671
  # See also Task#forward and EventGenerator#forward.
1330
1672
  def self.forward(mappings)
1331
1673
  mappings.each do |from, to|
1332
- from = event_model(from).symbol
1333
- forwarding_sets[from].merge Array[*to].map { |ev| event_model(ev).symbol }.to_value_set
1674
+ from = event_model(from).symbol
1675
+ targets = Array[*to].map { |ev| event_model(ev).symbol }
1676
+
1677
+ if event_model(from).terminal?
1678
+ non_terminal = targets.find_all { |name| !event_model(name).terminal? }
1679
+ if !non_terminal.empty?
1680
+ raise ArgumentError, "trying to establish a forwarding relation from the terminal event #{from} to the non-terminal event(s) #{targets}"
1681
+ end
1682
+ end
1683
+
1684
+ forwarding_sets[from].merge targets.to_value_set
1334
1685
  end
1335
1686
  update_terminal_flag
1336
1687
  end
@@ -1341,7 +1692,7 @@ module Roby
1341
1692
  end
1342
1693
 
1343
1694
  def to_s # :nodoc:
1344
- s = name.dup
1695
+ s = name.dup + arguments.to_s
1345
1696
  id = owners.map do |owner|
1346
1697
  next if owner == Roby::Distributed
1347
1698
  sibling = remote_siblings[owner]
@@ -1353,17 +1704,22 @@ module Roby
1353
1704
  s
1354
1705
  end
1355
1706
 
1356
- def pretty_print(pp) # :nodoc:
1357
- pp.text "#{self.class.name}:0x#{self.address.to_s(16)}"
1358
- pp.breakable
1359
- pp.nest(2) do
1360
- pp.text " owners: "
1361
- pp.seplist(owners) { |r| pp.text r.to_s }
1707
+ def pretty_print(pp, with_owners = true) # :nodoc:
1708
+ pp.text "#{model.name}:0x#{self.address.to_s(16)}"
1709
+ if with_owners
1710
+ pp.breakable
1711
+ pp.nest(2) do
1712
+ pp.text " owners: "
1713
+ pp.seplist(owners) { |r| pp.text r.to_s }
1714
+ pp.breakable
1362
1715
 
1363
- pp.breakable
1364
- pp.text "arguments: "
1365
- arguments.pretty_print(pp)
1366
- end
1716
+ pp.text "arguments: "
1717
+ arguments.pretty_print(pp)
1718
+ end
1719
+ else
1720
+ pp.text " "
1721
+ arguments.pretty_print(pp)
1722
+ end
1367
1723
  end
1368
1724
 
1369
1725
  # True if this task is a null task. See NullTask.
@@ -1417,7 +1773,7 @@ module Roby
1417
1773
  def fullfills?(models, args = {})
1418
1774
  if models.kind_of?(Task)
1419
1775
  klass, args =
1420
- models.class,
1776
+ models.model,
1421
1777
  models.meaningful_arguments
1422
1778
  models = [klass]
1423
1779
  else
@@ -1429,7 +1785,7 @@ module Roby
1429
1785
 
1430
1786
  # Check the arguments that are required by the model
1431
1787
  for tag in models
1432
- unless self_model.has_ancestor?(tag)
1788
+ if !self_model.has_ancestor?(tag)
1433
1789
  return false
1434
1790
  end
1435
1791
 
@@ -1449,6 +1805,25 @@ module Roby
1449
1805
  true
1450
1806
  end
1451
1807
 
1808
+ # True if +self+ can be used to replace +target+
1809
+ def can_replace?(target)
1810
+ fullfills?(*target.fullfilled_model)
1811
+ end
1812
+
1813
+ def can_merge?(target)
1814
+ target_model = target.fullfilled_model
1815
+ if !fullfills?(target_model.first)
1816
+ return false
1817
+ end
1818
+
1819
+ target_model.last.each do |key, val|
1820
+ if arguments.has_key?(key) && arguments[key] != val
1821
+ return false
1822
+ end
1823
+ end
1824
+ true
1825
+ end
1826
+
1452
1827
  include ExceptionHandlingObject
1453
1828
  inherited_enumerable('exception_handler', 'exception_handlers') { Array.new }
1454
1829
 
@@ -1457,9 +1832,10 @@ module Roby
1457
1832
 
1458
1833
  @@exception_handler_id = 0
1459
1834
 
1460
- # call-seq:
1461
- # on_exception(TaskModelViolation, ...) { |task, exception_object| ... }
1462
- #
1835
+ ##
1836
+ # :call-seq:
1837
+ # on_exception(exception_class, ...) { |task, exception_object| ... }
1838
+ #
1463
1839
  # Defines an exception handler. matcher === exception_object is used to
1464
1840
  # determine if the handler should be called when +exception_object+ has
1465
1841
  # been fired. The first matching handler is called. Call #pass_exception to pass
@@ -1576,8 +1952,8 @@ module Roby
1576
1952
  singleton_class.class_eval do
1577
1953
  setup_poll_method(block)
1578
1954
  end
1579
- on(:start) { Control.event_processing << method(:poll) }
1580
- on(:stop) { Control.event_processing.delete(method(:poll)) }
1955
+ on(:start) { |ev| @poll_handler_id = plan.engine.add_propagation_handler(method(:poll)) }
1956
+ on(:stop) { |ev| plan.engine.remove_propagation_handler(@poll_handler_id) }
1581
1957
  end
1582
1958
  end
1583
1959
 
@@ -1617,7 +1993,7 @@ module Roby
1617
1993
  start_event.call
1618
1994
  end
1619
1995
  on :start do
1620
- success_event.forward_once event(:success)
1996
+ success_event.forward_to_once event(:success)
1621
1997
  success_event.if_unreachable(true) do
1622
1998
  emit :failed if executable?
1623
1999
  end
@@ -1648,5 +2024,3 @@ module Roby
1648
2024
 
1649
2025
  end
1650
2026
 
1651
- require 'roby/task-operations'
1652
-