roby 0.8.0 → 3.0.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 (644) hide show
  1. checksums.yaml +7 -0
  2. data/.deep-cover.rb +3 -0
  3. data/.gitattributes +1 -0
  4. data/.gitignore +24 -0
  5. data/.simplecov +10 -0
  6. data/.travis.yml +17 -0
  7. data/.yardopts +4 -0
  8. data/Gemfile +15 -0
  9. data/README.md +11 -0
  10. data/Rakefile +47 -177
  11. data/benchmark/{alloc_misc.rb → attic/alloc_misc.rb} +2 -2
  12. data/benchmark/{discovery_latency.rb → attic/discovery_latency.rb} +19 -19
  13. data/benchmark/{garbage_collection.rb → attic/garbage_collection.rb} +9 -9
  14. data/benchmark/{genom.rb → attic/genom.rb} +0 -0
  15. data/benchmark/attic/transactions.rb +62 -0
  16. data/benchmark/plan_basic_operations.rb +28 -0
  17. data/benchmark/relations/graph.rb +63 -0
  18. data/benchmark/ruby/identity.rb +18 -0
  19. data/benchmark/ruby/set_intersect_vs_hash_merge.rb +39 -0
  20. data/benchmark/ruby/yield_vs_block.rb +35 -0
  21. data/benchmark/run +5 -0
  22. data/benchmark/synthetic_plan_modifications_with_transactions.rb +79 -0
  23. data/benchmark/transactions.rb +99 -51
  24. data/bin/roby +38 -197
  25. data/bin/roby-display +14 -0
  26. data/bin/roby-log +3 -176
  27. data/doc/guide/{src → attic}/abstraction/achieve_with.page +1 -1
  28. data/doc/guide/{src → attic}/abstraction/forwarding.page +1 -1
  29. data/doc/guide/{src → attic}/abstraction/hierarchy.page +1 -1
  30. data/doc/guide/{src → attic}/abstraction/index.page +1 -1
  31. data/doc/guide/{src → attic}/abstraction/task_models.page +1 -1
  32. data/doc/guide/{overview.rdoc → attic/cycle/api_overview.rdoc} +6 -1
  33. data/doc/guide/{src → attic}/cycle/cycle-overview.png +0 -0
  34. data/doc/guide/{src → attic}/cycle/cycle-overview.svg +0 -0
  35. data/doc/guide/attic/cycle/error_handling.page +98 -0
  36. data/doc/guide/{src → attic}/cycle/error_instantaneous_repair.png +0 -0
  37. data/doc/guide/{src → attic}/cycle/error_instantaneous_repair.svg +0 -0
  38. data/doc/guide/{src/cycle/error_handling.page → attic/cycle/error_sources.page} +46 -89
  39. data/doc/guide/{src → attic}/cycle/garbage_collection.page +1 -1
  40. data/doc/guide/{src → attic}/cycle/index.page +1 -1
  41. data/doc/guide/{src → attic}/cycle/propagation.page +11 -1
  42. data/doc/guide/{src → attic}/cycle/propagation_diamond.png +0 -0
  43. data/doc/guide/{src → attic}/cycle/propagation_diamond.svg +0 -0
  44. data/doc/guide/attic/plans/building_plans.page +89 -0
  45. data/doc/guide/attic/plans/code.page +192 -0
  46. data/doc/guide/{src/basics → attic/plans}/events.page +3 -4
  47. data/doc/guide/attic/plans/index.page +7 -0
  48. data/doc/guide/{plan_modifications.rdoc → attic/plans/plan_modifications.rdoc} +5 -3
  49. data/doc/guide/{src/basics → attic/plans}/plan_objects.page +2 -1
  50. data/doc/guide/attic/plans/querying_plans.page +5 -0
  51. data/doc/guide/{src/basics → attic/plans}/tasks.page +20 -20
  52. data/doc/guide/config.yaml +7 -4
  53. data/doc/guide/ext/extended_menu.rb +29 -0
  54. data/doc/guide/ext/init.rb +6 -0
  55. data/doc/guide/ext/rdoc_links.rb +7 -6
  56. data/doc/guide/src/advanced_concepts/history.page +5 -0
  57. data/doc/guide/src/advanced_concepts/index.page +11 -0
  58. data/doc/guide/src/advanced_concepts/recognizing_patterns.page +83 -0
  59. data/doc/guide/src/advanced_concepts/scheduling.page +87 -0
  60. data/doc/guide/src/advanced_concepts/transactions.page +5 -0
  61. data/doc/guide/src/advanced_concepts/unreachability.page +42 -0
  62. data/doc/guide/src/base.template +96 -0
  63. data/doc/guide/src/basics_shell_header.txt +5 -7
  64. data/doc/guide/src/building/action_coordination.page +96 -0
  65. data/doc/guide/src/building/actions.page +124 -0
  66. data/doc/guide/src/building/file_layout.page +71 -0
  67. data/doc/guide/src/building/index.page +50 -0
  68. data/doc/guide/src/building/patterns.page +86 -0
  69. data/doc/guide/src/building/patterns_forwarding.png +0 -0
  70. data/doc/guide/src/building/patterns_forwarding.svg +277 -0
  71. data/doc/guide/src/building/runtime.page +95 -0
  72. data/doc/guide/src/building/task_models.page +94 -0
  73. data/doc/guide/src/building/tasks.page +284 -0
  74. data/doc/guide/src/concepts/error_handling.page +100 -0
  75. data/doc/guide/src/concepts/exception_propagation.png +0 -0
  76. data/doc/guide/src/concepts/exception_propagation.svg +445 -0
  77. data/doc/guide/src/concepts/execution.page +85 -0
  78. data/doc/guide/src/concepts/execution.png +0 -0
  79. data/doc/guide/src/concepts/execution.svg +573 -0
  80. data/doc/guide/src/concepts/execution_cycle.png +0 -0
  81. data/doc/guide/src/concepts/garbage_collection.page +57 -0
  82. data/doc/guide/src/concepts/index.page +27 -0
  83. data/doc/guide/src/concepts/plans.page +101 -0
  84. data/doc/guide/src/concepts/policy.page +31 -0
  85. data/doc/guide/src/concepts/reactor.page +61 -0
  86. data/doc/guide/src/concepts/simple_plan_example.png +0 -0
  87. data/doc/guide/src/concepts/simple_plan_example.svg +376 -0
  88. data/doc/guide/src/default.template +9 -74
  89. data/doc/guide/src/event_relations/forward.page +71 -0
  90. data/doc/guide/src/event_relations/index.page +12 -0
  91. data/doc/guide/src/event_relations/scheduling_constraints.page +43 -0
  92. data/doc/guide/src/event_relations/signal.page +55 -0
  93. data/doc/guide/src/event_relations/temporal_constraints.page +77 -0
  94. data/doc/guide/src/htmldoc.metainfo +21 -8
  95. data/doc/guide/src/index.page +8 -3
  96. data/doc/guide/src/{introduction/install.page → installation/index.page} +37 -25
  97. data/doc/guide/src/installation/publications.page +14 -0
  98. data/doc/guide/src/{introduction → installation}/videos.page +14 -7
  99. data/doc/guide/src/interacting/index.page +16 -0
  100. data/doc/guide/src/interacting/run.page +33 -0
  101. data/doc/guide/src/interacting/shell.page +95 -0
  102. data/doc/guide/src/plugins/creating_plugins.page +72 -0
  103. data/doc/guide/src/plugins/index.page +27 -5
  104. data/doc/guide/src/plugins/{fault_tolerance.page → standard_plugins/fault_tolerance.page} +2 -2
  105. data/doc/guide/src/plugins/standard_plugins/index.page +11 -0
  106. data/doc/guide/src/plugins/{subsystems.page → standard_plugins/subsystems.page} +2 -2
  107. data/doc/guide/src/style_screen.css +687 -0
  108. data/doc/guide/src/task_relations/dependency.page +107 -0
  109. data/doc/guide/src/task_relations/executed_by.page +77 -0
  110. data/doc/guide/src/task_relations/index.page +12 -0
  111. data/doc/guide/src/task_relations/new_relations.page +119 -0
  112. data/doc/guide/src/task_relations/planned_by.page +46 -0
  113. data/doc/guide/src/tutorial/app.page +117 -0
  114. data/doc/guide/src/{basics → tutorial}/code_examples.page +6 -5
  115. data/doc/guide/src/{basics → tutorial}/dry.page +15 -15
  116. data/doc/guide/src/{basics → tutorial}/errors.page +43 -68
  117. data/doc/guide/src/tutorial/events.page +195 -0
  118. data/doc/guide/src/{basics → tutorial}/hierarchy.page +53 -52
  119. data/doc/guide/src/tutorial/index.page +13 -0
  120. data/doc/guide/src/tutorial/log_replay/goForward_1.png +0 -0
  121. data/doc/guide/src/tutorial/log_replay/goForward_2.png +0 -0
  122. data/doc/guide/src/tutorial/log_replay/goForward_3.png +0 -0
  123. data/doc/guide/src/{basics → tutorial}/log_replay/goForward_4.png +0 -0
  124. data/doc/guide/src/tutorial/log_replay/goForward_5.png +0 -0
  125. data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_1.png +0 -0
  126. data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_2.png +0 -0
  127. data/doc/guide/src/{basics → tutorial}/log_replay/hierarchy_error_3.png +0 -0
  128. data/doc/guide/src/tutorial/log_replay/moveto_code_error.png +0 -0
  129. data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_1.png +0 -0
  130. data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_2.png +0 -0
  131. data/doc/guide/src/{basics → tutorial}/log_replay/plan_repair_3.png +0 -0
  132. data/doc/guide/src/tutorial/log_replay/plan_repair_4.png +0 -0
  133. data/doc/guide/src/tutorial/log_replay/roby_log_main_window.png +0 -0
  134. data/doc/guide/src/{basics → tutorial}/log_replay/roby_log_relation_window.png +0 -0
  135. data/doc/guide/src/{basics → tutorial}/log_replay/roby_replay_event_representation.png +0 -0
  136. data/doc/guide/src/tutorial/relations_display.page +153 -0
  137. data/doc/guide/src/{basics → tutorial}/roby_cycle_overview.png +0 -0
  138. data/doc/guide/src/tutorial/shell.page +121 -0
  139. data/doc/guide/src/{basics → tutorial}/summary.page +1 -1
  140. data/doc/guide/src/tutorial/tasks.page +374 -0
  141. data/lib/roby.rb +102 -47
  142. data/lib/roby/actions.rb +17 -0
  143. data/lib/roby/actions/action.rb +80 -0
  144. data/lib/roby/actions/interface.rb +45 -0
  145. data/lib/roby/actions/library.rb +23 -0
  146. data/lib/roby/actions/models/action.rb +224 -0
  147. data/lib/roby/actions/models/coordination_action.rb +58 -0
  148. data/lib/roby/actions/models/interface.rb +22 -0
  149. data/lib/roby/actions/models/interface_base.rb +294 -0
  150. data/lib/roby/actions/models/library.rb +12 -0
  151. data/lib/roby/actions/models/method_action.rb +90 -0
  152. data/lib/roby/actions/task.rb +114 -0
  153. data/lib/roby/and_generator.rb +125 -0
  154. data/lib/roby/app.rb +2795 -829
  155. data/lib/roby/app/autotest_console_reporter.rb +138 -0
  156. data/lib/roby/app/base.rb +21 -0
  157. data/lib/roby/app/cucumber.rb +2 -0
  158. data/lib/roby/app/cucumber/controller.rb +439 -0
  159. data/lib/roby/app/cucumber/helpers.rb +280 -0
  160. data/lib/roby/app/cucumber/world.rb +32 -0
  161. data/lib/roby/app/debug.rb +136 -0
  162. data/lib/roby/app/gen.rb +2 -0
  163. data/lib/roby/app/rake.rb +178 -38
  164. data/lib/roby/app/robot_config.rb +9 -0
  165. data/lib/roby/app/robot_names.rb +115 -0
  166. data/lib/roby/app/run.rb +3 -2
  167. data/lib/roby/app/scripts.rb +72 -0
  168. data/lib/roby/app/scripts/autotest.rb +173 -0
  169. data/lib/roby/app/scripts/display.rb +2 -0
  170. data/lib/roby/app/scripts/restart.rb +52 -0
  171. data/lib/roby/app/scripts/results.rb +17 -8
  172. data/lib/roby/app/scripts/run.rb +155 -24
  173. data/lib/roby/app/scripts/shell.rb +147 -62
  174. data/lib/roby/app/scripts/test.rb +107 -22
  175. data/lib/roby/app/test_reporter.rb +74 -0
  176. data/lib/roby/app/test_server.rb +159 -0
  177. data/lib/roby/app/vagrant.rb +47 -0
  178. data/lib/roby/backports.rb +16 -0
  179. data/lib/roby/cli/display.rb +190 -0
  180. data/lib/roby/cli/exceptions.rb +17 -0
  181. data/lib/roby/cli/gen/actions/class.rb +5 -0
  182. data/lib/roby/cli/gen/actions/test.rb +6 -0
  183. data/lib/roby/cli/gen/app/.yardopts +6 -0
  184. data/lib/roby/cli/gen/app/README.md +28 -0
  185. data/lib/roby/cli/gen/app/Rakefile +15 -0
  186. data/{app → lib/roby/cli/gen/app}/config/app.yml +29 -39
  187. data/lib/roby/cli/gen/app/models/.gitattributes +1 -0
  188. data/{app → lib/roby/cli/gen/app/scripts}/controllers/.gitattributes +0 -0
  189. data/{app/data/.gitattributes → lib/roby/cli/gen/app/test/.gitignore} +0 -0
  190. data/lib/roby/cli/gen/class/class.rb +6 -0
  191. data/lib/roby/cli/gen/class/test.rb +7 -0
  192. data/lib/roby/cli/gen/helpers.rb +203 -0
  193. data/lib/roby/cli/gen/module/module.rb +5 -0
  194. data/lib/roby/cli/gen/module/test.rb +6 -0
  195. data/lib/roby/cli/gen/roby_app/config/init.rb +17 -0
  196. data/lib/roby/cli/gen/roby_app/config/robots/robot.rb +40 -0
  197. data/lib/roby/cli/gen/task/class.rb +44 -0
  198. data/lib/roby/cli/gen/task/test.rb +6 -0
  199. data/lib/roby/cli/gen_main.rb +120 -0
  200. data/lib/roby/cli/log.rb +276 -0
  201. data/lib/roby/cli/log/flamegraph.html +499 -0
  202. data/lib/roby/cli/log/flamegraph_renderer.rb +88 -0
  203. data/lib/roby/cli/main.rb +153 -0
  204. data/lib/roby/coordination.rb +60 -0
  205. data/lib/roby/coordination/action_script.rb +25 -0
  206. data/lib/roby/coordination/action_state_machine.rb +125 -0
  207. data/lib/roby/coordination/actions.rb +106 -0
  208. data/lib/roby/coordination/base.rb +145 -0
  209. data/lib/roby/coordination/calculus.rb +40 -0
  210. data/lib/roby/coordination/child.rb +28 -0
  211. data/lib/roby/coordination/event.rb +29 -0
  212. data/lib/roby/coordination/fault_handler.rb +25 -0
  213. data/lib/roby/coordination/fault_handling_task.rb +13 -0
  214. data/lib/roby/coordination/fault_response_table.rb +110 -0
  215. data/lib/roby/coordination/models/action_script.rb +64 -0
  216. data/lib/roby/coordination/models/action_state_machine.rb +224 -0
  217. data/lib/roby/coordination/models/actions.rb +191 -0
  218. data/lib/roby/coordination/models/arguments.rb +55 -0
  219. data/lib/roby/coordination/models/base.rb +176 -0
  220. data/lib/roby/coordination/models/capture.rb +86 -0
  221. data/lib/roby/coordination/models/child.rb +35 -0
  222. data/lib/roby/coordination/models/event.rb +41 -0
  223. data/lib/roby/coordination/models/exceptions.rb +42 -0
  224. data/lib/roby/coordination/models/fault_handler.rb +219 -0
  225. data/lib/roby/coordination/models/fault_response_table.rb +77 -0
  226. data/lib/roby/coordination/models/root.rb +22 -0
  227. data/lib/roby/coordination/models/script.rb +283 -0
  228. data/lib/roby/coordination/models/task.rb +184 -0
  229. data/lib/roby/coordination/models/task_from_action.rb +50 -0
  230. data/lib/roby/coordination/models/task_from_as_plan.rb +33 -0
  231. data/lib/roby/coordination/models/task_from_instanciation_object.rb +31 -0
  232. data/lib/roby/coordination/models/task_from_variable.rb +27 -0
  233. data/lib/roby/coordination/models/task_with_dependencies.rb +48 -0
  234. data/lib/roby/coordination/models/variable.rb +32 -0
  235. data/lib/roby/coordination/script.rb +200 -0
  236. data/lib/roby/coordination/script_instruction.rb +12 -0
  237. data/lib/roby/coordination/task.rb +45 -0
  238. data/lib/roby/coordination/task_base.rb +69 -0
  239. data/lib/roby/coordination/task_script.rb +293 -0
  240. data/lib/roby/coordination/task_state_machine.rb +308 -0
  241. data/lib/roby/decision_control.rb +33 -21
  242. data/lib/roby/distributed_object.rb +76 -0
  243. data/lib/roby/droby.rb +17 -0
  244. data/lib/roby/droby/droby_id.rb +6 -0
  245. data/lib/roby/droby/enable.rb +153 -0
  246. data/lib/roby/droby/event_logger.rb +189 -0
  247. data/lib/roby/droby/event_logging.rb +57 -0
  248. data/lib/roby/droby/exceptions.rb +14 -0
  249. data/lib/roby/droby/identifiable.rb +22 -0
  250. data/lib/roby/droby/logfile.rb +141 -0
  251. data/lib/roby/droby/logfile/client.rb +176 -0
  252. data/lib/roby/droby/logfile/file_format.md +97 -0
  253. data/lib/roby/droby/logfile/index.rb +117 -0
  254. data/lib/roby/droby/logfile/reader.rb +139 -0
  255. data/lib/roby/droby/logfile/server.rb +199 -0
  256. data/lib/roby/droby/logfile/writer.rb +114 -0
  257. data/lib/roby/droby/marshal.rb +264 -0
  258. data/lib/roby/droby/marshallable.rb +12 -0
  259. data/lib/roby/droby/null_event_logger.rb +25 -0
  260. data/lib/roby/droby/object_manager.rb +205 -0
  261. data/lib/roby/droby/peer_id.rb +6 -0
  262. data/lib/roby/droby/plan_rebuilder.rb +373 -0
  263. data/lib/roby/droby/rebuilt_plan.rb +160 -0
  264. data/lib/roby/droby/remote_droby_id.rb +6 -0
  265. data/lib/roby/droby/timepoints.rb +205 -0
  266. data/lib/roby/droby/timepoints_ctf.metadata.erb +101 -0
  267. data/lib/roby/droby/timepoints_ctf.rb +125 -0
  268. data/lib/roby/droby/v5.rb +14 -0
  269. data/lib/roby/droby/v5/builtin.rb +120 -0
  270. data/lib/roby/droby/v5/droby_class.rb +45 -0
  271. data/lib/roby/droby/v5/droby_constant.rb +81 -0
  272. data/lib/roby/droby/v5/droby_dump.rb +1026 -0
  273. data/lib/roby/droby/v5/droby_id.rb +44 -0
  274. data/lib/roby/droby/v5/droby_model.rb +82 -0
  275. data/lib/roby/droby/v5/peer_id.rb +10 -0
  276. data/lib/roby/droby/v5/remote_droby_id.rb +42 -0
  277. data/lib/roby/event.rb +79 -957
  278. data/lib/roby/event_constraints.rb +835 -0
  279. data/lib/roby/event_generator.rb +1047 -0
  280. data/lib/roby/event_structure/causal_link.rb +6 -0
  281. data/lib/roby/event_structure/forwarding.rb +6 -0
  282. data/lib/roby/event_structure/precedence.rb +7 -0
  283. data/lib/roby/event_structure/signal.rb +8 -0
  284. data/lib/roby/event_structure/temporal_constraints.rb +640 -0
  285. data/lib/roby/exceptions.rb +446 -152
  286. data/lib/roby/executable_plan.rb +549 -0
  287. data/lib/roby/execution_engine.rb +1997 -950
  288. data/lib/roby/filter_generator.rb +26 -0
  289. data/lib/roby/gui/chronicle_view.rb +225 -0
  290. data/lib/roby/gui/chronicle_widget.rb +925 -0
  291. data/lib/roby/gui/dot_id.rb +11 -0
  292. data/lib/roby/gui/exception_view.rb +44 -0
  293. data/lib/roby/gui/log_display.rb +273 -0
  294. data/lib/roby/gui/model_views.rb +2 -0
  295. data/lib/roby/gui/model_views/action_interface.rb +53 -0
  296. data/lib/roby/gui/model_views/task.rb +47 -0
  297. data/lib/roby/gui/model_views/task.rhtml +41 -0
  298. data/lib/roby/gui/object_info_view.rb +89 -0
  299. data/lib/roby/gui/plan_dot_layout.rb +427 -0
  300. data/lib/roby/gui/plan_rebuilder_widget.rb +357 -0
  301. data/lib/roby/gui/qt4_toMSecsSinceEpoch.rb +8 -0
  302. data/lib/roby/gui/relations_view.rb +278 -0
  303. data/lib/roby/gui/relations_view/relations.ui +139 -0
  304. data/lib/roby/gui/relations_view/relations_canvas.rb +1088 -0
  305. data/lib/roby/gui/relations_view/relations_config.rb +292 -0
  306. data/lib/roby/gui/relations_view/relations_view.ui +53 -0
  307. data/lib/roby/gui/scheduler_view.css +24 -0
  308. data/lib/roby/gui/scheduler_view.rb +46 -0
  309. data/lib/roby/gui/scheduler_view.rhtml +53 -0
  310. data/lib/roby/gui/stepping.rb +93 -0
  311. data/lib/roby/gui/stepping.ui +181 -0
  312. data/lib/roby/gui/styles.rb +81 -0
  313. data/lib/roby/gui/task_display_configuration.rb +42 -0
  314. data/lib/roby/gui/task_state_at.rb +38 -0
  315. data/lib/roby/hooks.rb +26 -0
  316. data/lib/roby/interface.rb +136 -469
  317. data/lib/roby/interface/async.rb +20 -0
  318. data/lib/roby/interface/async/action_monitor.rb +188 -0
  319. data/lib/roby/interface/async/interface.rb +498 -0
  320. data/lib/roby/interface/async/job_monitor.rb +213 -0
  321. data/lib/roby/interface/async/log.rb +238 -0
  322. data/lib/roby/interface/async/new_job_listener.rb +79 -0
  323. data/lib/roby/interface/async/ui_connector.rb +183 -0
  324. data/lib/roby/interface/client.rb +553 -0
  325. data/lib/roby/interface/command.rb +24 -0
  326. data/lib/roby/interface/command_argument.rb +16 -0
  327. data/lib/roby/interface/command_library.rb +92 -0
  328. data/lib/roby/interface/droby_channel.rb +174 -0
  329. data/lib/roby/interface/exceptions.rb +22 -0
  330. data/lib/roby/interface/interface.rb +655 -0
  331. data/lib/roby/interface/job.rb +47 -0
  332. data/lib/roby/interface/rest.rb +10 -0
  333. data/lib/roby/interface/rest/api.rb +29 -0
  334. data/lib/roby/interface/rest/helpers.rb +24 -0
  335. data/lib/roby/interface/rest/server.rb +212 -0
  336. data/lib/roby/interface/server.rb +154 -0
  337. data/lib/roby/interface/shell_client.rb +468 -0
  338. data/lib/roby/interface/shell_subcommand.rb +24 -0
  339. data/lib/roby/interface/subcommand_client.rb +35 -0
  340. data/lib/roby/interface/tcp.rb +168 -0
  341. data/lib/roby/models/arguments.rb +112 -0
  342. data/lib/roby/models/plan_object.rb +83 -0
  343. data/lib/roby/models/task.rb +835 -0
  344. data/lib/roby/models/task_event.rb +62 -0
  345. data/lib/roby/models/task_service.rb +78 -0
  346. data/lib/roby/or_generator.rb +88 -0
  347. data/lib/roby/plan.rb +1751 -864
  348. data/lib/roby/plan_object.rb +611 -0
  349. data/lib/roby/plan_service.rb +200 -0
  350. data/lib/roby/promise.rb +332 -0
  351. data/lib/roby/queries.rb +23 -0
  352. data/lib/roby/queries/and_matcher.rb +32 -0
  353. data/lib/roby/queries/any.rb +27 -0
  354. data/lib/roby/queries/code_error_matcher.rb +58 -0
  355. data/lib/roby/queries/event_generator_matcher.rb +9 -0
  356. data/lib/roby/queries/execution_exception_matcher.rb +165 -0
  357. data/lib/roby/queries/index.rb +165 -0
  358. data/lib/roby/queries/localized_error_matcher.rb +149 -0
  359. data/lib/roby/queries/matcher_base.rb +107 -0
  360. data/lib/roby/queries/none.rb +27 -0
  361. data/lib/roby/queries/not_matcher.rb +30 -0
  362. data/lib/roby/queries/op_matcher.rb +8 -0
  363. data/lib/roby/queries/or_matcher.rb +30 -0
  364. data/lib/roby/queries/plan_object_matcher.rb +363 -0
  365. data/lib/roby/queries/query.rb +188 -0
  366. data/lib/roby/queries/task_event_generator_matcher.rb +86 -0
  367. data/lib/roby/queries/task_matcher.rb +344 -0
  368. data/lib/roby/relations.rb +42 -678
  369. data/lib/roby/relations/bidirectional_directed_adjacency_graph.rb +492 -0
  370. data/lib/roby/relations/directed_relation_support.rb +268 -0
  371. data/lib/roby/relations/event_relation_graph.rb +19 -0
  372. data/lib/roby/relations/fork_merge_visitor.rb +154 -0
  373. data/lib/roby/relations/graph.rb +533 -0
  374. data/lib/roby/relations/models/directed_relation_support.rb +11 -0
  375. data/lib/roby/relations/models/graph.rb +75 -0
  376. data/lib/roby/relations/models/task_relation_graph.rb +18 -0
  377. data/lib/roby/relations/space.rb +380 -0
  378. data/lib/roby/relations/task_relation_graph.rb +20 -0
  379. data/lib/roby/robot.rb +85 -38
  380. data/lib/roby/schedulers/basic.rb +155 -25
  381. data/lib/roby/schedulers/null.rb +20 -0
  382. data/lib/roby/schedulers/reporting.rb +31 -0
  383. data/lib/roby/schedulers/state.rb +129 -0
  384. data/lib/roby/schedulers/temporal.rb +91 -0
  385. data/lib/roby/singletons.rb +87 -0
  386. data/lib/roby/standalone.rb +4 -2
  387. data/lib/roby/standard_errors.rb +405 -82
  388. data/lib/roby/state.rb +6 -3
  389. data/lib/roby/state/conf_model.rb +5 -0
  390. data/lib/roby/state/events.rb +181 -95
  391. data/lib/roby/state/goal_model.rb +77 -0
  392. data/lib/roby/state/open_struct.rb +591 -0
  393. data/lib/roby/state/open_struct_model.rb +68 -0
  394. data/lib/roby/state/pos.rb +45 -45
  395. data/lib/roby/state/shapes.rb +11 -11
  396. data/lib/roby/state/state_model.rb +303 -0
  397. data/lib/roby/state/task.rb +43 -0
  398. data/lib/roby/support.rb +88 -148
  399. data/lib/roby/task.rb +1361 -1750
  400. data/lib/roby/task_arguments.rb +428 -0
  401. data/lib/roby/task_event.rb +127 -0
  402. data/lib/roby/task_event_generator.rb +337 -0
  403. data/lib/roby/task_service.rb +6 -0
  404. data/lib/roby/task_structure/conflicts.rb +104 -0
  405. data/lib/roby/task_structure/dependency.rb +932 -0
  406. data/lib/roby/task_structure/error_handling.rb +118 -0
  407. data/lib/roby/task_structure/executed_by.rb +234 -0
  408. data/lib/roby/task_structure/planned_by.rb +90 -0
  409. data/lib/roby/tasks/aggregator.rb +37 -0
  410. data/lib/roby/tasks/external_process.rb +275 -0
  411. data/lib/roby/tasks/group.rb +27 -0
  412. data/lib/roby/tasks/null.rb +19 -0
  413. data/lib/roby/tasks/parallel.rb +43 -0
  414. data/lib/roby/tasks/sequence.rb +88 -0
  415. data/lib/roby/tasks/simple.rb +21 -0
  416. data/lib/roby/{thread_task.rb → tasks/thread.rb} +50 -24
  417. data/lib/roby/tasks/timeout.rb +17 -0
  418. data/lib/roby/tasks/virtual.rb +55 -0
  419. data/lib/roby/template_plan.rb +7 -0
  420. data/lib/roby/test/aruba_minitest.rb +74 -0
  421. data/lib/roby/test/assertion.rb +16 -0
  422. data/lib/roby/test/assertions.rb +490 -0
  423. data/lib/roby/test/common.rb +368 -591
  424. data/lib/roby/test/dsl.rb +149 -0
  425. data/lib/roby/test/error.rb +18 -0
  426. data/lib/roby/test/event_reporter.rb +83 -0
  427. data/lib/roby/test/execution_expectations.rb +1134 -0
  428. data/lib/roby/test/expect_execution.rb +151 -0
  429. data/lib/roby/test/minitest_helpers.rb +166 -0
  430. data/lib/roby/test/roby_app_helpers.rb +200 -0
  431. data/lib/roby/test/run_planners.rb +155 -0
  432. data/lib/roby/test/self.rb +112 -0
  433. data/lib/roby/test/spec.rb +198 -0
  434. data/lib/roby/test/tasks/empty_task.rb +4 -4
  435. data/lib/roby/test/tasks/goto.rb +28 -27
  436. data/lib/roby/test/teardown_plans.rb +100 -0
  437. data/lib/roby/test/testcase.rb +239 -307
  438. data/lib/roby/test/tools.rb +159 -155
  439. data/lib/roby/test/validate_state_machine.rb +75 -0
  440. data/lib/roby/transaction.rb +1125 -0
  441. data/lib/roby/transaction/event_generator_proxy.rb +63 -0
  442. data/lib/roby/transaction/plan_object_proxy.rb +99 -0
  443. data/lib/roby/transaction/plan_service_proxy.rb +43 -0
  444. data/lib/roby/transaction/proxying.rb +120 -0
  445. data/lib/roby/transaction/task_event_generator_proxy.rb +19 -0
  446. data/lib/roby/transaction/task_proxy.rb +135 -0
  447. data/lib/roby/until_generator.rb +30 -0
  448. data/lib/roby/version.rb +5 -0
  449. data/lib/roby/yard.rb +169 -0
  450. data/lib/yard-roby.rb +1 -0
  451. data/manifest.xml +32 -6
  452. data/roby.gemspec +59 -0
  453. metadata +788 -587
  454. data/Manifest.txt +0 -321
  455. data/NOTES +0 -4
  456. data/README.txt +0 -166
  457. data/TODO.txt +0 -146
  458. data/app/README.txt +0 -24
  459. data/app/Rakefile +0 -8
  460. data/app/config/ROBOT.rb +0 -5
  461. data/app/config/init.rb +0 -33
  462. data/app/config/roby.yml +0 -3
  463. data/app/controllers/ROBOT.rb +0 -2
  464. data/app/planners/ROBOT/main.rb +0 -6
  465. data/app/planners/main.rb +0 -5
  466. data/app/scripts/distributed +0 -3
  467. data/app/scripts/generate/bookmarks +0 -3
  468. data/app/scripts/replay +0 -3
  469. data/app/scripts/results +0 -3
  470. data/app/scripts/run +0 -3
  471. data/app/scripts/server +0 -3
  472. data/app/scripts/shell +0 -3
  473. data/app/scripts/test +0 -3
  474. data/app/tasks/.gitattributes +0 -0
  475. data/app/tasks/ROBOT/.gitattributes +0 -0
  476. data/bin/roby-shell +0 -25
  477. data/doc/guide/src/basics/app.page +0 -139
  478. data/doc/guide/src/basics/index.page +0 -11
  479. data/doc/guide/src/basics/log_replay/goForward_1.png +0 -0
  480. data/doc/guide/src/basics/log_replay/goForward_2.png +0 -0
  481. data/doc/guide/src/basics/log_replay/goForward_3.png +0 -0
  482. data/doc/guide/src/basics/log_replay/goForward_5.png +0 -0
  483. data/doc/guide/src/basics/log_replay/plan_repair_4.png +0 -0
  484. data/doc/guide/src/basics/log_replay/roby_log_main_window.png +0 -0
  485. data/doc/guide/src/basics/relations_display.page +0 -203
  486. data/doc/guide/src/basics/shell.page +0 -102
  487. data/doc/guide/src/default.css +0 -319
  488. data/doc/guide/src/introduction/index.page +0 -29
  489. data/doc/guide/src/introduction/publications.page +0 -14
  490. data/doc/guide/src/relations/dependency.page +0 -89
  491. data/doc/guide/src/relations/index.page +0 -12
  492. data/ext/droby/dump.cc +0 -175
  493. data/ext/droby/extconf.rb +0 -3
  494. data/ext/graph/algorithm.cc +0 -746
  495. data/ext/graph/extconf.rb +0 -7
  496. data/ext/graph/graph.cc +0 -575
  497. data/ext/graph/graph.hh +0 -183
  498. data/ext/graph/iterator_sequence.hh +0 -102
  499. data/ext/graph/undirected_dfs.hh +0 -226
  500. data/ext/graph/undirected_graph.hh +0 -421
  501. data/lib/roby/app/scripts/generate/bookmarks.rb +0 -162
  502. data/lib/roby/app/scripts/replay.rb +0 -31
  503. data/lib/roby/app/scripts/server.rb +0 -18
  504. data/lib/roby/basic_object.rb +0 -151
  505. data/lib/roby/config.rb +0 -14
  506. data/lib/roby/distributed.rb +0 -36
  507. data/lib/roby/distributed/base.rb +0 -448
  508. data/lib/roby/distributed/communication.rb +0 -875
  509. data/lib/roby/distributed/connection_space.rb +0 -616
  510. data/lib/roby/distributed/distributed_object.rb +0 -206
  511. data/lib/roby/distributed/drb.rb +0 -62
  512. data/lib/roby/distributed/notifications.rb +0 -531
  513. data/lib/roby/distributed/peer.rb +0 -555
  514. data/lib/roby/distributed/protocol.rb +0 -529
  515. data/lib/roby/distributed/proxy.rb +0 -343
  516. data/lib/roby/distributed/subscription.rb +0 -311
  517. data/lib/roby/distributed/transaction.rb +0 -498
  518. data/lib/roby/external_process_task.rb +0 -225
  519. data/lib/roby/graph.rb +0 -160
  520. data/lib/roby/log.rb +0 -3
  521. data/lib/roby/log/chronicle.rb +0 -303
  522. data/lib/roby/log/console.rb +0 -74
  523. data/lib/roby/log/data_stream.rb +0 -275
  524. data/lib/roby/log/dot.rb +0 -279
  525. data/lib/roby/log/event_stream.rb +0 -161
  526. data/lib/roby/log/file.rb +0 -396
  527. data/lib/roby/log/gui/basic_display.ui +0 -83
  528. data/lib/roby/log/gui/basic_display_ui.rb +0 -89
  529. data/lib/roby/log/gui/chronicle.rb +0 -26
  530. data/lib/roby/log/gui/chronicle_view.rb +0 -40
  531. data/lib/roby/log/gui/chronicle_view.ui +0 -70
  532. data/lib/roby/log/gui/chronicle_view_ui.rb +0 -90
  533. data/lib/roby/log/gui/data_displays.rb +0 -171
  534. data/lib/roby/log/gui/data_displays.ui +0 -155
  535. data/lib/roby/log/gui/data_displays_ui.rb +0 -146
  536. data/lib/roby/log/gui/notifications.rb +0 -26
  537. data/lib/roby/log/gui/relations.rb +0 -269
  538. data/lib/roby/log/gui/relations.ui +0 -123
  539. data/lib/roby/log/gui/relations_ui.rb +0 -120
  540. data/lib/roby/log/gui/relations_view.rb +0 -185
  541. data/lib/roby/log/gui/relations_view.ui +0 -149
  542. data/lib/roby/log/gui/relations_view_ui.rb +0 -144
  543. data/lib/roby/log/gui/replay.rb +0 -366
  544. data/lib/roby/log/gui/replay_controls.rb +0 -206
  545. data/lib/roby/log/gui/replay_controls.ui +0 -282
  546. data/lib/roby/log/gui/replay_controls_ui.rb +0 -249
  547. data/lib/roby/log/gui/runtime.rb +0 -130
  548. data/lib/roby/log/hooks.rb +0 -186
  549. data/lib/roby/log/logger.rb +0 -203
  550. data/lib/roby/log/notifications.rb +0 -244
  551. data/lib/roby/log/plan_rebuilder.rb +0 -468
  552. data/lib/roby/log/relations.rb +0 -1084
  553. data/lib/roby/log/server.rb +0 -547
  554. data/lib/roby/log/sqlite.rb +0 -47
  555. data/lib/roby/log/timings.rb +0 -233
  556. data/lib/roby/plan-object.rb +0 -371
  557. data/lib/roby/planning.rb +0 -13
  558. data/lib/roby/planning/loops.rb +0 -309
  559. data/lib/roby/planning/model.rb +0 -1012
  560. data/lib/roby/planning/task.rb +0 -180
  561. data/lib/roby/query.rb +0 -655
  562. data/lib/roby/relations/conflicts.rb +0 -67
  563. data/lib/roby/relations/dependency.rb +0 -358
  564. data/lib/roby/relations/ensured.rb +0 -19
  565. data/lib/roby/relations/error_handling.rb +0 -22
  566. data/lib/roby/relations/events.rb +0 -7
  567. data/lib/roby/relations/executed_by.rb +0 -208
  568. data/lib/roby/relations/influence.rb +0 -10
  569. data/lib/roby/relations/planned_by.rb +0 -63
  570. data/lib/roby/state/information.rb +0 -55
  571. data/lib/roby/state/state.rb +0 -367
  572. data/lib/roby/task-operations.rb +0 -186
  573. data/lib/roby/task_index.rb +0 -80
  574. data/lib/roby/test/distributed.rb +0 -230
  575. data/lib/roby/test/tasks/simple_task.rb +0 -23
  576. data/lib/roby/transactions.rb +0 -507
  577. data/lib/roby/transactions/proxy.rb +0 -325
  578. data/plugins/fault_injection/History.txt +0 -4
  579. data/plugins/fault_injection/README.txt +0 -34
  580. data/plugins/fault_injection/Rakefile +0 -12
  581. data/plugins/fault_injection/TODO.txt +0 -0
  582. data/plugins/fault_injection/app.rb +0 -52
  583. data/plugins/fault_injection/fault_injection.rb +0 -89
  584. data/plugins/fault_injection/test/test_fault_injection.rb +0 -78
  585. data/plugins/subsystems/README.txt +0 -37
  586. data/plugins/subsystems/Rakefile +0 -13
  587. data/plugins/subsystems/app.rb +0 -182
  588. data/plugins/subsystems/test/app/README +0 -24
  589. data/plugins/subsystems/test/app/Rakefile +0 -8
  590. data/plugins/subsystems/test/app/config/app.yml +0 -71
  591. data/plugins/subsystems/test/app/config/init.rb +0 -12
  592. data/plugins/subsystems/test/app/config/roby.yml +0 -3
  593. data/plugins/subsystems/test/app/planners/main.rb +0 -20
  594. data/plugins/subsystems/test/app/scripts/distributed +0 -3
  595. data/plugins/subsystems/test/app/scripts/replay +0 -3
  596. data/plugins/subsystems/test/app/scripts/results +0 -3
  597. data/plugins/subsystems/test/app/scripts/run +0 -3
  598. data/plugins/subsystems/test/app/scripts/server +0 -3
  599. data/plugins/subsystems/test/app/scripts/shell +0 -3
  600. data/plugins/subsystems/test/app/scripts/test +0 -3
  601. data/plugins/subsystems/test/app/tasks/services.rb +0 -15
  602. data/plugins/subsystems/test/test_subsystems.rb +0 -78
  603. data/test/distributed/test_communication.rb +0 -195
  604. data/test/distributed/test_connection.rb +0 -284
  605. data/test/distributed/test_execution.rb +0 -378
  606. data/test/distributed/test_mixed_plan.rb +0 -341
  607. data/test/distributed/test_plan_notifications.rb +0 -238
  608. data/test/distributed/test_protocol.rb +0 -525
  609. data/test/distributed/test_query.rb +0 -106
  610. data/test/distributed/test_remote_plan.rb +0 -491
  611. data/test/distributed/test_transaction.rb +0 -466
  612. data/test/mockups/external_process +0 -28
  613. data/test/mockups/tasks.rb +0 -27
  614. data/test/planning/test_loops.rb +0 -432
  615. data/test/planning/test_model.rb +0 -427
  616. data/test/planning/test_task.rb +0 -126
  617. data/test/relations/test_conflicts.rb +0 -42
  618. data/test/relations/test_dependency.rb +0 -324
  619. data/test/relations/test_ensured.rb +0 -38
  620. data/test/relations/test_executed_by.rb +0 -224
  621. data/test/relations/test_planned_by.rb +0 -56
  622. data/test/suite_core.rb +0 -29
  623. data/test/suite_distributed.rb +0 -10
  624. data/test/suite_planning.rb +0 -4
  625. data/test/suite_relations.rb +0 -8
  626. data/test/tasks/test_external_process.rb +0 -126
  627. data/test/tasks/test_thread_task.rb +0 -70
  628. data/test/test_bgl.rb +0 -528
  629. data/test/test_event.rb +0 -969
  630. data/test/test_exceptions.rb +0 -591
  631. data/test/test_execution_engine.rb +0 -987
  632. data/test/test_gui.rb +0 -20
  633. data/test/test_interface.rb +0 -43
  634. data/test/test_log.rb +0 -125
  635. data/test/test_log_server.rb +0 -133
  636. data/test/test_plan.rb +0 -418
  637. data/test/test_query.rb +0 -424
  638. data/test/test_relations.rb +0 -260
  639. data/test/test_state.rb +0 -432
  640. data/test/test_support.rb +0 -16
  641. data/test/test_task.rb +0 -1181
  642. data/test/test_testcase.rb +0 -138
  643. data/test/test_transactions.rb +0 -610
  644. data/test/test_transactions_proxy.rb +0 -216
@@ -1,13 +0,0 @@
1
- require 'roby'
2
-
3
- module Roby
4
- module Planning
5
- extend Logger::Hierarchy
6
- extend Logger::Forward
7
- end
8
- end
9
-
10
- require 'roby/planning/task'
11
- require 'roby/planning/loops'
12
- require 'roby/planning/model'
13
-
@@ -1,309 +0,0 @@
1
- module Roby
2
- # This class unrolls a loop in the plan. It maintains +lookahead+ patterns
3
- # developped at all times by calling an external planner, and manages them.
4
- # This documentation will start by describing the general behaviour of this
5
- # task, and then we will detail different specific modes of operation.
6
- #
7
- # == Behaviour description
8
- # The task unrolls the loop by generating /patterns/, which are a
9
- # combination of a task representing the operation to be done during one
10
- # pass of the loop, and a planning task which will generate the subplan for
11
- # this operation. These patterns are developped as children of either the
12
- # PlanningLoop task itself, or its planned_task if there is one.
13
- #
14
- # During the execution of this suite of patterns, the following constraints
15
- # are always met:
16
- #
17
- # * the planning task of a pattern is started after the one of the previous
18
- # pattern has finished.
19
- # * a pattern is started after the previous one has finished.
20
- #
21
- # The #start! command do not starts the loop per-se. It only makes the
22
- # first +lookahead+ patterns to be developped. You have to call
23
- # #loop_start! once to start the generated patterns themselves.
24
- #
25
- # == Periodic and nonperiodic loops
26
- # On the one hand, if the +:period+ option of #initialize is non-nil, it is
27
- # expected to be a floating-point value representing a time in seconds. In
28
- # that case, the loop is *periodic* and each pattern in the loop is started
29
- # at the given periodic rate, triggered by the #periodic_trigger event.
30
- # Note that the 'zero-period' case is a special situation where the loop
31
- # runs as fast as possible.
32
- #
33
- # On the other hand, if +:period+ is nil, the loop is nonperiodic, and each
34
- # pattern must be explicitely started by calling #loop_start!. Finally,
35
- # #loop_start! can also be called to bypass the period value (i.e. to
36
- # start a pattern earlier than expected). Repetitive calls to #loop_start!
37
- # will make the loop develop and start at most one pattern.
38
- #
39
- # == Zero lookahead
40
- # When the loop lookahead is nonzero, patterns are planend ahead-of-time: they
41
- # are planned as soon as possible. In some cases, it is non desirable, for instance
42
- # because some information is available only at a later time.
43
- #
44
- # For these situations, one can use a zero lookahead. In that case, the
45
- # patterns are not pre-planned, but instead the planning task is started
46
- # only when the pattern itself should have been started: either when the
47
- # period timeouts, or when #loop_start! is explicitely called.
48
- #
49
- # TODO: make figures.
50
- #
51
- class PlanningLoop < Roby::Task
52
- terminates
53
-
54
- # An array of [planning_task, user_command]. The *last* element is the
55
- # *first* arrived
56
- attr_reader :patterns
57
-
58
- # For periodic updates. If false, the next loop is started when the
59
- # 'loop_start' command is called
60
- argument :period
61
- # How many loops should we have unrolled at all times
62
- argument :lookahead
63
-
64
- # The task model we should produce
65
- argument :planned_model
66
-
67
- # The planner model we should use
68
- argument :planner_model
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
80
- # The planner method options
81
- argument :method_options
82
-
83
- # The method name. This can be nil a FreeMethod is used for planning
84
- argument :method_name
85
-
86
- # Filters the options in +options+, splitting between the options that
87
- # are specific to the planning task and those that are to be forwarded
88
- # to the planner itself
89
- def self.filter_options(options)
90
- task_arguments, planning_options = Kernel.filter_options options,
91
- :period => nil,
92
- :lookahead => 1,
93
- :planner_model => nil,
94
- :planning_method => nil,
95
- :planned_model => Roby::Task,
96
- :method_name => nil,
97
- :method_options => {},
98
- :planning_owners => nil
99
-
100
- task_arguments = PlanningTask.validate_planning_options(task_arguments)
101
-
102
- if task_arguments[:lookahead] < 0
103
- raise ArgumentError, "lookahead must be positive"
104
- end
105
- task_arguments[:period] ||= nil
106
- [task_arguments, planning_options]
107
- end
108
-
109
- # If this loop is periodic of nonzero period, the state event which
110
- # represents that period.
111
- attr_reader :periodic_trigger
112
-
113
- def initialize(options)
114
- task_arguments, planning_options = PlanningLoop.filter_options(options)
115
- task_arguments[:method_options].merge!(planning_options)
116
- super(task_arguments)
117
-
118
- if period && period > 0
119
- @periodic_trigger = State.on_delta :t => period
120
- periodic_trigger.disable
121
- periodic_trigger.signals event(:loop_start)
122
- end
123
-
124
- @patterns = []
125
- @pattern_id = 0
126
- end
127
-
128
- # The task on which the children are added
129
- def main_task; planned_task || self end
130
-
131
- def planned_task # :nodoc:
132
- planned_tasks.find { true }
133
- end
134
-
135
- # The PlanningTask object for the last pattern
136
- def last_planning_task
137
- if pattern = patterns.first
138
- pattern.first
139
- end
140
- end
141
-
142
- # Appends a new unplanned pattern after all the patterns already developped
143
- #
144
- # +context+ is forwarded to the planned task
145
- def append_pattern(*context)
146
- # Create the new pattern
147
- task_arguments = arguments.slice(:planner_model, :planned_model, :planning_method)
148
- task_arguments[:method_options] = method_options.dup
149
- task_arguments[:method_options][:pattern_id] = @pattern_id
150
- @pattern_id += 1
151
-
152
- planning = PlanningTask.new(task_arguments)
153
- planned = planning.planned_task
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
158
-
159
- # Schedule it. We start the new pattern when these three conditions are met:
160
- # * it has been planned (planning has finished)
161
- # * the previous one (if any) has finished
162
- # * the period (if any) has expired or an external event required
163
- # the explicit start of the pattern (call done to user_command,
164
- # for instance through a call to #loop_start!)
165
- #
166
- # The +precondition+ event represents a situation where the new pattern
167
- # *can* be started, while +command+ is the situation asking for the
168
- # pattern to start.
169
- precondition = planning.event(:success)
170
- user_command = EventGenerator.new(true)
171
- command = user_command
172
-
173
- if last_planning = last_planning_task
174
- last_planned = last_planning.planned_task
175
-
176
- if !last_planned.finished?
177
- precondition &= last_planned.event(:stop)
178
- end
179
-
180
- if period && !periodic_trigger
181
- command |= planned.event(:success)
182
- end
183
-
184
- if last_planning.finished?
185
- planning.start!(*context)
186
- else
187
- last_planning.event(:success).
188
- filter(*context).
189
- signals(planning.event(:start))
190
- end
191
- end
192
- command &= precondition
193
-
194
- patterns.unshift([planning, user_command])
195
- command.signals(planned.event(:start))
196
- planning
197
- end
198
-
199
- # Remove all pending patterns and starts unrolling as much new patterns
200
- # as lookahead requires. Kills the currently running pattern (if there
201
- # is one).
202
- event :reinit do |context|
203
- did_reinit = []
204
-
205
- # Remove all depends_on relations and all pending patterns from
206
- # the pattern set.
207
- for pattern in patterns
208
- old_planning, ev = pattern
209
- old_task = old_planning.planned_task
210
- main_task.remove_child old_task
211
-
212
- if old_task && old_task.running?
213
- did_reinit << old_task.event(:stop)
214
- elsif old_planning.running?
215
- did_reinit << old_planning.event(:stop)
216
- end
217
- end
218
- patterns.clear
219
-
220
- if did_reinit.empty?
221
- emit :reinit
222
- else
223
- did_reinit.
224
- map { |ev| ev.when_unreachable }.
225
- inject { |a, b| a & b }.
226
- forward_to event(:reinit)
227
- end
228
- end
229
- on :reinit do |ev|
230
- @pattern_id = 0
231
- if lookahead > 0
232
- first_planning = nil
233
- while patterns.size < lookahead
234
- new_planning = append_pattern
235
- first_planning ||= new_planning
236
- end
237
- first_planning.start!
238
- end
239
- loop_start!
240
- end
241
-
242
- # Generates the first +lookahead+ patterns and start planning. The
243
- # patterns themselves are started when +loop_start+ is called the first
244
- # time.
245
- event :start do
246
- if lookahead > 0
247
- first_planning = nil
248
- while patterns.size < lookahead
249
- new_planning = append_pattern
250
- first_planning ||= new_planning
251
- end
252
- signals(:start, first_planning, :start)
253
- end
254
-
255
- emit :start
256
- end
257
-
258
-
259
- # The first time, start executing the patterns. During the loop
260
- # execution, force starting the next pending pattern, bypassing the
261
- # period if there is one. In case of zero-lookahead loops, the next
262
- # pattern will be planned before it is executed.
263
- event :loop_start do |context|
264
- # Start the periodic trigger if there is one
265
- if periodic_trigger && periodic_trigger.disabled?
266
- periodic_trigger.enable
267
- end
268
-
269
- # Find the first non-running pattern and start it. In case of
270
- # zero-lookahead, if no task is already pending, we should add one
271
- # and start it explicitely
272
- if new_pattern = patterns.reverse.find { |task, ev| task.planned_task.pending? }
273
- t, ev = new_pattern
274
- ev.call(*context)
275
- command = ev.enum_child_objects(EventStructure::Signal).find { true }
276
- elsif lookahead == 0
277
- start_planning = !last_planning_task
278
- planning = append_pattern(*context)
279
- if start_planning
280
- planning.start!(*context)
281
- end
282
- _, ev = patterns[0]
283
- ev.call(*context)
284
- end
285
- end
286
-
287
- on :loop_start do |event|
288
- return unless self_owned?
289
- if event.task.lookahead != 0
290
- append_pattern
291
- end
292
-
293
- main_task.remove_finished_children
294
- end
295
-
296
- event :loop_success
297
-
298
- event :loop_end
299
- on :loop_end do |event|
300
- return unless self_owned?
301
- patterns.pop
302
- end
303
-
304
- # For ordering during event propagation
305
- causal_link :loop_start => :loop_end
306
- causal_link :loop_success => :loop_end
307
- end
308
- end
309
-
@@ -1,1012 +0,0 @@
1
- module Roby
2
- # The Planning module provides basic tools to create plans (graph of tasks
3
- # and events)
4
- module Planning
5
- # Violation of plan models, for instance if a method returns a Task object
6
- # which is of a wrong model
7
- class PlanModelError < RuntimeError
8
- attr_accessor :planner
9
- def initialize(planner = nil)
10
- @planner = planner
11
- super()
12
- end
13
- end
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
-
42
- # Raised a method has found no valid development
43
- class NotFound < PlanModelError
44
- # The name of the method which has failed
45
- attr_accessor :method_name
46
- # The planning options
47
- attr_accessor :method_options
48
- # A method => error hash of all the method that have
49
- # been tried. +error+ can either be a NotFound exception
50
- # or another exception
51
- attr_reader :errors
52
-
53
- def initialize(planner, errors)
54
- @errors = errors
55
- super(planner)
56
- end
57
-
58
- def pretty_print(pp)
59
- if errors.empty?
60
- pp.text "no candidate for #{method_name}(#{method_options})"
61
- else
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
71
-
72
- pp.breakable
73
- errors.each do |m, error|
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
92
- end
93
- end
94
- end
95
- end
96
-
97
- # Some common tools for Planner and Library
98
- module Tools
99
- def using(*modules)
100
- modules.each do |mod|
101
- if mod.respond_to?(:planning_methods)
102
- include mod
103
- elsif planning_mod = (mod.const_get('Planning') rescue nil)
104
- include planning_mod
105
- else
106
- raise ArgumentError, "#{mod} is not a planning library and has no Planning module which is one"
107
- end
108
- end
109
- end
110
- end
111
-
112
- # This mixin defines the method inheritance validation method. This is
113
- # then used by MethodDefinition and MethodModel
114
- module MethodInheritance
115
- # Checks that options in +options+ can be used to overload +self+.
116
- # Updates options if needed
117
- def validate(options)
118
- if returns
119
- if options[:returns] && !(options[:returns] <= returns)
120
- raise ArgumentError, "return task type #{options[:returns]} forbidden since it overloads #{returns}"
121
- else
122
- options[:returns] ||= returns
123
- end
124
- end
125
-
126
- if self.options.has_key?(:reuse)
127
- if options.has_key?(:reuse) && options[:reuse] != self.options[:reuse]
128
- raise ArgumentError, "the :reuse option is already set on the #{name} model"
129
- end
130
- options[:reuse] = self.options[:reuse]
131
- else
132
- options[:reuse] = true unless options.has_key?(:reuse)
133
- end
134
-
135
- options
136
- end
137
- end
138
-
139
- # An implementation of a planning method.
140
- class MethodDefinition
141
- include MethodInheritance
142
-
143
- attr_reader :name, :options, :body
144
- def initialize(name, options, body)
145
- @name, @options, @body = name, options, body
146
- end
147
-
148
- # The method ID
149
- def id; options[:id] end
150
- # If this method handles recursion
151
- def recursive?; options[:recursive] end
152
- # What kind of task this method returns
153
- #
154
- # If this is nil, the method may return a task array or a task
155
- # aggregation
156
- def returns; options[:returns] end
157
- # If the method allows reusing tasks already in the plan
158
- # reuse? is always false if there is no return type defined
159
- def reuse?; (!options.has_key?(:reuse) || options[:reuse]) if returns end
160
- # Call the method definition
161
- def call(planner); body.call(planner) end
162
-
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
172
- end
173
-
174
- # The model of a planning method. This does not define an actual
175
- # implementation of the method, only the model methods should abide to.
176
- class MethodModel
177
- include MethodInheritance
178
-
179
- # The return type the method model defines
180
- #
181
- # If this is nil, methods of this model may return a task array
182
- # or a task aggregation
183
- def returns; options[:returns] end
184
- # If the model allows reusing tasks already in the plan
185
- def reuse?; !options.has_key?(:reuse) || options[:reuse] end
186
-
187
- # The model name
188
- attr_reader :name
189
- # The model options, as a Hash
190
- attr_reader :options
191
-
192
- def initialize(name, options = Hash.new)
193
- @name, @options = name, options
194
- end
195
- def ==(model)
196
- name == model.name && options == model.options
197
- end
198
-
199
- # call-seq:
200
- # merge(new_options) => self
201
- #
202
- # Add new options in this model. Raises ArgumentError if the
203
- # new options cannot be merged because they are incompatible
204
- # with the current model definition
205
- def merge(new_options)
206
- validate_options(new_options, [:returns, :reuse])
207
- validate_option(new_options, :returns, false) do |rettype|
208
- if options[:returns] && options[:returns] != rettype
209
- raise ArgumentError, "return type already specified for method #{name}"
210
- end
211
- options[:returns] = rettype
212
- end
213
- validate_option(new_options, :reuse, false) do |flag|
214
- if options.has_key?(:reuse) && options[:reuse] != flag
215
- raise ArgumentError, "the reuse flag is already set to #{options[:reuse]} on #{name}"
216
- end
217
- options[:reuse] = flag
218
- true
219
- end
220
-
221
- self
222
- end
223
-
224
- def overload(old_model)
225
- if old_returns = old_model.returns
226
- if returns && !(returns < old_returns)
227
- raise ArgumentError, "new return type #{returns} is not a subclass of the old one #{old_returns}"
228
- elsif !returns
229
- options[:returns] = old_returns
230
- end
231
- end
232
- if options.has_key?(:reuse) && old_model.options.has_key?(:reuse) && options[:reuse] != old_model.reuse
233
- raise ArgumentError, "the reuse flag for #{name}h as already been set to #{options[:reuse]} on our parent model"
234
- elsif !options.has_key?(:reuse) && old_model.options.has_key?(:reuse)
235
- options[:reuse] = old_model.reuse
236
- end
237
- end
238
-
239
- # Do not allow changing this model anymore
240
- def freeze
241
- options.freeze
242
- super
243
- end
244
-
245
- def initialize_copy(from) # :nodoc:
246
- @name = from.name.dup
247
- @options = from.options.dup
248
- end
249
-
250
- def to_s; "#{name}(#{options})" end
251
- end
252
-
253
- PlanningMethod = Struct.new :name, :model, :description, :instances
254
-
255
- # A planner searches a suitable development for a set of methods.
256
- # Methods are defined using Planner::method. You can then ask
257
- # for a plan by sending your method name to the Planner object
258
- #
259
- # For instance
260
- #
261
- # class MyPlanner < Planner
262
- # method(:do_it) { }
263
- # method(:do_sth_else) { ... }
264
- # end
265
- #
266
- # planner = MyPlanner.new
267
- # planner.do_it => result of the do_it block
268
- #
269
- # See Planner::method for a detailed description of the development
270
- # search
271
- #
272
- class Planner
273
- extend Tools
274
-
275
- # The resulting plan
276
- attr_reader :plan
277
-
278
- # Creates a Planner object which acts on +plan+
279
- def initialize(plan)
280
- @plan = plan
281
- @stack = Array.new
282
- @arguments = Array.new
283
- end
284
-
285
- # A list of options on which the methods are selected
286
- # in find_methods
287
- #
288
- # When calling a planning method, only the methods for
289
- # which these options match the user-provided options
290
- # are called. The other options are not considered
291
- METHOD_SELECTION_OPTIONS = [:id, :recursive, :returns]
292
- KNOWN_OPTIONS = [:lazy, :reuse, :args] + METHOD_SELECTION_OPTIONS
293
-
294
- def self.validate_method_query(name, options)
295
- name = name.to_s
296
- roby_options, method_arguments =
297
- filter_options options, KNOWN_OPTIONS
298
-
299
- validate_option(options, :returns, false,
300
- "the ':returns' option must be a task model") do |opt|
301
- opt.is_a?(Roby::TaskModelTag) ||
302
- opt.has_ancestor?(Roby::Task)
303
- end
304
-
305
- [name, roby_options, method_arguments]
306
- end
307
-
308
- # Return the method model for +name+, or nil
309
- def self.method_model(name)
310
- model = send("#{name}_model")
311
- rescue NoMethodError
312
- end
313
-
314
- class << self
315
- def last_id; @@last_id ||= 0 end
316
- def last_id=(new_value); @@last_id = new_value end
317
- def next_id; self.last_id += 1 end
318
- end
319
-
320
- # Some validation on the method IDs
321
- # * an integer represented as a string is converted to integer form
322
- # * a symbol is converted to string
323
- def self.validate_method_id(method_id)
324
- method_id = method_id.to_s if Symbol === method_id
325
-
326
- if method_id.respond_to?(:to_str) && method_id.to_str =~ /^\d+$/
327
- Integer(method_id)
328
- else
329
- method_id
330
- end
331
- end
332
-
333
- # Creates, overloads or updates a method model
334
- # Returns the MethodModel object
335
- def self.update_method_model(name, options)
336
- name = name.to_s
337
- unless send("enum_#{name}_methods", nil).empty?
338
- raise ArgumentError, "cannot change the method model for #{name} since methods are already using it"
339
- end
340
-
341
- old_model = method_model(name)
342
- new_model = MethodModel.new(name)
343
- new_model.merge(options)
344
-
345
- if old_model == new_model
346
- if !instance_variable_get("@#{name}_model")
347
- instance_variable_set("@#{name}_model", new_model)
348
- end
349
- return new_model
350
- elsif instance_variable_get("@#{name}_model")
351
- # old_model is defined at this level
352
- return old_model.merge(options)
353
- else
354
- unless respond_to?("#{name}_model")
355
- singleton_class.class_eval <<-EOD
356
- def #{name}_model
357
- @#{name}_model || superclass.#{name}_model
358
- end
359
- EOD
360
- end
361
- new_model.overload(old_model) if old_model
362
- instance_variable_set("@#{name}_model", new_model)
363
- end
364
- end
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
-
385
- # call-seq:
386
- # method(name, option1 => value1, option2 => value2) { } => method definition
387
- # method(name, option1 => value1, option2 => value2) => method model
388
- #
389
- # In the first form, define a new method +name+. The given block
390
- # is used as method definition. It shall either return a Task
391
- # object or an object whose #each method yields task objects, or
392
- # raise a PlanModelError exception, or of one of its subclasses.
393
- #
394
- # The second form defines a method model, which defines
395
- # constraints on the method defined with this name
396
- #
397
- # == Overloading: using the +id+ option
398
- # The +id+ option defines the method ID. The ID can be used
399
- # to override a method definition in submodels. For instance,
400
- # if you do
401
- #
402
- # class A < Planner
403
- # method(:do_it, :id => 'first') { ... }
404
- # method(:do_it, :id => 'second') { ... }
405
- # end
406
- # class B < A
407
- # method(:do_it, :id => 'first') { ... }
408
- # end
409
- #
410
- # Then calling B.new.do_it will call the +first+ method defined
411
- # in B and the +second+ defined in A
412
- #
413
- # If no method ID is given, an unique number is allocated. Try not
414
- # using numbers as method IDs yourself, since you could overload
415
- # an automatic ID.
416
- #
417
- # == Constraining the returned object
418
- # The +returns+ option defines what kind of Task object this method
419
- # shall return.
420
- #
421
- # For instance, in
422
- #
423
- # class A < Planner
424
- # method(:do_it, :id => 'first', :returns => MyTask) { ... }
425
- # method(:do_it, :id => 'second') { ... }
426
- # end
427
- # class B < A
428
- # method(:do_it, :id => 'first') { ... }
429
- # end
430
- #
431
- # The +do_it+ method defined in B will have to return a MyTask-derived
432
- # Task object. Method models can be used to put a constraint on all
433
- # methods of a given name. For instance, in the following example, all
434
- # +do_it+ methods would have to return MyTask-based objects
435
- #
436
- # class A < Planner
437
- # method(:do_it, :returns => MyTask)
438
- # method(:do_it, :id => 'first') { ... }
439
- # method(:do_it, :id => 'second') { ... }
440
- # end
441
- #
442
- # == Recursive call to methods
443
- # If the +recursive+ option is true, then the method can be called back even if it
444
- # currently being developed. The default is false
445
- #
446
- # For instance, the following example will raise a NoMethodError:
447
- #
448
- # class A < Planner
449
- # method(:do_it) { do_it }
450
- # end
451
- # A.new.do_it
452
- #
453
- # while this one will behave properly
454
- #
455
- # class A < Planner
456
- # method(:do_it) { do_it }
457
- # method(:do_it, :recursive => true) { ... }
458
- # end
459
- #
460
- # == Reusing already existing tasks in plan
461
- # If the +reuse+ flag is set (the default), instead of calling a method
462
- # definition, the planner will try to find a suitable task in the current
463
- # plan if the developed method defines a :returns attribute. Compatibility
464
- # is checked using Task#fullfills?
465
- #
466
- # == Defined attributes
467
- # For each method +name+, the planner class gets a few attributes and methods:
468
- # * each_name_method iterates on all MethodDefinition objects for +name+
469
- # * name_model returns the method model. It is not defined if no method model exists
470
- # * each_name_filter iterates on all filters for +name+
471
- def self.method(name, options = Hash.new, &body)
472
- name, options = validate_method_query(name, options)
473
-
474
- # Define the method enumerator and the method public interface
475
- if !respond_to?("#{name}_methods")
476
- inherited_enumerable("#{name}_method", "#{name}_methods", :map => true) { Hash.new }
477
- class_eval <<-PLANNING_METHOD_END
478
- def #{name}(options = Hash.new)
479
- plan_method("#{name}", options)
480
- end
481
- class << self
482
- cached_enum("#{name}_method", "#{name}_methods", true)
483
- end
484
- PLANNING_METHOD_END
485
- singleton_class.class_eval do
486
- attr_reader "#{name}_description"
487
- end
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
496
-
497
- # We are updating the method model
498
- if !body
499
- return update_method_model(name, options)
500
- end
501
-
502
- # Handle the method ID
503
- if method_id = options[:id]
504
- method_id = validate_method_id(method_id)
505
- if method_id.respond_to?(:to_int)
506
- self.last_id = method_id if self.last_id < method_id
507
- end
508
- else
509
- method_id = next_id
510
- end
511
- options[:id] = method_id
512
-
513
- # Get the method model (if any)
514
- if model = method_model(name)
515
- options = model.validate(options)
516
- model.freeze
517
- end
518
-
519
- # Check if we are overloading an old method
520
- if send("#{name}_methods")[method_id]
521
- raise ArgumentError, "method #{name}:#{method_id} is already defined on this planning model"
522
- elsif old_method = find_methods(name, :id => method_id)
523
- old_method = *old_method
524
- options = old_method.validate(options)
525
- Planning.debug { "overloading #{name}:#{method_id}" }
526
- end
527
-
528
- # Register the method definition
529
- #
530
- # First, define an "anonymous" method on this planner model to
531
- # avoid calling instance_eval during planning
532
- if body.arity > 0
533
- raise ArgumentError, "method body must accept zero arguments calls"
534
- end
535
- temp_method_name = "m#{@@temp_method_id += 1}"
536
- define_method(temp_method_name, &body)
537
- mdef = MethodDefinition.new(name, options, instance_method(temp_method_name))
538
- send("#{name}_methods")[method_id] = mdef
539
- end
540
- @@temp_method_id = 0
541
-
542
- # Returns an array of the names of all planning methods
543
- def self.planning_methods_names
544
- names = Set.new
545
- methods.each do |method_name|
546
- if method_name =~ /^each_(\w+)_method$/
547
- names << $1
548
- end
549
- end
550
-
551
- names
552
- end
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
-
581
- def self.clear_model
582
- planning_methods_names.each do |name|
583
- remove_planning_method(name)
584
- end
585
- end
586
-
587
- # Undefines all the definitions for the planning method +name+ on
588
- # this model. Definitions available on the parent are not removed
589
- def self.remove_planning_method(name)
590
- remove_method(name)
591
- remove_inherited_enumerable("#{name}_method", "#{name}_methods")
592
- if method_defined?("#{name}_filter")
593
- remove_inherited_enumerable("#{name}_filter", "#{name}_filters")
594
- end
595
- end
596
-
597
- def self.remove_inherited_enumerable(enum, attr = enum)
598
- if instance_variable_defined?("@#{attr}")
599
- remove_instance_variable("@#{attr}")
600
- end
601
- singleton_class.class_eval do
602
- remove_method("each_#{enum}")
603
- remove_method(attr)
604
- end
605
- end
606
-
607
- # Add a selection filter on the +name+ method. When developing the
608
- # +name+ method, the filter is called with the method options and
609
- # the MethodDefinition object, and should return +false+ if the
610
- # method is to be discarded, and +true+ otherwise
611
- #
612
- # Example
613
- # class MyPlanner < Planning::Planner
614
- # method(:m, :id => 1) do
615
- # raise
616
- # end
617
- #
618
- # method(:m, :id => 2) do
619
- # Roby::Task.new
620
- # end
621
- #
622
- # # the id == 1 version of m fails, remove it of the set
623
- # # of valid methods
624
- # filter(:m) do |opts, m|
625
- # m.id == 2
626
- # end
627
- # end
628
- #
629
- # This is mainly useful for external selection of methods (for
630
- # instance to implement some kind of dependency injection), or for
631
- # testing
632
- def self.filter(name, &filter)
633
- check_arity(filter, 2)
634
-
635
- if !respond_to?("#{name}_filters")
636
- inherited_enumerable("#{name}_filter", "#{name}_filters") { Array.new }
637
- class_eval <<-EOD
638
- class << self
639
- cached_enum("#{name}_filter", "#{name}_filters", false)
640
- end
641
- EOD
642
- end
643
- send("#{name}_filters") << filter
644
- end
645
-
646
- def self.each_method(name, id, &iterator)
647
- send("each_#{name}_method", id, &iterator)
648
- end
649
-
650
- # Find all methods that can be used to plan +[name, options]+. The selection is
651
- # done in two steps:
652
- # * we search all definition of +name+ that are compatible with +options. In this
653
- # stage, only the options listed in METHOD_SELECTION_OPTIONS are compared
654
- # * we call the method filters (if any) to remove unsuitable methods
655
- def self.find_methods(name, options = Hash.new)
656
- # validate the options hash, and split it into the options that are used for
657
- # method selection and the ones that are ignored here
658
- name, options = validate_method_query(name, options)
659
- method_selection = options.slice(*METHOD_SELECTION_OPTIONS)
660
-
661
- if method_id = method_selection[:id]
662
- method_selection[:id] = method_id = validate_method_id(method_id)
663
- result = send("enum_#{name}_methods", method_id).find { true }
664
- result = if result && result.options.merge(method_selection) == result.options
665
- [result]
666
- end
667
- else
668
- result = send("enum_#{name}_methods", nil).collect do |id, m|
669
- if m.options.merge(method_selection) == m.options
670
- m
671
- end
672
- end.compact
673
- end
674
-
675
- return nil if !result
676
-
677
- filter_method = "enum_#{name}_filters"
678
- if respond_to?(filter_method)
679
- # Remove results for which at least one filter returns false
680
- result.reject! { |m| send(filter_method).any? { |f| !f[options, m] } }
681
- end
682
-
683
- if result.empty?; nil
684
- else; result
685
- end
686
- end
687
-
688
- # If there is method definitions for +name+
689
- def has_method?(name); singleton_class.has_method?(name) end
690
- def self.has_method?(name); respond_to?("#{name}_methods") end
691
-
692
- # Returns the method model that should be considered when using
693
- # the result of the method +name+ with options +options+
694
- #
695
- # This model should be used for instance when adding a new
696
- # hierarchy relation between a parent and the result of
697
- # <tt>plan.#{name}(options)</tt>
698
- def self.model_of(name, options = {})
699
- model = if options[:id]
700
- enum_for("each_method", name, options[:id]).find { true }
701
- end
702
- model ||= method_model(name)
703
- model || default_method_model(name)
704
- end
705
-
706
- def self.default_method_model(name)
707
- MethodModel.new(name, :returns => Task)
708
- end
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
-
717
- # Creates a planning task which will call the same planning method
718
- # than the one currently being generated.
719
- #
720
- # +options+ is an option hash. These options are used to override
721
- # the current method options. Only one option is recognized by
722
- # +replan_task+:
723
- #
724
- # strict:: if true, we use the current method name and id for
725
- # the planning task. If false, use only the method name.
726
- # defaults to true.
727
- def replan_task(options = nil)
728
- method_options = arguments.dup
729
- if !options.has_key?(:strict) || options.delete(:strict)
730
- method_options.merge!(:id => @stack.last[1])
731
- end
732
-
733
- if options
734
- method_options.merge!(options)
735
- end
736
-
737
- Roby::PlanningTask.new :planner_model => self.class,
738
- :method_name => @stack.last[0],
739
- :method_options => method_options
740
- end
741
-
742
- def stop; @stop_required = true end
743
- def interruption_point; raise Interrupt, "interrupted planner" if @stop_required end
744
-
745
- # Find a suitable development for the +name+ method.
746
- def plan_method(name, options = Hash.new)
747
- if @stack.empty?
748
- @stop_required = false
749
- end
750
- interruption_point
751
-
752
- name = name.to_s
753
-
754
- planning_options, method_options =
755
- filter_options options, KNOWN_OPTIONS
756
-
757
- if method_options.empty?
758
- method_options = planning_options.delete(:args) || {}
759
- elsif planning_options[:args] && !planning_options[:args].empty?
760
- raise ArgumentError, "provided method-specific options through both :args and the option hash"
761
- end
762
-
763
- Planning.debug { "planning #{name}[#{method_options}]" }
764
-
765
- # Check for recursion
766
- if (options[:id] && @stack.include?([name, options[:id]])) || (!options[:id] && @stack.find { |n, _| n == name })
767
- options[:recursive] = true
768
- end
769
-
770
- # Get all valid methods. If no candidate are found, still try
771
- # to get a task to re-use
772
- methods = singleton_class.find_methods(name, options)
773
-
774
- # Check if we can reuse a task already in the plan
775
- if !options.has_key?(:reuse) || options[:reuse]
776
- all_returns = if methods
777
- methods.map { |m| m.returns if m.reuse? }
778
- else []
779
- end
780
- if (model = singleton_class.method_model(name)) && !options[:id]
781
- all_returns << model.returns if model.reuse?
782
- end
783
- all_returns.compact!
784
-
785
- for return_type in all_returns
786
- if task = find_reusable_task(return_type, method_options)
787
- return task
788
- end
789
- end
790
- end
791
-
792
- if !methods || methods.empty?
793
- raise NotFound.new(self, Hash.new)
794
- end
795
-
796
- # Call the methods
797
- call_planning_methods(Hash.new, method_options, *methods)
798
-
799
- rescue Interrupt
800
- raise
801
-
802
- rescue NotFound => e
803
- e.method_name = name
804
- e.method_options = options
805
- raise e.dup, e.message, caller(1)
806
- end
807
-
808
- def find_reusable_task(return_type, method_options)
809
- query = plan.find_tasks.
810
- which_fullfills(return_type, method_options).
811
- self_owned.
812
- not_abstract.
813
- not_finished.
814
- roots(TaskStructure::Hierarchy)
815
-
816
- for candidate in query
817
- Planning.debug { "selecting task #{candidate} instead of planning #{return_type}[#{method_options}]" }
818
- return candidate
819
- end
820
- nil
821
- end
822
-
823
- def arguments; @arguments.last end
824
- private :arguments
825
-
826
- # Tries to find a successfull development in the provided method list.
827
- #
828
- # It raises NotFound if none of the methods returned successfully
829
- def call_planning_methods(errors, method_options, method, *methods)
830
- begin
831
- @stack.push [method.name, method.id]
832
- @arguments.push(method_options)
833
- Planning.debug { "calling #{method.name}:#{method.id} with arguments #{arguments}" }
834
- begin
835
- result = method.call(self)
836
- rescue PlanModelError, Interrupt
837
- raise
838
- rescue Exception => e
839
- raise PlanModelError.new(self), e.message, e.backtrace
840
- end
841
-
842
- # Check that result is a task or a task collection
843
- unless result && (result.respond_to?(:to_task) || result.respond_to?(:each) || !result.respond_to?(:each_task))
844
- raise PlanModelError.new(self), "#{method} returned #{result}, which is neither a task nor a task collection"
845
- end
846
-
847
- # Insert resulting tasks in +plan+
848
- plan.add(result)
849
-
850
- expected_return = method.returns
851
- if expected_return
852
- if !result.respond_to?(:to_task) ||
853
- !result.fullfills?(expected_return, arguments.slice(*expected_return.arguments))
854
-
855
- if !result then result = "nil"
856
- elsif result.respond_to?(:each)
857
- result = result.map { |t| "#{t}(#{t.arguments})" }.join(", ")
858
- else result = "#{result}(#{result.arguments})"
859
- end
860
- raise PlanModelError.new(self), "#{method} returned #{result} which does not fullfill #{method.returns}(#{arguments})"
861
- end
862
- end
863
- Planning.debug { "found #{result}" }
864
-
865
- result
866
-
867
- ensure
868
- @arguments.pop
869
- @stack.pop
870
- end
871
-
872
- rescue PlanModelError => e
873
- e.planner = self unless e.planner
874
- errors[method] = e
875
- if methods.empty?
876
- raise NotFound.new(self, errors)
877
- else
878
- call_planning_methods(errors, method_options, *methods)
879
- end
880
- end
881
-
882
- private :call_planning_methods
883
-
884
- # Builds a loop in a plan (i.e. a method which is generated in
885
- # loop)
886
- def make_loop(options = {}, &block)
887
- raise ArgumentError, "no block given" unless block
888
-
889
- loop_id = Planner.next_id
890
- if !@stack.empty?
891
- loop_id = "#{@stack.last[1]}_#{loop_id}"
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)
898
- planning_options[:id] = loop_id
899
- planning_options[:reuse] = false
900
- loop_method.options.merge!(planning_options)
901
-
902
- options[:method_options] ||= {}
903
- options[:method_options].merge!(arguments || {})
904
- options[:method_options][:id] = loop_method.id
905
- PlanningLoop.new(options)
906
- end
907
- end
908
-
909
- # A planning Library is only a way to gather a set of planning
910
- # methods. It is created by
911
- # module MyLibrary
912
- # planning_library
913
- # method(:bla) do
914
- # end
915
- # end
916
- # or
917
- # my_library = Roby::Planning::Library.new do
918
- # method(:bla) do end
919
- # end
920
- #
921
- # It is then used by simply including the library in another library
922
- # or in a Planner class
923
- #
924
- # module AnotherLibrary
925
- # include MyLibrary
926
- # end
927
- #
928
- # class MyPlanner < Planner
929
- # include AnotherLibrary
930
- # end
931
- #
932
- # Alternatively, you can use Planner::use and Library::use, which search
933
- # for a Planning module in the given module. For instance
934
- #
935
- # module Namespace
936
- # module Planning
937
- # planning_library
938
- # [...]
939
- # end
940
- # end
941
- #
942
- # can be used with
943
- #
944
- # class MyPlanner < Planner
945
- # using Namespace
946
- # end
947
- #
948
- module Library
949
- include Tools
950
-
951
- attr_reader :default_options
952
-
953
- def planning_methods; @methods ||= Array.new end
954
- def method(name, options = Hash.new, &body)
955
- if body && default_options
956
- options = default_options.merge(options)
957
- end
958
- planning_methods << [name, options, body]
959
- end
960
-
961
- def self.clear_model
962
- planning_methods.clear
963
- end
964
-
965
- # Cannot use included here because included() is called *after* the module
966
- # has been included
967
- def append_features(klass)
968
- new_libraries = ancestors.enum_for.
969
- reject { |mod| klass < mod }.
970
- find_all { |mod| mod.respond_to?(:planning_methods) }
971
-
972
- super
973
-
974
- unless klass < Planner
975
- if Class === klass
976
- Roby.debug "including a planning library in a class which is not a Planner, which is useless"
977
- else
978
- klass.extend Library
979
- end
980
- return
981
- end
982
-
983
- new_libraries.reverse_each do |mod|
984
- mod.planning_methods.each do |name, options, body|
985
- begin
986
- klass.method(name, options, &body)
987
- rescue ArgumentError => e
988
- raise ArgumentError, "cannot include the #{self} library in #{klass}: when inserting #{name}#{options}, #{e.message}", caller(0)
989
- end
990
- end
991
- end
992
- end
993
-
994
- def self.new(&block)
995
- Module.new do
996
- extend Library
997
- class_eval(&block)
998
- end
999
- end
1000
- end
1001
-
1002
- end
1003
- end
1004
-
1005
-
1006
- class Module
1007
- def planning_library(default_options = Hash.new)
1008
- extend Roby::Planning::Library
1009
- instance_variable_set(:@default_options, default_options)
1010
- end
1011
- end
1012
-