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,5 +1,3 @@
1
- require 'roby/planning/task'
2
-
3
1
  module Roby
4
2
  # This class unrolls a loop in the plan. It maintains +lookahead+ patterns
5
3
  # developped at all times by calling an external planner, and manages them.
@@ -68,29 +66,40 @@ module Roby
68
66
 
69
67
  # The planner model we should use
70
68
  argument :planner_model
71
- # The planner method name
72
- argument :method_name
69
+
70
+ # The planner method name. This is not a mandatory argument as
71
+ # otherwise we would break logging and distributed Roby: this attribute
72
+ # can hold a MethodDefinition object that cannot be shared.
73
+ #
74
+ # Anyway, the only meaningful argument in distributed context is the
75
+ # method name itself. Event method_options could be removed in the
76
+ # future.
77
+ def planning_method
78
+ arguments[:planning_method]
79
+ end
73
80
  # The planner method options
74
81
  argument :method_options
75
82
 
83
+ # The method name. This can be nil a FreeMethod is used for planning
84
+ argument :method_name
85
+
76
86
  # Filters the options in +options+, splitting between the options that
77
87
  # are specific to the planning task and those that are to be forwarded
78
88
  # to the planner itself
79
- def self.filter_options(options) # :nodoc:
89
+ def self.filter_options(options)
80
90
  task_arguments, planning_options = Kernel.filter_options options,
81
91
  :period => nil,
82
92
  :lookahead => 1,
83
93
  :planner_model => nil,
94
+ :planning_method => nil,
84
95
  :planned_model => Roby::Task,
85
96
  :method_name => nil,
86
97
  :method_options => {},
87
98
  :planning_owners => nil
88
99
 
89
- if !task_arguments[:method_name]
90
- raise ArgumentError, "required argument :method_name missing"
91
- elsif !task_arguments[:planner_model]
92
- raise ArgumentError, "required argument :planner_model missing"
93
- elsif task_arguments[:lookahead] < 0
100
+ task_arguments = PlanningTask.validate_planning_options(task_arguments)
101
+
102
+ if task_arguments[:lookahead] < 0
94
103
  raise ArgumentError, "lookahead must be positive"
95
104
  end
96
105
  task_arguments[:period] ||= nil
@@ -109,7 +118,7 @@ module Roby
109
118
  if period && period > 0
110
119
  @periodic_trigger = State.on_delta :t => period
111
120
  periodic_trigger.disable
112
- periodic_trigger.on event(:loop_start)
121
+ periodic_trigger.signals event(:loop_start)
113
122
  end
114
123
 
115
124
  @patterns = []
@@ -135,17 +144,17 @@ module Roby
135
144
  # +context+ is forwarded to the planned task
136
145
  def append_pattern(*context)
137
146
  # Create the new pattern
138
- task_arguments = arguments.slice(:planner_model, :planned_model, :method_name)
147
+ task_arguments = arguments.slice(:planner_model, :planned_model, :planning_method)
139
148
  task_arguments[:method_options] = method_options.dup
140
149
  task_arguments[:method_options][:pattern_id] = @pattern_id
141
150
  @pattern_id += 1
142
151
 
143
152
  planning = PlanningTask.new(task_arguments)
144
153
  planned = planning.planned_task
145
- planned.forward(:start, self, :loop_start)
146
- planned.forward(:success, self, :loop_success)
147
- planned.forward(:stop, self, :loop_end)
148
- main_task.realized_by planned
154
+ planned.forward_to(:start, self, :loop_start)
155
+ planned.forward_to(:success, self, :loop_success)
156
+ planned.forward_to(:stop, self, :loop_end)
157
+ main_task.depends_on planned, :model => planned.model
149
158
 
150
159
  # Schedule it. We start the new pattern when these three conditions are met:
151
160
  # * it has been planned (planning has finished)
@@ -175,13 +184,15 @@ module Roby
175
184
  if last_planning.finished?
176
185
  planning.start!(*context)
177
186
  else
178
- last_planning.event(:success).filter(*context).on(planning.event(:start))
187
+ last_planning.event(:success).
188
+ filter(*context).
189
+ signals(planning.event(:start))
179
190
  end
180
191
  end
181
192
  command &= precondition
182
193
 
183
194
  patterns.unshift([planning, user_command])
184
- command.on(planned.event(:start))
195
+ command.signals(planned.event(:start))
185
196
  planning
186
197
  end
187
198
 
@@ -189,13 +200,9 @@ module Roby
189
200
  # as lookahead requires. Kills the currently running pattern (if there
190
201
  # is one).
191
202
  event :reinit do |context|
192
- unless running?
193
- raise ArgumentError, "#reinit called, but the loop is not running"
194
- end
195
-
196
203
  did_reinit = []
197
204
 
198
- # Remove all realized_by relations and all pending patterns from
205
+ # Remove all depends_on relations and all pending patterns from
199
206
  # the pattern set.
200
207
  for pattern in patterns
201
208
  old_planning, ev = pattern
@@ -216,7 +223,7 @@ module Roby
216
223
  did_reinit.
217
224
  map { |ev| ev.when_unreachable }.
218
225
  inject { |a, b| a & b }.
219
- forward event(:reinit)
226
+ forward_to event(:reinit)
220
227
  end
221
228
  end
222
229
  on :reinit do |ev|
@@ -242,7 +249,7 @@ module Roby
242
249
  new_planning = append_pattern
243
250
  first_planning ||= new_planning
244
251
  end
245
- on(:start, first_planning)
252
+ signals(:start, first_planning, :start)
246
253
  end
247
254
 
248
255
  emit :start
@@ -1,10 +1,3 @@
1
- require 'roby/planning/task'
2
- require 'roby/task'
3
- require 'roby/control'
4
- require 'roby/plan'
5
- require 'utilrb/module/ancestor_p'
6
- require 'set'
7
-
8
1
  module Roby
9
2
  # The Planning module provides basic tools to create plans (graph of tasks
10
3
  # and events)
@@ -19,6 +12,33 @@ module Roby
19
12
  end
20
13
  end
21
14
 
15
+ MethodArgDescription = Struct.new :name, :doc, :required
16
+ class MethodDescription
17
+ attr_reader :doc
18
+
19
+ attr_reader :arguments
20
+
21
+ attr_predicate :advanced?
22
+
23
+ def initialize(doc = nil)
24
+ @doc = doc
25
+ @arguments = []
26
+ end
27
+
28
+ def required_arg(name, doc)
29
+ arguments << MethodArgDescription.new(name, doc, true)
30
+ self
31
+ end
32
+ def optional_arg(name, doc)
33
+ arguments << MethodArgDescription.new(name, doc, false)
34
+ self
35
+ end
36
+ def advanced
37
+ @advanced = true
38
+ self
39
+ end
40
+ end
41
+
22
42
  # Raised a method has found no valid development
23
43
  class NotFound < PlanModelError
24
44
  # The name of the method which has failed
@@ -35,33 +55,43 @@ module Roby
35
55
  super(planner)
36
56
  end
37
57
 
38
- def message
58
+ def pretty_print(pp)
39
59
  if errors.empty?
40
- "no candidate for #{method_name}(#{method_options})"
60
+ pp.text "no candidate for #{method_name}(#{method_options})"
41
61
  else
42
- msg = "cannot develop a #{method_name}(#{method_options}) method"
43
- first, *rem = *Roby.filter_backtrace(backtrace)
62
+ first, *rem = Roby.filter_backtrace(backtrace)
63
+ pp.text "cannot develop a #{method_name}(#{method_options.to_s[1..-2]}) method"
64
+ pp.breakable
65
+ pp.group(4, " ") do
66
+ rem.each do |line|
67
+ pp.text "from #{line}"
68
+ pp.breakable
69
+ end
70
+ end
44
71
 
45
- full = "#{first}: #{msg}\n from #{rem.join("\n from ")}"
72
+ pp.breakable
46
73
  errors.each do |m, error|
47
- first, *rem = *Roby.filter_backtrace(error.backtrace)
48
- full << "\n#{first}: #{m} failed with #{error.message}\n from #{rem.join("\n from ")}"
74
+ if error.kind_of?(NotFound)
75
+ first, *rem = *Roby.filter_backtrace(error.backtrace)
76
+ pp.text "in method #{m}"
77
+ pp.breakable
78
+ error.pretty_print(pp)
79
+ else
80
+ first, *rem = *Roby.filter_backtrace(error.backtrace)
81
+ pp.text "planning method #{m} failed"
82
+ pp.breakable
83
+ pp.text "#{first}: #{error.message}"
84
+ pp.breakable
85
+ pp.group(4, " ") do
86
+ rem.each do |line|
87
+ pp.text "from #{line}"
88
+ pp.breakable
89
+ end
90
+ end
91
+ end
49
92
  end
50
- full
51
- end
52
- end
53
-
54
- def full_message
55
- msg = message
56
- first, *rem = *Roby.filter_backtrace(backtrace)
57
-
58
- full = "#{first}: #{msg}\n from #{rem.join("\n from ")}"
59
- errors.each do |m, error|
60
- first = error.backtrace.first
61
- full << "\n#{first} #{m} failed because of #{error.full_message}"
62
93
  end
63
- full
64
- end
94
+ end
65
95
  end
66
96
 
67
97
  # Some common tools for Planner and Library
@@ -130,7 +160,15 @@ module Roby
130
160
  # Call the method definition
131
161
  def call(planner); body.call(planner) end
132
162
 
133
- def to_s; "#{name}:#{id}(#{options})" end
163
+ def to_s
164
+ opts = options.dup
165
+ opts.delete :id
166
+ "#{name}:#{id}(#{opts.to_s[1..-2]})"
167
+ end
168
+ end
169
+
170
+ class FreeMethod < MethodDefinition
171
+ def call(planner); planner.instance_eval(&body) end
134
172
  end
135
173
 
136
174
  # The model of a planning method. This does not define an actual
@@ -151,7 +189,9 @@ module Roby
151
189
  # The model options, as a Hash
152
190
  attr_reader :options
153
191
 
154
- def initialize(name, options = Hash.new); @name, @options = name, options end
192
+ def initialize(name, options = Hash.new)
193
+ @name, @options = name, options
194
+ end
155
195
  def ==(model)
156
196
  name == model.name && options == model.options
157
197
  end
@@ -210,6 +250,8 @@ module Roby
210
250
  def to_s; "#{name}(#{options})" end
211
251
  end
212
252
 
253
+ PlanningMethod = Struct.new :name, :model, :description, :instances
254
+
213
255
  # A planner searches a suitable development for a set of methods.
214
256
  # Methods are defined using Planner::method. You can then ask
215
257
  # for a plan by sending your method name to the Planner object
@@ -321,6 +363,25 @@ module Roby
321
363
  end
322
364
  end
323
365
 
366
+ # call-seq:
367
+ # describe(first_line, second_line, ...).
368
+ # required_arg(arg_name, arg_doc).
369
+ # optional_arg(arg_name, arg_doc)
370
+ #
371
+ # Describes the next method or method model. It adds a description
372
+ # text for the method, which can be shown for instance by the
373
+ # shell's "action" command. It is also possible to describe the
374
+ # expected method arguments.
375
+ def self.describe(*text)
376
+ if text.empty?
377
+ text = ["(no description set)"]
378
+ else
379
+ text.map! { |s| s.to_str }
380
+ end
381
+
382
+ @next_method_description = MethodDescription.new(text)
383
+ end
384
+
324
385
  # call-seq:
325
386
  # method(name, option1 => value1, option2 => value2) { } => method definition
326
387
  # method(name, option1 => value1, option2 => value2) => method model
@@ -410,7 +471,7 @@ module Roby
410
471
  def self.method(name, options = Hash.new, &body)
411
472
  name, options = validate_method_query(name, options)
412
473
 
413
- # Define the method enumerator and the method selection
474
+ # Define the method enumerator and the method public interface
414
475
  if !respond_to?("#{name}_methods")
415
476
  inherited_enumerable("#{name}_method", "#{name}_methods", :map => true) { Hash.new }
416
477
  class_eval <<-PLANNING_METHOD_END
@@ -421,7 +482,17 @@ module Roby
421
482
  cached_enum("#{name}_method", "#{name}_methods", true)
422
483
  end
423
484
  PLANNING_METHOD_END
485
+ singleton_class.class_eval do
486
+ attr_reader "#{name}_description"
487
+ end
424
488
  end
489
+ if @next_method_description
490
+ if instance_variable_get("@#{name}_description")
491
+ raise "#{name} already has a description"
492
+ end
493
+ instance_variable_set("@#{name}_description", @next_method_description)
494
+ @next_method_description = nil
495
+ end
425
496
 
426
497
  # We are updating the method model
427
498
  if !body
@@ -463,7 +534,8 @@ module Roby
463
534
  end
464
535
  temp_method_name = "m#{@@temp_method_id += 1}"
465
536
  define_method(temp_method_name, &body)
466
- send("#{name}_methods")[method_id] = MethodDefinition.new(name, options, instance_method(temp_method_name))
537
+ mdef = MethodDefinition.new(name, options, instance_method(temp_method_name))
538
+ send("#{name}_methods")[method_id] = mdef
467
539
  end
468
540
  @@temp_method_id = 0
469
541
 
@@ -479,6 +551,33 @@ module Roby
479
551
  names
480
552
  end
481
553
 
554
+ def self.planning_methods
555
+ names = methods.map do |method_name|
556
+ if method_name =~ /^each_(\w+)_method$/
557
+ $1
558
+ end
559
+ end.compact.sort
560
+
561
+ names.map do |name|
562
+ desc = PlanningMethod.new
563
+ desc.name = name
564
+ desc.description = planning_method_description(name)
565
+ #desc.model = method_model(name)
566
+ #desc.instances = Array.new
567
+ #send("each_#{name}_method") do |instance|
568
+ # desc.instances << instance
569
+ #end
570
+ desc
571
+ end
572
+ end
573
+
574
+ def self.planning_method_description(name)
575
+ return instance_variable_get("@#{name}_description") || MethodDescription.new
576
+ end
577
+ def planning_method_description(name)
578
+ self.class.planning_method_description(name)
579
+ end
580
+
482
581
  def self.clear_model
483
582
  planning_methods_names.each do |name|
484
583
  remove_planning_method(name)
@@ -608,6 +707,13 @@ module Roby
608
707
  MethodModel.new(name, :returns => Task)
609
708
  end
610
709
 
710
+ # Creates a TaskSequence with the given tasks
711
+ def sequence(*tasks)
712
+ seq = Sequence.new
713
+ tasks.each { |t| seq << t }
714
+ seq
715
+ end
716
+
611
717
  # Creates a planning task which will call the same planning method
612
718
  # than the one currently being generated.
613
719
  #
@@ -653,9 +759,8 @@ module Roby
653
759
  elsif planning_options[:args] && !planning_options[:args].empty?
654
760
  raise ArgumentError, "provided method-specific options through both :args and the option hash"
655
761
  end
656
- @arguments.push(method_options)
657
762
 
658
- Planning.debug { "planning #{name}[#{arguments}]" }
763
+ Planning.debug { "planning #{name}[#{method_options}]" }
659
764
 
660
765
  # Check for recursion
661
766
  if (options[:id] && @stack.include?([name, options[:id]])) || (!options[:id] && @stack.find { |n, _| n == name })
@@ -678,7 +783,7 @@ module Roby
678
783
  all_returns.compact!
679
784
 
680
785
  for return_type in all_returns
681
- if task = find_reusable_task(return_type)
786
+ if task = find_reusable_task(return_type, method_options)
682
787
  return task
683
788
  end
684
789
  end
@@ -689,7 +794,7 @@ module Roby
689
794
  end
690
795
 
691
796
  # Call the methods
692
- call_planning_methods(Hash.new, options, *methods)
797
+ call_planning_methods(Hash.new, method_options, *methods)
693
798
 
694
799
  rescue Interrupt
695
800
  raise
@@ -697,22 +802,19 @@ module Roby
697
802
  rescue NotFound => e
698
803
  e.method_name = name
699
804
  e.method_options = options
700
- raise e
701
-
702
- ensure
703
- @arguments.pop
805
+ raise e.dup, e.message, caller(1)
704
806
  end
705
807
 
706
- def find_reusable_task(return_type)
808
+ def find_reusable_task(return_type, method_options)
707
809
  query = plan.find_tasks.
708
- which_fullfills(return_type, arguments).
810
+ which_fullfills(return_type, method_options).
709
811
  self_owned.
710
812
  not_abstract.
711
813
  not_finished.
712
814
  roots(TaskStructure::Hierarchy)
713
815
 
714
816
  for candidate in query
715
- Planning.debug { "selecting task #{candidate} instead of planning #{return_type}[#{arguments}]" }
817
+ Planning.debug { "selecting task #{candidate} instead of planning #{return_type}[#{method_options}]" }
716
818
  return candidate
717
819
  end
718
820
  nil
@@ -724,9 +826,10 @@ module Roby
724
826
  # Tries to find a successfull development in the provided method list.
725
827
  #
726
828
  # It raises NotFound if none of the methods returned successfully
727
- def call_planning_methods(errors, options, method, *methods)
829
+ def call_planning_methods(errors, method_options, method, *methods)
728
830
  begin
729
831
  @stack.push [method.name, method.id]
832
+ @arguments.push(method_options)
730
833
  Planning.debug { "calling #{method.name}:#{method.id} with arguments #{arguments}" }
731
834
  begin
732
835
  result = method.call(self)
@@ -742,7 +845,7 @@ module Roby
742
845
  end
743
846
 
744
847
  # Insert resulting tasks in +plan+
745
- plan.discover(result)
848
+ plan.add(result)
746
849
 
747
850
  expected_return = method.returns
748
851
  if expected_return
@@ -762,6 +865,7 @@ module Roby
762
865
  result
763
866
 
764
867
  ensure
868
+ @arguments.pop
765
869
  @stack.pop
766
870
  end
767
871
 
@@ -771,7 +875,7 @@ module Roby
771
875
  if methods.empty?
772
876
  raise NotFound.new(self, errors)
773
877
  else
774
- call_planning_methods(errors, options, *methods)
878
+ call_planning_methods(errors, method_options, *methods)
775
879
  end
776
880
  end
777
881
 
@@ -782,20 +886,22 @@ module Roby
782
886
  def make_loop(options = {}, &block)
783
887
  raise ArgumentError, "no block given" unless block
784
888
 
785
- options.merge! :planner_model => self.class, :method_name => 'loops'
786
- _, planning_options = PlanningLoop.filter_options(options)
787
-
788
889
  loop_id = Planner.next_id
789
890
  if !@stack.empty?
790
891
  loop_id = "#{@stack.last[1]}_#{loop_id}"
791
892
  end
893
+ loop_method = FreeMethod.new 'loops', {}, lambda(&block)
894
+
895
+ options[:planner_model] = self.class
896
+ options[:planning_method] = loop_method
897
+ _, planning_options = PlanningLoop.filter_options(options)
792
898
  planning_options[:id] = loop_id
793
899
  planning_options[:reuse] = false
794
- m = self.class.method('loops', planning_options, &block)
900
+ loop_method.options.merge!(planning_options)
795
901
 
796
902
  options[:method_options] ||= {}
797
903
  options[:method_options].merge!(arguments || {})
798
- options[:method_options][:id] = m.id
904
+ options[:method_options][:id] = loop_method.id
799
905
  PlanningLoop.new(options)
800
906
  end
801
907
  end