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
@@ -0,0 +1,24 @@
1
+ module Roby
2
+ module Interface
3
+ # Representation of a subcommand on {Interface} on the shell side
4
+ class ShellSubcommand < SubcommandClient
5
+ def call(options, path, m, *args)
6
+ parent.call(options, [name] + path, m, *args)
7
+ end
8
+
9
+ def method_missing(m, *args)
10
+ parent.call(Hash.new, [name], m, *args)
11
+ rescue NoMethodError => e
12
+ if e.message =~ /undefined method .#{m}./
13
+ puts "invalid command name #{m}, call 'help #{path.join(".")}' for more information"
14
+ else raise
15
+ end
16
+ rescue ArgumentError => e
17
+ if e.message =~ /wrong number of arguments/ && e.backtrace.first =~ /#{m.to_s}/
18
+ puts e.message
19
+ else raise
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ module Roby
2
+ module Interface
3
+ # Representation of a subcommand on {Interface} on the shell side
4
+ class SubcommandClient
5
+ # @return [ShellClient,ShellSubcommand] the parent shell /
6
+ # subcommand
7
+ attr_reader :parent
8
+ # @return [
9
+ # @return [String] the subcommand name
10
+ attr_reader :name
11
+ # @return [String] the subcommand description
12
+ attr_reader :description
13
+ # @return [String] the set of commands on this subcommand
14
+ attr_reader :commands
15
+
16
+ def initialize(parent, name, description, commands)
17
+ @parent, @name, @description, @commands =
18
+ parent, name, description, commands
19
+ end
20
+
21
+ def call(path, m, *args)
22
+ parent.call([name] + path, m, *args)
23
+ end
24
+
25
+ def path
26
+ parent.path + [name]
27
+ end
28
+
29
+ def method_missing(m, *args)
30
+ parent.call([name], m, *args)
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,168 @@
1
+ require 'socket'
2
+
3
+ module Roby
4
+ module Interface
5
+ # An object that publishes a Roby interface using a TCP server
6
+ class TCPServer
7
+ # @return [Interface] the interface object we give access to
8
+ attr_reader :interface
9
+ # @return [::TCPServer] the TCP server we are accepting from
10
+ attr_reader :server
11
+ # @return [Array<Client>] set of currently active clients
12
+ attr_reader :clients
13
+ # Whether the server handler should warn about disconnections
14
+ attr_predicate :warn_about_disconnection?, true
15
+ # Whether a non-comm-related failure will cause the whole Roby app
16
+ # to quit
17
+ attr_predicate :abort_on_exception?, true
18
+
19
+ # @return [String] the address this interface is bound to
20
+ def ip_address
21
+ server.local_address.ip_address
22
+ end
23
+
24
+ # @return [Integer] the port on which this interface runs
25
+ def ip_port
26
+ server.local_address.ip_port
27
+ end
28
+
29
+ # Creates a new interface server on the given port
30
+ #
31
+ # @param [Integer] port
32
+ def initialize(app, host: nil, port: Roby::Interface::DEFAULT_PORT)
33
+ @app = app
34
+ @interface = Interface.new(app)
35
+ @server =
36
+ begin ::TCPServer.new(host, port)
37
+ rescue TypeError
38
+ raise Errno::EADDRINUSE, "#{port} already in use"
39
+ end
40
+ @clients = Array.new
41
+ @abort_on_exception = true
42
+ @accept_executor = Concurrent::CachedThreadPool.new
43
+ @accept_future = queue_accept_future
44
+ @propagation_handler_id = interface.execution_engine.
45
+ add_propagation_handler(
46
+ description: 'TCPServer#process_pending_requests',
47
+ on_error: :ignore) do
48
+ process_pending_requests
49
+ end
50
+ @warn_about_disconnection = false
51
+ end
52
+
53
+ def queue_accept_future
54
+ Concurrent::Future.execute(executor: @accept_executor) do
55
+ socket = @server.accept
56
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
57
+ socket
58
+ end
59
+ end
60
+
61
+ # Returns the port this server is bound to
62
+ #
63
+ # @return [Integer]
64
+ def port
65
+ Roby.warn_deprecated "Interface::TCPServer#port is deprecated in favor "\
66
+ "of #ip_port to match ruby's Addrinfo API"
67
+ ip_port
68
+ end
69
+
70
+ # Creates a server object that will manage the replies on a
71
+ # particular TCP socket
72
+ #
73
+ # @return [Server]
74
+ def create_server(socket)
75
+ Server.new(DRobyChannel.new(socket, false), interface)
76
+ end
77
+
78
+ # Process all incoming connection requests
79
+ #
80
+ # The new clients are added into the Roby event loop
81
+ def process_pending_requests
82
+ if @accept_future.rejected?
83
+ raise @accept_future.reason
84
+ elsif @accept_future.fulfilled?
85
+ clients << create_server(@accept_future.value)
86
+ @accept_future = queue_accept_future
87
+ end
88
+
89
+ exceptions = []
90
+ clients.delete_if do |client|
91
+ begin
92
+ client.poll
93
+ rescue Exception => e
94
+ client.close
95
+
96
+ if warn_about_disconnection?
97
+ Roby::Interface.warn "disconnecting from #{client.client_id}"
98
+ end
99
+
100
+ next(true) if e.kind_of?(ComError)
101
+
102
+ if abort_on_exception?
103
+ exceptions << e
104
+ else
105
+ Roby.log_exception_with_backtrace(e, Roby::Interface, :warn)
106
+ end
107
+ true
108
+ end
109
+ end
110
+
111
+ raise exceptions.first unless exceptions.empty?
112
+ rescue Exception => e
113
+ if abort_on_exception?
114
+ @app.execution_engine.
115
+ add_framework_error(e, "Interface::TCPServer")
116
+ else
117
+ Roby.log_exception_with_backtrace(e, Roby, :warn)
118
+ end
119
+ end
120
+
121
+ # Closes this server
122
+ def close
123
+ clients.each do |c|
124
+ unless c.closed?
125
+ c.close
126
+ end
127
+ end
128
+ clients.clear
129
+ if server && !server.closed?
130
+ server.close
131
+ end
132
+ @accept_executor.shutdown
133
+ interface.execution_engine.
134
+ remove_propagation_handler(@propagation_handler_id)
135
+ end
136
+
137
+ # Whether the given client is handled by this server
138
+ def has_client?(client)
139
+ @clients.include?(client)
140
+ end
141
+ end
142
+
143
+ # Connect to a Roby controller interface at this host and port
144
+ #
145
+ # @return [Client] the client object that gives access
146
+ def self.connect_with_tcp_to(host, port = DEFAULT_PORT,
147
+ marshaller: DRoby::Marshal.new(auto_create_plans: true))
148
+ require 'socket'
149
+ socket = TCPSocket.new(host, port)
150
+ addr = socket.addr(true)
151
+ Client.new(DRobyChannel.new(socket, true,
152
+ marshaller: DRoby::Marshal.new(auto_create_plans: true)),
153
+ "#{addr[2]}:#{addr[1]}")
154
+
155
+ rescue Errno::ECONNREFUSED => e
156
+ raise ConnectionError, "failed to connect to #{host}:#{port}: #{e.message}",
157
+ e.backtrace
158
+ rescue SocketError => e
159
+ raise e, "cannot connect to host '#{host}' port '#{port}': #{e.message}",
160
+ e.backtrace
161
+ rescue ::Exception
162
+ if socket && !socket.closed?
163
+ socket.close
164
+ end
165
+ raise
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,112 @@
1
+ module Roby
2
+ module Models
3
+ # Support for argument handling in the relevant models (task services
4
+ # and tasks)
5
+ module Arguments
6
+ extend MetaRuby::Attributes
7
+
8
+ # Representation of one argument
9
+ Argument = Struct.new :name, :default, :doc do
10
+ # Tests whether this argument has a default
11
+ def has_default?
12
+ default != NO_DEFAULT_ARGUMENT
13
+ end
14
+
15
+ # Tests whether this argument has a delayed argument as default
16
+ def has_delayed_default?
17
+ has_default? && TaskArguments.delayed_argument?(default)
18
+ end
19
+ end
20
+
21
+ # The set of knwon argument names
22
+ #
23
+ # @return [Set<Symbol>]
24
+ inherited_attribute("argument", "__arguments", map: true) { Hash.new }
25
+
26
+ # @return [Array<String>] the list of arguments required by this task model
27
+ def arguments
28
+ each_argument.map { |name, _| name }
29
+ end
30
+
31
+ # The null object used in {#argument} to signify that there are no
32
+ # default arguments
33
+ #
34
+ # nil cannot be used as 'nil' is a valid default as well
35
+ NO_DEFAULT_ARGUMENT = Object.new
36
+ def NO_DEFAULT_ARGUMENT.evaluate_delayed_argument
37
+ raise NotImplementedError, "trying to evaluate Roby::Models::Task::NO_DEFAULT_ARGUMENT which is an internal null object"
38
+ end
39
+ NO_DEFAULT_ARGUMENT.freeze
40
+
41
+ # @overload argument(argument_name, options)
42
+ # @param [String] argument_name the name of the new argument
43
+ # @param [Hash] options
44
+ # @param default the default value for this argument. It
45
+ # can either be a plain value (e.g. a number) or one of the
46
+ # delayed arguments (see examples below)
47
+ # @param doc documentation string for the argument. If left
48
+ # to nil, the method will attempt to extract the argument's
49
+ # documentation block.
50
+ #
51
+ # @example getting an argument at runtime from another object
52
+ # argument :target_point, default: from(:planned_task).target_point
53
+ # @example getting an argument at runtime from the global configuration
54
+ # argument :target_point, default: from_conf.target_position
55
+ # @example defining 'nil' as a default value
56
+ # argument :main_direction, default: nil
57
+ def argument(name, default: NO_DEFAULT_ARGUMENT, doc: nil)
58
+ name = name.to_sym
59
+ if !TaskArguments.delayed_argument?(default)
60
+ default = DefaultArgument.new(default)
61
+ end
62
+ doc ||= MetaRuby::DSLs.parse_documentation_block /\.rb$/, 'argument'
63
+ __arguments[name] = Argument.new(name, default, doc)
64
+
65
+ if name =~ /^\w+$/ && !method_defined?(name)
66
+ define_method(name) { arguments[name] }
67
+ define_method("#{name}=") { |value| arguments[name] = value }
68
+ end
69
+ end
70
+
71
+ # Access an argument's default value
72
+ #
73
+ # @param [String] argname the argument name
74
+ # @return [(Boolean,Object)] the first returned value determines
75
+ # whether there is a default defined for the requested argument and
76
+ # the second is that value. Note that the default value can be nil.
77
+ def default_argument(argname)
78
+ if (arg = find_argument(argname)) && arg.has_default?
79
+ return true, arg.default
80
+ end
81
+ end
82
+
83
+ # The part of +arguments+ that is meaningful for this task model
84
+ def meaningful_arguments(arguments)
85
+ self_arguments = self.arguments
86
+ result = Hash.new
87
+ arguments.each_assigned_argument do |key, value|
88
+ if self_arguments.include?(key)
89
+ result[key] = value
90
+ end
91
+ end
92
+ result
93
+ end
94
+
95
+ # Checks if this model fullfills everything in +models+
96
+ def fullfills?(models)
97
+ if !models.respond_to?(:each)
98
+ models = [models]
99
+ end
100
+
101
+ for tag in models
102
+ if !has_ancestor?(tag)
103
+ return false
104
+ end
105
+ end
106
+ true
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+
@@ -0,0 +1,83 @@
1
+ module Roby
2
+ module Models
3
+ module PlanObject
4
+ include MetaRuby::ModelAsClass
5
+ extend MetaRuby::Attributes
6
+ include Transaction::Proxying::Cache
7
+
8
+ # @return [Array<UnboundMethod>] set of finalization handlers
9
+ # defined at the model level
10
+ # @see PlanObject.when_finalized
11
+ inherited_attribute(:finalization_handler, :finalization_handlers) { Array.new }
12
+
13
+ # Adds a model-level finalization handler, i.e. a handler that will be
14
+ # called on every instance of the class
15
+ #
16
+ # The block is called in the context of the task that got finalized
17
+ # (i.e. in the block, self is this task)
18
+ #
19
+ # @return [void]
20
+ def when_finalized(&block)
21
+ method_name = "finalization_handler_#{block.object_id}"
22
+ define_method(method_name, &block)
23
+ finalization_handlers << instance_method(method_name)
24
+ end
25
+
26
+ # If true, the backtrace at which a plan object is finalized is
27
+ # stored in this object's {PlanObject#removed_at} attribute.
28
+ #
29
+ # It defaults to false
30
+ #
31
+ # @see PlanObject#finalized!
32
+ attr_predicate :debug_finalization_place?, true
33
+
34
+ # This class method sets up the enclosing class as a child object,
35
+ # with the root object being returned by the given attribute.
36
+ # Task event generators are for instance defined by
37
+ #
38
+ # class TaskEventGenerator < EventGenerator
39
+ # # The task this generator belongs to
40
+ # attr_reader :task
41
+ #
42
+ # child_plan_object :task
43
+ # end
44
+ def child_plan_object(attribute)
45
+ class_eval <<-EOD, __FILE__, __LINE__+1
46
+ def root_object; #{attribute} end
47
+ def root_object?; false end
48
+ def owners; #{attribute}.owners end
49
+ def distribute?; #{attribute}.distribute? end
50
+ def plan; #{attribute}.plan end
51
+ def executable?; #{attribute}.executable? end
52
+
53
+ def subscribed?; #{attribute}.subscribed? end
54
+ def updated?; #{attribute}.updated? end
55
+ def updated_by?(peer); #{attribute}.updated_by?(peer) end
56
+ def update_on?(peer); #{attribute}.update_on?(peer) end
57
+ def updated_peers; #{attribute}.updated_peers end
58
+ def remotely_useful?; #{attribute}.remotely_useful? end
59
+
60
+ def forget_peer(peer)
61
+ remove_sibling_for(peer)
62
+ end
63
+ def sibling_of(remote_object, peer)
64
+ if !distribute?
65
+ raise ArgumentError, "#{self} is local only"
66
+ end
67
+
68
+ add_sibling_for(peer, remote_object)
69
+ end
70
+
71
+ private :plan=
72
+ private :executable=
73
+ EOD
74
+ end
75
+
76
+ # Create a {Queries::PlanObjectMatcher}
77
+ def match
78
+ Queries::PlanObjectMatcher.new.with_model(self)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,835 @@
1
+ module Roby
2
+ module Models
3
+ module Task
4
+ include MetaRuby::ModelAsClass
5
+ include Models::Arguments
6
+
7
+ # Proxy class used as intermediate by Task.with_arguments
8
+ class AsPlanProxy
9
+ def initialize(model, arguments)
10
+ @model, @arguments = model, arguments
11
+ end
12
+
13
+ def as_plan
14
+ @model.as_plan(@arguments)
15
+ end
16
+ end
17
+
18
+ class Template < TemplatePlan
19
+ attr_reader :events_by_name
20
+ attr_accessor :success_events
21
+ attr_accessor :failure_events
22
+ attr_accessor :terminal_events
23
+
24
+ def initialize
25
+ super
26
+ @events_by_name = Hash.new
27
+ end
28
+ end
29
+
30
+ class TemplateEventGenerator < EventGenerator
31
+ def initialize(controlable, event_model, plan: Template.new)
32
+ super(controlable, plan: plan)
33
+ @event_model = event_model
34
+ end
35
+ def model
36
+ @event_model
37
+ end
38
+ end
39
+
40
+ def invalidate_template
41
+ @template = nil
42
+ end
43
+
44
+ # The plan that is used to instantiate this task model
45
+ def template
46
+ return @template if @template
47
+
48
+ template = Template.new
49
+ each_event do |event_name, event_model|
50
+ template.add(event = TemplateEventGenerator.new(event_model.controlable?, event_model, plan: template))
51
+ template.events_by_name[event_name] = event
52
+ end
53
+
54
+ instantiate_event_relations(template)
55
+ @template = template
56
+ end
57
+
58
+ def instantiate_event_relations(template)
59
+ events = template.events_by_name
60
+
61
+ all_signals.each do |generator, signalled_events|
62
+ next if signalled_events.empty?
63
+ generator = events[generator]
64
+
65
+ for signalled in signalled_events
66
+ signalled = events[signalled]
67
+ generator.signals signalled
68
+ end
69
+ end
70
+
71
+ all_forwardings.each do |generator, signalled_events|
72
+ next if signalled_events.empty?
73
+ generator = events[generator]
74
+
75
+ for signalled in signalled_events
76
+ signalled = events[signalled]
77
+ generator.forward_to signalled
78
+ end
79
+ end
80
+
81
+ all_causal_links.each do |generator, signalled_events|
82
+ next if signalled_events.empty?
83
+ generator = events[generator]
84
+
85
+ for signalled in signalled_events
86
+ signalled = events[signalled]
87
+ generator.add_causal_link signalled
88
+ end
89
+ end
90
+
91
+ # Add a link from internal_event to stop if stop is controllable
92
+ if events[:stop].controlable?
93
+ events[:internal_error].signals events[:stop]
94
+ end
95
+
96
+ terminal_events, success_events, failure_events =
97
+ compute_terminal_events(events)
98
+
99
+ template.terminal_events = terminal_events
100
+ template.success_events = success_events
101
+ template.failure_events = failure_events
102
+ start_event = events[:start]
103
+
104
+ # WARN: the start event CAN be terminal: it can be a signal from
105
+ # :start to a terminal event
106
+ #
107
+ # Create the precedence relations between 'normal' events and the terminal events
108
+ root_terminal_events = terminal_events.find_all do |ev|
109
+ (ev != start_event) && ev.root?(Roby::EventStructure::Precedence)
110
+ end
111
+
112
+ events.each_value do |ev|
113
+ next if ev == start_event
114
+ if !terminal_events.include?(ev)
115
+ if ev.root?(Roby::EventStructure::Precedence)
116
+ start_event.add_precedence(ev)
117
+ end
118
+ if ev.leaf?(Roby::EventStructure::Precedence)
119
+ for terminal in root_terminal_events
120
+ ev.add_precedence(terminal)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ def discover_terminal_events(events, terminal_set, set, root)
128
+ stack = [root]
129
+ while !stack.empty?
130
+ vertex = stack.shift
131
+ for relation in [EventStructure::Signal, EventStructure::Forwarding]
132
+ for parent in vertex.parent_objects(relation)
133
+ if !events.include?(parent)
134
+ next
135
+ elsif parent[vertex, relation]
136
+ next
137
+ elsif !terminal_set.include?(parent)
138
+ terminal_set << parent
139
+ set << parent if set
140
+ stack << parent
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ def compute_terminal_events(events)
148
+ success_events, failure_events, terminal_events =
149
+ [events[:success]].to_set,
150
+ [events[:failed]].to_set,
151
+ [events[:stop], events[:success], events[:failed]].to_set
152
+
153
+ event_set = events.values.to_set
154
+ discover_terminal_events(event_set, terminal_events, success_events, events[:success])
155
+ discover_terminal_events(event_set, terminal_events, failure_events, events[:failed])
156
+ discover_terminal_events(event_set, terminal_events, nil, events[:stop])
157
+
158
+ events.each_value do |ev|
159
+ if ev.event_model.terminal?
160
+ if !success_events.include?(ev) && !failure_events.include?(ev)
161
+ terminal_events << ev
162
+ end
163
+ end
164
+ end
165
+
166
+ return terminal_events, success_events, failure_events
167
+ end
168
+
169
+ # If this class model has an 'as_plan', this specifies what arguments
170
+ # should be passed to as_plan
171
+ def with_arguments(arguments = Hash.new)
172
+ if respond_to?(:as_plan)
173
+ AsPlanProxy.new(self, arguments)
174
+ else
175
+ raise NoMethodError, "#with_arguments is invalid on #self, as #self does not have an #as_plan method"
176
+ end
177
+ end
178
+
179
+ # Default implementation of the #as_plan method
180
+ #
181
+ # The #as_plan method is used to use task models as representation of
182
+ # abstract actions. For instance, if an #as_plan method is available on
183
+ # a particular MoveTo task model, one can do
184
+ #
185
+ # root.depends_on(MoveTo)
186
+ #
187
+ # This default implementation looks for planning methods declared in the
188
+ # main Roby application planners that return the required task type or
189
+ # one of its subclasses. If one is found, it is using it to generate the
190
+ # action. Otherwise, it falls back to returning a new instance of this
191
+ # task model, unless the model is abstract in which case it raises
192
+ # ArgumentError.
193
+ #
194
+ # It can be used with
195
+ #
196
+ # class TaskModel < Roby::Task
197
+ # end
198
+ #
199
+ # root = Roby::Task.new
200
+ # child = root.depends_on(TaskModel)
201
+ #
202
+ # If arguments need to be given, the #with_arguments method should be
203
+ # used:
204
+ #
205
+ # root = Roby::Task.new
206
+ # child = root.depends_on(TaskModel.with_arguments(id: 200))
207
+ #
208
+ def as_plan(arguments = Hash.new)
209
+ Roby.app.prepare_action(self, **arguments).first
210
+ rescue Application::ActionResolutionError
211
+ if abstract?
212
+ raise Application::ActionResolutionError, "#{self} is abstract and no planning method exists that returns it"
213
+ else
214
+ new(arguments)
215
+ end
216
+ end
217
+
218
+ # @deprecated
219
+ #
220
+ # Use #each_submodel instead
221
+ def all_models
222
+ submodels
223
+ end
224
+
225
+ # Clears all definitions saved in this model. This is to be used by the
226
+ # reloading code
227
+ def clear_model
228
+ class_eval do
229
+ # Remove event models
230
+ events.each_key do |ev_symbol|
231
+ remove_const ev_symbol.to_s.camelcase(:upper)
232
+ end
233
+
234
+ [@events, @signal_sets, @forwarding_sets, @causal_link_sets,
235
+ @arguments, @handler_sets, @precondition_sets].each do |set|
236
+ set.clear if set
237
+ end
238
+ end
239
+ super
240
+ end
241
+
242
+ # Declares an attribute set which follows the task models inheritance
243
+ # hierarchy. Define the corresponding enumeration methods as well.
244
+ #
245
+ # For instance,
246
+ # model_attribute_list 'signal'
247
+ #
248
+ # defines the model-level signals, which can be accessed through
249
+ # .each_signal(model)
250
+ # .signals(model)
251
+ # #each_signal(model)
252
+ #
253
+ def self.model_attribute_list(name) # :nodoc:
254
+ class_eval <<-EOD, __FILE__, __LINE__+1
255
+ inherited_attribute("#{name}_set", "#{name}_sets", map: true) { Hash.new { |h, k| h[k] = Set.new } }
256
+ def each_#{name}(model)
257
+ for obj in #{name}s(model)
258
+ yield(obj)
259
+ end
260
+ self
261
+ end
262
+ def #{name}s(model)
263
+ result = Set.new
264
+ each_#{name}_set(model, false) do |set|
265
+ result.merge set
266
+ end
267
+ result
268
+ end
269
+
270
+ def all_#{name}s
271
+ if @all_#{name}s
272
+ @all_#{name}s
273
+ else
274
+ result = Hash.new
275
+ each_#{name}_set do |from, targets|
276
+ result[from] ||= Set.new
277
+ result[from].merge(targets)
278
+ end
279
+ @all_#{name}s = result
280
+ end
281
+ end
282
+ EOD
283
+ end
284
+
285
+ def self.model_relation(name)
286
+ model_attribute_list(name)
287
+ end
288
+
289
+
290
+ # @!group Event Relations
291
+
292
+ # The set of signals that are registered on this task model
293
+ model_relation 'signal'
294
+
295
+ # The set of forwardings that are registered on this task model
296
+ model_relation('forwarding')
297
+
298
+ # The set of causal links that are registered on this task model
299
+ model_relation('causal_link')
300
+
301
+ # The set of event handlers that are registered on this task model
302
+ model_attribute_list('handler')
303
+
304
+ # The set of precondition handlers that are registered on this task model
305
+ model_attribute_list('precondition')
306
+
307
+ # Establish model-level signals between events of that task. These
308
+ # signals will be established on all the instances of this task model
309
+ # (and its subclasses).
310
+ #
311
+ # Signals cause the target event(s) command to be called when the
312
+ # source event is emitted.
313
+ #
314
+ # @param [Hash<Symbol,Array<Symbol>>,Hash<Symbol,Symbol>] mappings the source-to-target mappings
315
+ # @raise [ArgumentError] if the target event is not controlable,
316
+ # i.e. not have a command
317
+ #
318
+ # @example when establishing multiple relations from the same source use name-to-arrays
319
+ # signal start: [:one, :two]
320
+ def signal(mappings)
321
+ mappings.each do |from, to|
322
+ from = event_model(from)
323
+ targets = Array[*to].map { |ev| event_model(ev) }
324
+
325
+ if from.terminal?
326
+ non_terminal = targets.find_all { |ev| !ev.terminal? }
327
+ if !non_terminal.empty?
328
+ raise ArgumentError, "trying to establish a signal from the terminal event #{from} to the non-terminal events #{non_terminal}"
329
+ end
330
+ end
331
+ non_controlable = targets.find_all { |ev| !ev.controlable? }
332
+ if !non_controlable.empty?
333
+ raise ArgumentError, "trying to signal #{non_controlable.join(" ")} which is/are not controlable"
334
+ end
335
+
336
+ signal_sets[from.symbol].merge targets.map { |ev| ev.symbol }
337
+ end
338
+ update_terminal_flag
339
+ end
340
+
341
+ # Establish model-level causal links between events of that task. These
342
+ # signals will be established on all the instances of this task
343
+ # model (and its subclasses).
344
+ #
345
+ # Causal links are used during event propagation to order the
346
+ # propagation properly. Establish a causal link when e.g. an event
347
+ # handler might call or emit on another of this task's event
348
+ #
349
+ # @param [Hash<Symbol,Array<Symbol>>,Hash<Symbol,Symbol>] mappings the source-to-target mappings
350
+ #
351
+ # @example when establishing multiple relations from the same source use name-to-arrays
352
+ # signal start: [:one, :two]
353
+ def causal_link(mappings)
354
+ mappings.each do |from, to|
355
+ from = event_model(from).symbol
356
+ causal_link_sets[from].merge Array[*to].map { |ev| event_model(ev).symbol }
357
+ end
358
+ update_terminal_flag
359
+ end
360
+
361
+ # Establish model-level forwarding between events of that task.
362
+ # These relations will be established on all the instances of this
363
+ # task model (and its subclasses).
364
+ #
365
+ # Forwarding is used to cause the target event to be emitted when
366
+ # the source event is.
367
+ #
368
+ # @param [Hash<Symbol,Array<Symbol>>,Hash<Symbol,Symbol>] mappings the source-to-target mappings
369
+ # @example
370
+ # # A task that is stopped as soon as it is started
371
+ # class MyTask < Roby::Task
372
+ # forward start: :stop
373
+ # end
374
+ #
375
+ # @see Task#forward
376
+ # @see EventGenerator#forward.
377
+ # @see Roby::EventStructure::Forward the forwarding relation.
378
+ def forward(mappings)
379
+ mappings.each do |from, to|
380
+ from = event_model(from).symbol
381
+ targets = Array[*to].map { |ev| event_model(ev).symbol }
382
+
383
+ if event_model(from).terminal?
384
+ non_terminal = targets.find_all { |name| !event_model(name).terminal? }
385
+ if !non_terminal.empty?
386
+ raise ArgumentError, "trying to establish a forwarding relation from the terminal event #{from} to the non-terminal event(s) #{targets}"
387
+ end
388
+ end
389
+
390
+ forwarding_sets[from].merge targets
391
+ end
392
+ update_terminal_flag
393
+ end
394
+
395
+ # @!endgroup
396
+
397
+ # Helper method to define delayed arguments from related objects
398
+ #
399
+ # @example propagate an argument from a parent task
400
+ # argument :target, default: from(:parent).target
401
+ def from(object)
402
+ if object.kind_of?(Symbol)
403
+ Roby.from(nil).send(object)
404
+ else
405
+ Roby.from(object)
406
+ end
407
+ end
408
+
409
+ # Helper method to define delayed arguments from the State object
410
+ #
411
+ # @example get an argument from the State object
412
+ # argument :initial_pose, default: from_state.pose
413
+ def from_state(state_object = State)
414
+ Roby.from_state(state_object)
415
+ end
416
+
417
+ # Declare that tasks of this model can finish by simply emitting
418
+ # stop, i.e. with no specific action.
419
+ #
420
+ # @example
421
+ # class MyTask < Roby::Task
422
+ # terminates
423
+ # end
424
+ #
425
+ def terminates
426
+ event :failed, command: true, terminal: true
427
+ interruptible
428
+ end
429
+
430
+ # Declare that tasks of this model can be interrupted by calling the
431
+ # command of {Roby::Task#failed_event}
432
+ #
433
+ # @raise [ArgumentError] if {Roby::Task#failed_event} is not controlable.
434
+ def interruptible
435
+ if !has_event?(:failed) || !event_model(:failed).controlable?
436
+ raise ArgumentError, "failed is not controlable"
437
+ end
438
+
439
+ event(:stop) do |context|
440
+ if starting?
441
+ start_event.signals stop_event
442
+ return
443
+ end
444
+ failed!(context)
445
+ end
446
+ end
447
+
448
+ # True if this task is an abstract task
449
+ #
450
+ # @see abstract
451
+ attr_predicate :abstract?, true
452
+
453
+ # Declare that this task model defines abstract tasks. Abstract
454
+ # tasks can be used to represent an action, without specifically
455
+ # representing how this action should be done.
456
+ #
457
+ # Instances of abstract task models are not executable, i.e. they
458
+ # cannot be started.
459
+ #
460
+ # @see abstract? executable?
461
+ def abstract
462
+ @abstract = true
463
+ end
464
+
465
+ # @api private
466
+ #
467
+ # Update the terminal flag for the event models that are defined in
468
+ # this task model. The event is terminal if model-level signals
469
+ # ({#signal}) or forwards ({#forward}) lead to the
470
+ # emission of {#stop_event}
471
+ def update_terminal_flag # :nodoc:
472
+ events = each_event.map { |name, _| name }
473
+ terminal_events = [:stop]
474
+ events.delete(:stop)
475
+
476
+ loop do
477
+ old_size = terminal_events.size
478
+ events.delete_if do |ev|
479
+ if signals(ev).any? { |sig_ev| terminal_events.include?(sig_ev) } ||
480
+ forwardings(ev).any? { |sig_ev| terminal_events.include?(sig_ev) }
481
+ terminal_events << ev
482
+ true
483
+ end
484
+ end
485
+ break if old_size == terminal_events.size
486
+ end
487
+
488
+ terminal_events.each do |sym|
489
+ if ev = self.events[sym]
490
+ ev.terminal = true
491
+ else
492
+ ev = superclass.event_model(sym)
493
+ unless ev.terminal?
494
+ event sym, model: ev, terminal: true,
495
+ command: (ev.method(:call) rescue nil)
496
+ end
497
+ end
498
+ end
499
+ end
500
+
501
+ # Defines a new event on this task.
502
+ #
503
+ # @param [Symbol] event_name the event name
504
+ # @param [Hash] options an option hash
505
+ # @option options [Boolean] :controllable if true, the event is
506
+ # controllable and will use the default command of emitting directly
507
+ # in the command
508
+ # @option options [Boolean] :terminal if true, the event is marked as
509
+ # terminal, i.e. it will terminate the task upon emission. Giving this
510
+ # flag is required to redeclare an existing terminal event in a
511
+ # subclass. Otherwise, it is determined automatically by checking
512
+ # whether the event is forwarded to :stop
513
+ # @option options [Class] :model the base class used to create the
514
+ # model for this event. This class is going to be used to generate the
515
+ # event. Defaults to TaskEvent.
516
+ #
517
+ # When a task event (for instance +start+) is emitted, a Roby::TaskEvent
518
+ # object is created to describe the information related to this
519
+ # emission (time, sources, context information, ...). Task.event
520
+ # defines a specific event model MyTask::MyEvent for each task event
521
+ # with name :my_event. This specific model is by default a subclass of
522
+ # Roby::TaskEvent, but it is possible to override that by using the +model+
523
+ # option.
524
+ def event(event_name, options = Hash.new, &block)
525
+ event_name = event_name.to_sym
526
+
527
+ options = validate_options options,
528
+ controlable: nil, command: nil, terminal: nil,
529
+ model: find_event_model(event_name) || Roby::TaskEvent
530
+
531
+ if options.has_key?(:controlable)
532
+ options[:command] = options[:controlable]
533
+ elsif !options.has_key?(:command) && block
534
+ options[:command] = define_command_method(event_name, block)
535
+ end
536
+ validate_event_definition_request(event_name, options)
537
+
538
+ # Define the event class
539
+ new_event = options[:model].new_submodel task_model: self,
540
+ terminal: options[:terminal],
541
+ symbol: event_name, command: options[:command]
542
+ new_event.permanent_model = self.permanent_model?
543
+
544
+ setup_terminal_handler = false
545
+ old_model = find_event_model(event_name)
546
+ if new_event.symbol != :stop && options[:terminal] && (!old_model || !old_model.terminal?)
547
+ setup_terminal_handler = true
548
+ end
549
+
550
+ events[new_event.symbol] = new_event
551
+ if setup_terminal_handler
552
+ forward(new_event => :stop)
553
+ end
554
+ const_set(event_name.to_s.camelcase(:upper), new_event)
555
+
556
+ define_event_methods(event_name)
557
+ new_event
558
+ end
559
+
560
+ # @api private
561
+ #
562
+ # Define the method that will be used as command for the given event
563
+ #
564
+ # @param [Symbol] event_name the event name
565
+ def define_command_method(event_name, block)
566
+ check_arity(block, 1, strict: true)
567
+ define_method("event_command_#{event_name}", &block)
568
+ method = instance_method("event_command_#{event_name}")
569
+ lambda do |dst_task, *event_context|
570
+ method.bind(dst_task).call(*event_context)
571
+ end
572
+ end
573
+
574
+ # @api private
575
+ #
576
+ # Define support methods for a task event
577
+ #
578
+ # @param [Symbol] event_name the event name
579
+ def define_event_methods(event_name)
580
+ event_name = event_name.to_sym
581
+ if !method_defined?("#{event_name}_event")
582
+ define_method("#{event_name}_event") do
583
+ @bound_events[event_name] || event(event_name)
584
+ end
585
+ end
586
+ if !method_defined?("#{event_name}?")
587
+ define_method("#{event_name}?") do
588
+ (@bound_events[event_name] || event(event_name)).emitted?
589
+ end
590
+ end
591
+ if !method_defined?("#{event_name}!")
592
+ define_method("#{event_name}!") do |*context|
593
+ (@bound_events[event_name] || event(event_name)).call(*context)
594
+ end
595
+ end
596
+ if !respond_to?("#{event_name}_event")
597
+ singleton_class.class_eval do
598
+ define_method("#{event_name}_event") do
599
+ find_event_model(event_name)
600
+ end
601
+ end
602
+ end
603
+ end
604
+
605
+ # @api private
606
+ #
607
+ # Validate the parameters passed to {#event}
608
+ #
609
+ # @raise [ArgumentError] if there are inconsistencies / errors in
610
+ # the arguments
611
+ def validate_event_definition_request(event_name, options) #:nodoc:
612
+ if options[:command] && options[:command] != true && !options[:command].respond_to?(:call)
613
+ raise ArgumentError, "Allowed values for :command option: true, false, nil and an object responding to #call. Got #{options[:command]}"
614
+ end
615
+
616
+ if event_name.to_sym == :stop
617
+ if options.has_key?(:terminal) && !options[:terminal]
618
+ raise ArgumentError, "the 'stop' event cannot be non-terminal"
619
+ end
620
+ options[:terminal] = true
621
+ end
622
+
623
+ # Check for inheritance rules
624
+ if events.include?(event_name)
625
+ raise ArgumentError, "event #{event_name} already defined"
626
+ elsif old_event = find_event_model(event_name)
627
+ if old_event.terminal? && !options[:terminal]
628
+ raise ArgumentError, "trying to override #{old_event.symbol} in #{self} which is terminal into a non-terminal event"
629
+ elsif old_event.controlable? && !options[:command]
630
+ raise ArgumentError, "trying to override #{old_event.symbol} in #{self} which is controlable into a non-controlable event"
631
+ end
632
+ end
633
+ end
634
+
635
+ # The events defined by the task model
636
+ #
637
+ # @return [Hash<Symbol,TaskEvent>]
638
+ inherited_attribute(:event, :events, map: true) { Hash.new }
639
+
640
+ def enum_events # :nodoc
641
+ Roby.warn_deprecated "#enum_events is deprecated, use #each_event without a block instead"
642
+ each_event
643
+ end
644
+
645
+ # Get the list of terminal events for this task model
646
+ def terminal_events
647
+ each_event.find_all { |_, e| e.terminal? }.
648
+ map { |_, e| e }
649
+ end
650
+
651
+ # Find the event class for +event+, or nil if +event+ is not an event name for this model
652
+ def find_event_model(name)
653
+ find_event(name.to_sym)
654
+ end
655
+
656
+ # Accesses an event model
657
+ #
658
+ # This method gives access to this task's event models. If given a
659
+ # name, it returns the corresponding event model. If given an event
660
+ # model, it verifies that the model is part of the events of self
661
+ # and returns it.
662
+ #
663
+ # @return [Model<TaskEvent>] a subclass of Roby::TaskEvent
664
+ # @raise [ArgumentError] if the provided event name or model does not
665
+ # exist on self
666
+ def event_model(model_def) #:nodoc:
667
+ if model_def.respond_to?(:to_sym)
668
+ ev_model = find_event_model(model_def.to_sym)
669
+ unless ev_model
670
+ all_events = each_event.map { |name, _| name }
671
+ raise ArgumentError, "#{model_def} is not an event of #{name}: #{all_events}" unless ev_model
672
+ end
673
+ elsif model_def.respond_to?(:has_ancestor?) && model_def.has_ancestor?(Roby::TaskEvent)
674
+ # Check that model_def is an event class for us
675
+ ev_model = find_event_model(model_def.symbol)
676
+ if !ev_model
677
+ raise ArgumentError, "no #{model_def.symbol} event in #{name}"
678
+ elsif ev_model != model_def
679
+ raise ArgumentError, "the event model #{model_def} is not a model for #{name} (found #{ev_model} with the same name)"
680
+ end
681
+ else
682
+ raise ArgumentError, "wanted either a symbol or an event class, got #{model_def}"
683
+ end
684
+
685
+ ev_model
686
+ end
687
+
688
+ # Checks if _name_ is a name for an event of this task
689
+ alias :has_event? :find_event_model
690
+
691
+ private :validate_event_definition_request
692
+
693
+ # Adds an event handler for the given event model. The block is
694
+ # going to be called whenever some events are emitted.
695
+ #
696
+ # Unlike a block given to {EventGenerator#on}, the block is
697
+ # evaluated in the context of the task instance.
698
+ #
699
+ # @param [Array<Symbol>] event_names the name of the events on which
700
+ # to install the handler
701
+ # @yieldparam [Object] context the arguments passed to {Roby::Task#emit}
702
+ # when the event was emitted
703
+ def on(*event_names, &user_handler)
704
+ if !user_handler
705
+ raise ArgumentError, "#on called without a block"
706
+ end
707
+
708
+ check_arity(user_handler, 1, strict: true)
709
+ event_names.each do |from|
710
+ from = event_model(from).symbol
711
+ if user_handler
712
+ method_name = "event_handler_#{from}_#{Object.address_from_id(user_handler.object_id).to_s(16)}"
713
+ define_method(method_name, &user_handler)
714
+
715
+ handler = lambda { |event| event.task.send(method_name, event) }
716
+ handler_sets[from] << EventGenerator::EventHandler.new(handler, false, false)
717
+ end
718
+ end
719
+ end
720
+
721
+
722
+ def precondition(event, reason, &block)
723
+ event = event_model(event)
724
+ precondition_sets[event.symbol] << [reason, block]
725
+ end
726
+
727
+ # Returns the lists of tags this model fullfills.
728
+ def provided_services
729
+ ancestors.find_all { |m| m.kind_of?(Models::TaskServiceModel) }
730
+ end
731
+
732
+ # Declares that the given block should be called at each execution
733
+ # cycle, when the task is running. Use it that way:
734
+ #
735
+ # class MyTask < Roby::Task
736
+ # poll do
737
+ # ... do something ...
738
+ # end
739
+ # end
740
+ #
741
+ # If the given polling block raises an exception, the task will be
742
+ # terminated by emitting its +failed+ event.
743
+ def poll(&block)
744
+ if !block_given?
745
+ raise ArgumentError, "no block given"
746
+ end
747
+
748
+ define_method(:poll_handler, &block)
749
+ end
750
+
751
+ # Defines an exception handler.
752
+ #
753
+ # When propagating exceptions, {ExecutionException} goes up in the
754
+ # task hierarchy and calls matching handlers on the tasks it finds,
755
+ # and on their planning task. The first matching handler is called,
756
+ # and the exception propagation assumes that it handled the
757
+ # exception (i.e. won't look for new handlers) unless it calls
758
+ # {Roby::Task#pass_exception}
759
+ #
760
+ # @param [#to_execution_exception_matcher] matcher object for
761
+ # exceptions. Subclasses of {LocalizedError} have it (matching the
762
+ # exception class) as well as {Task} (matches exception origin).
763
+ # See {Roby::Queries} for more advanced exception matchers.
764
+ #
765
+ # @yieldparam [ExecutionException] exception the exception that is
766
+ # being handled
767
+ #
768
+ # @example install a handler for a TaskModelViolation exception
769
+ # on_exception(TaskModelViolation, ...) do |task, exception_object|
770
+ # if cannot_handle
771
+ # task.pass_exception # send to the next handler
772
+ # end
773
+ # do_handle
774
+ # end
775
+ def on_exception(matcher, &handler)
776
+ check_arity(handler, 1, strict: true)
777
+ matcher = matcher.to_execution_exception_matcher
778
+ id = (@@exception_handler_id += 1)
779
+ define_method("exception_handler_#{id}", &handler)
780
+ exception_handlers.unshift [matcher, instance_method("exception_handler_#{id}")]
781
+ end
782
+
783
+ @@exception_handler_id = 0
784
+
785
+ def query(*args)
786
+ q = Queries::Query.new
787
+ if args.empty? && self != Task
788
+ q.which_fullfills(self)
789
+ else
790
+ q.which_fullfills(*args)
791
+ end
792
+ q
793
+ end
794
+
795
+ # Returns a TaskMatcher object that matches this task model
796
+ def match(*args)
797
+ matcher = Queries::TaskMatcher.new
798
+ if args.empty? && self != Task
799
+ matcher.which_fullfills(self)
800
+ else
801
+ matcher.which_fullfills(*args)
802
+ end
803
+ matcher
804
+ end
805
+
806
+ # @return [Queries::ExecutionExceptionMatcher] an exception match
807
+ # object that matches exceptions originating from this task
808
+ def to_execution_exception_matcher
809
+ Queries::ExecutionExceptionMatcher.new.with_origin(self)
810
+ end
811
+
812
+ def fullfills?(models)
813
+ if models.respond_to?(:each)
814
+ models = models.to_a
815
+ else models = [models]
816
+ end
817
+
818
+ models.each do |m|
819
+ m.each_fullfilled_model do |test_m|
820
+ return false if !has_ancestor?(test_m)
821
+ end
822
+ end
823
+ return true
824
+ end
825
+
826
+ def can_merge?(target_model)
827
+ fullfills?(target_model)
828
+ end
829
+
830
+ def to_coordination_task(task_model)
831
+ Roby::Coordination::Models::TaskFromAsPlan.new(self, self)
832
+ end
833
+ end
834
+ end
835
+ end