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,26 @@
1
+ module Roby
2
+ # Modifies an event context
3
+ #
4
+ # See EventGenerator#filter for details
5
+ class FilterGenerator < EventGenerator
6
+ def initialize(user_context, &block)
7
+ if block && !user_context.empty?
8
+ raise ArgumentError, "you must set either the filter or the value, not both"
9
+ end
10
+
11
+ if block
12
+ super() do |context|
13
+ context = context.map do |val|
14
+ block.call(val)
15
+ end
16
+ emit(*context)
17
+ end
18
+ else
19
+ super() do
20
+ emit(*user_context)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,225 @@
1
+ require 'roby/gui/chronicle_widget'
2
+
3
+ module Roby
4
+ module GUI
5
+ # Integration of a {ChronicleWidget} to use with a {PlanRebuilderWidget}
6
+ class ChronicleView < Qt::Widget
7
+ # The underlying ChronicleWidget instance
8
+ attr_reader :chronicle
9
+ # The historyw widget instance
10
+ attr_reader :history_widget
11
+
12
+ def initialize(history_widget, parent = nil)
13
+ super(parent)
14
+
15
+ @layout = Qt::VBoxLayout.new(self)
16
+ @menu_layout = Qt::HBoxLayout.new
17
+ @layout.add_layout(@menu_layout)
18
+ @history_widget = history_widget
19
+ @chronicle = ChronicleWidget.new(self)
20
+ Qt::Object.connect(@chronicle, SIGNAL('selectedTime(QDateTime)'),
21
+ history_widget, SLOT('seek(QDateTime)'))
22
+ chronicle.add_tasks_info(*history_widget.tasks_info)
23
+ Qt::Object.connect(history_widget, SIGNAL('addedSnapshot(int)'),
24
+ self, SLOT('addedSnapshot(int)'))
25
+ @layout.add_widget(@chronicle)
26
+
27
+ # Now setup the menu bar
28
+ @btn_play = Qt::PushButton.new("Play", self)
29
+ @menu_layout.add_widget(@btn_play)
30
+ @btn_play.connect(SIGNAL('clicked()')) do
31
+ if @play_timer
32
+ stop
33
+ @btn_play.text = "Play"
34
+ else
35
+ play
36
+ @btn_play.text = "Stop"
37
+ end
38
+ end
39
+
40
+ @btn_sort = Qt::PushButton.new("Sort", self)
41
+ @menu_layout.add_widget(@btn_sort)
42
+ @btn_sort.menu = sort_options
43
+ @btn_show = Qt::PushButton.new("Show", self)
44
+ @menu_layout.add_widget(@btn_show)
45
+ @btn_show.menu = show_options
46
+ @menu_layout.add_stretch(1)
47
+ @restrict_to_jobs_btn = Qt::CheckBox.new("Restrict to jobs", self)
48
+ @restrict_to_jobs_btn.checkable = true
49
+ @restrict_to_jobs_btn.connect(SIGNAL('toggled(bool)')) do |set|
50
+ chronicle.restrict_to_jobs = set
51
+ end
52
+ @menu_layout.add_widget(@restrict_to_jobs_btn)
53
+
54
+ @filter_lbl = Qt::Label.new("Filter", self)
55
+ @filter_box = Qt::LineEdit.new(self)
56
+ @filter_box.connect(SIGNAL('textChanged(QString const&)')) do |text|
57
+ if text.empty?
58
+ chronicle.filter = nil
59
+ else
60
+ chronicle.filter = Regexp.new(text.split(' ').join("|"))
61
+ end
62
+ end
63
+ @menu_layout.add_widget(@filter_lbl)
64
+ @menu_layout.add_widget(@filter_box)
65
+ @filter_out_lbl = Qt::Label.new("Filter out", self)
66
+ @filter_out_box = Qt::LineEdit.new(self)
67
+ @filter_out_box.connect(SIGNAL('textChanged(QString const&)')) do |text|
68
+ if text.empty?
69
+ chronicle.filter_out = nil
70
+ else
71
+ chronicle.filter_out = Regexp.new(text.split(' ').join("|"))
72
+ end
73
+ end
74
+ @menu_layout.add_widget(@filter_out_lbl)
75
+ @menu_layout.add_widget(@filter_out_box)
76
+ @menu_layout.add_stretch(1)
77
+
78
+ resize(500, 300)
79
+ end
80
+
81
+ def addedSnapshot(cycle)
82
+ chronicle.add_tasks_info(*history_widget.tasks_info_of_snapshot(cycle))
83
+ end
84
+ slots 'addedSnapshot(int)'
85
+
86
+ def sort_options
87
+ @mnu_sort = Qt::Menu.new(self)
88
+ @actgrp_sort = Qt::ActionGroup.new(@mnu_sort)
89
+
90
+ @act_sort = Hash.new
91
+ { "Start time" => :start_time, "Last event" => :last_event }.
92
+ each do |text, value|
93
+ act = Qt::Action.new(text, self)
94
+ act.checkable = true
95
+ act.connect(SIGNAL('toggled(bool)')) do |onoff|
96
+ if onoff
97
+ @chronicle.sort_mode = value
98
+ @chronicle.update
99
+ end
100
+ end
101
+ @actgrp_sort.add_action(act)
102
+ @mnu_sort.add_action(act)
103
+ @act_sort[value] = act
104
+ end
105
+
106
+ @act_sort[:start_time].checked = true
107
+ @mnu_sort
108
+ end
109
+
110
+ def show_options
111
+ @mnu_show = Qt::Menu.new(self)
112
+ @actgrp_show = Qt::ActionGroup.new(@mnu_show)
113
+
114
+ @act_show = Hash.new
115
+ { "All" => :all, "Running" => :running, "Current" => :current }.
116
+ each do |text, value|
117
+ act = Qt::Action.new(text, self)
118
+ act.checkable = true
119
+ act.connect(SIGNAL('toggled(bool)')) do |onoff|
120
+ if onoff
121
+ @chronicle.show_mode = value
122
+ @chronicle.setDisplayTime
123
+ end
124
+ end
125
+ @actgrp_show.add_action(act)
126
+ @mnu_show.add_action(act)
127
+ @act_show[value] = act
128
+ end
129
+
130
+ @act_show[:all].checked = true
131
+ @mnu_show
132
+ end
133
+
134
+ PLAY_STEP = 0.1
135
+
136
+ def play
137
+ @play_timer = Qt::Timer.new(self)
138
+ Qt::Object.connect(@play_timer, SIGNAL('timeout()'), self, SLOT('step()'))
139
+ @play_timer.start(Integer(1000 * PLAY_STEP))
140
+ end
141
+ slots 'play()'
142
+
143
+ def step
144
+ if chronicle.display_time == chronicle.current_time
145
+ return
146
+ end
147
+
148
+ new_time = chronicle.display_time + PLAY_STEP
149
+ if new_time >= chronicle.current_time
150
+ new_time = chronicle.current_time
151
+ end
152
+ chronicle.setDisplayTime(new_time)
153
+ end
154
+ slots 'step()'
155
+
156
+ def stop
157
+ @play_timer.stop
158
+ @play_timer = nil
159
+ end
160
+ slots 'stop()'
161
+
162
+ def updateWindowTitle
163
+ if parent_title = history_widget.window_title
164
+ self.window_title = parent_title + ": Chronicle"
165
+ else
166
+ self.window_title = "roby-display: Chronicle"
167
+ end
168
+ end
169
+ slots 'updateWindowTitle()'
170
+
171
+ def update_time_range(start_time, current_time)
172
+ chronicle.update_time_range(start_time, current_time)
173
+ end
174
+
175
+ def update_display_time(display_time)
176
+ chronicle.update_display_time(display_time)
177
+ end
178
+
179
+ def setDisplayTime(time)
180
+ if !chronicle.base_time
181
+ chronicle.update_base_time(history_widget.start_time)
182
+ chronicle.update_current_time(history_widget.current_time)
183
+ end
184
+ @chronicle.setDisplayTime(time)
185
+ end
186
+ slots 'setDisplayTime(QDateTime)'
187
+
188
+ def setCurrentTime(time)
189
+ if !chronicle.base_time
190
+ chronicle.update_base_time(history_widget.start_time)
191
+ chronicle.update_current_time(history_widget.current_time)
192
+ end
193
+ @chronicle.setCurrentTime(time)
194
+ end
195
+ slots 'setCurrentTime(QDateTime)'
196
+
197
+ # Save view configuration
198
+ def save_options
199
+ result = Hash.new
200
+ result['show_mode'] = chronicle.show_mode
201
+ result['sort_mode'] = chronicle.sort_mode
202
+ result['time_scale'] = chronicle.time_scale
203
+ result['restrict_to_jobs'] = chronicle.restrict_to_jobs?
204
+ result
205
+ end
206
+
207
+ # Apply saved configuration
208
+ def apply_options(options)
209
+ if scale = options['time_scale']
210
+ chronicle.time_scale = scale
211
+ end
212
+ if mode = options['show_mode']
213
+ @act_show[mode].checked = true
214
+ end
215
+ if mode = options['sort_mode']
216
+ @act_sort[mode].checked = true
217
+ end
218
+ if mode = options['restrict_to_jobs']
219
+ @restrict_to_jobs_btn.checked = true
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+
@@ -0,0 +1,925 @@
1
+ require 'Qt4'
2
+ require 'roby/gui/qt4_toMSecsSinceEpoch'
3
+ require 'utilrb/module/attr_predicate'
4
+ require 'roby/gui/styles'
5
+ require 'roby/gui/object_info_view'
6
+ require 'roby/gui/task_state_at'
7
+
8
+ module Roby
9
+ module GUI
10
+ # Widget to display tasks on a chronicle (i.e. timelines)
11
+ #
12
+ # Use {ChronicleView} when using a {PlanRebuilderWidget}
13
+ #
14
+ # The following interactions are available:
15
+ #
16
+ # * CTRL + wheel: change time scale
17
+ # * ALT + wheel: horizontal scroll
18
+ # * wheel: vertical scroll
19
+ # * double-click: task info view
20
+ #
21
+ class ChronicleWidget < Qt::AbstractScrollArea
22
+ # Whether the widget gets live data in real time
23
+ attr_predicate :live?
24
+ def live=(flag)
25
+ @live = flag
26
+ self.track_current_time = live? && (value == horizontal_scroll_bar.maximum)
27
+ end
28
+ # Whether the widget is currently tracking {#current_time}
29
+ attr_predicate :track_current_time?, true
30
+ # True if the time scroll bar is currently pressed
31
+ attr_predicate :horizontal_scroll_bar_down?, true
32
+ # Internal representation of the desired time scale. Don't use it
33
+ # directly, but use #time_to_pixel or #pixel_to_time
34
+ attr_reader :time_scale
35
+ # Change the time scale and update the view
36
+ def time_scale=(new_value)
37
+ @time_scale = new_value
38
+ @pixel_to_time =
39
+ if new_value < 0
40
+ new_value.abs
41
+ else 1.0 / new_value
42
+ end
43
+
44
+ @time_to_pixel =
45
+ if new_value > 0
46
+ new_value
47
+ else 1.0 / new_value.abs
48
+ end
49
+
50
+ update_scroll_ranges
51
+ invalidate_layout_cache
52
+ invalidate_current_tasks
53
+ update
54
+ end
55
+
56
+ # Scale factor to convert seconds to pixels
57
+ #
58
+ # pixels = time_to_pixel * time
59
+ attr_reader :time_to_pixel
60
+
61
+ # Scale factor to convert seconds to pixels
62
+ #
63
+ # pixel = time_to_pixel * time
64
+ attr_reader :pixel_to_time
65
+
66
+ # How many pixels should there be between the 'now' line and the
67
+ # right side, in pixels
68
+ attr_reader :live_update_margin
69
+ # The point (in pixels) where the current display time should be
70
+ # located on the display
71
+ attr_accessor :display_point
72
+ # The time that is currently at the middle of the view
73
+ attr_accessor :display_time
74
+ # The system's current time
75
+ attr_accessor :current_time
76
+ # The startup time
77
+ attr_accessor :base_time
78
+ # The base height of a task line
79
+ attr_accessor :task_height
80
+ # The separation, in pixels, between tasks
81
+ attr_accessor :task_separation
82
+ # The index of the task that is currently at the top of the view. It
83
+ # is an index in #current_tasks
84
+ attr_accessor :start_line
85
+ # All known tasks
86
+ #
87
+ # @see add_tasks_info remove_tasks
88
+ attr_accessor :all_tasks
89
+ # Job information about all known tasks
90
+ #
91
+ # @see add_tasks_info remove_tasks
92
+ attr_accessor :all_job_info
93
+ # Scheduler information
94
+ #
95
+ # @return [Schedulers::State]
96
+ attr_reader :scheduler_state
97
+ # The task layout as computed in the last call to #paintEvent
98
+ attr_reader :task_layout
99
+ # The set of tasks that should currently be managed by the view.
100
+ #
101
+ # It is updated in #update(), i.e. when the view gets something to
102
+ # display
103
+ attr_reader :current_tasks
104
+ # An ordered set of [y, task], where +y+ is the position in Y of the
105
+ # bottom of a task line and +task+ the corresponding task object
106
+ #
107
+ # It is updated on display
108
+ attr_reader :position_to_task
109
+ # The current sorting mode. Can be :start_time or :last_event.
110
+ # Defaults to :start_time
111
+ #
112
+ # In :start mode, the tasks are sorted by the time at which they
113
+ # started. In :last_event, by the time of the last event emitted
114
+ # before the current displayed time: it shows the last active tasks
115
+ # first)
116
+ attr_reader :sort_mode
117
+ # See #sort_mode
118
+ def sort_mode=(mode)
119
+ if ![:start_time, :last_event].include?(mode)
120
+ raise ArgumentError, "sort_mode can either be :start_time or :last_event, got #{mode}"
121
+ end
122
+ @sort_mode = mode
123
+ end
124
+ # Whether the order defined by {#sort_mode} should be inverted
125
+ def reverse_sort?
126
+ !!@reverse_sort
127
+ end
128
+ # Whether the order defined by {#sort_mode} should be inverted
129
+ def reverse_sort=(flag)
130
+ @reverse_sort = flag
131
+ end
132
+ # High-level filter on the list of shown tasks. Can either be :all,
133
+ # :running, :current. Defaults to :all
134
+ #
135
+ # In :all mode, all tasks that are included in a plan in a certain
136
+ # point in time are displayed.
137
+ #
138
+ # In :running mode, only the tasks that are running within the
139
+ # display time window are shown.
140
+ #
141
+ # In :current mode, only the tasks that have emitted events within
142
+ # the display time window are shown
143
+ #
144
+ # In :in_range mode, only the tasks that would display something
145
+ # within the display time window are shown
146
+ attr_reader :show_mode
147
+
148
+ # Per-task visual layout information
149
+ #
150
+ # @return [Hash<Task,TaskLayout>]
151
+ attr_reader :layout_cache
152
+
153
+ # Per-task messages to be displayed
154
+ attr_reader :messages_per_task
155
+
156
+ # @api private
157
+ #
158
+ # Clears {#layout_cache} because parameters changed that require to
159
+ # recompute the task layouts
160
+ def invalidate_layout_cache
161
+ layout_cache.clear
162
+ end
163
+
164
+ # See #show_mode
165
+ def show_mode=(mode)
166
+ if ![:all, :running, :current, :in_range].include?(mode)
167
+ raise ArgumentError, "show_mode can be :all, :running, :in_range or :current, got #{mode}"
168
+ end
169
+ @show_mode = mode
170
+ end
171
+
172
+ # @return [Boolean] true if only the action's toplevel tasks are
173
+ # shown
174
+ attr_predicate :restrict_to_jobs?
175
+
176
+ # Sets whether only the toplevel job tasks should be shown
177
+ def restrict_to_jobs=(set)
178
+ @restrict_to_jobs = set
179
+ setDisplayTime
180
+ update
181
+ end
182
+
183
+ # Inclusion filter on task names
184
+ #
185
+ # If it contains a regular expression, only the task names that
186
+ # match the expression will be displayed
187
+ attr_reader :filter
188
+
189
+ # Sets the filter regular expression. See #filter
190
+ def filter=(value)
191
+ @filter = value
192
+ setDisplayTime
193
+ update
194
+ end
195
+
196
+ # Exclusion filter on task names
197
+ #
198
+ # If it contains a regular expression, the task names that match the
199
+ # expression will not be displayed
200
+ attr_reader :filter_out
201
+
202
+ # Sets the filter_out regular expression. See #filter_out
203
+ def filter_out=(value)
204
+ @filter_out = value
205
+ setDisplayTime
206
+ update
207
+ end
208
+
209
+ # Display the events "in the future", or stop at the current time.
210
+ # When enabled, a log replay display will look like a live display
211
+ # (use to generate videos for instance)
212
+ attr_predicate :show_future_events?, true
213
+
214
+ def initialize(parent = nil)
215
+ super(parent)
216
+
217
+ @layout_cache = Hash.new
218
+ @messages_per_task = Hash.new { |h, k| h[k] = Array.new }
219
+ @current_tasks = Array.new
220
+ @current_tasks_dirty = true
221
+ self.time_scale = 10
222
+ @task_height = 10
223
+ @task_separation = 10
224
+ @live_update_margin = 10
225
+ @start_line = 0
226
+ @all_tasks = Set.new
227
+ @all_job_info = Hash.new
228
+ @scheduler_state = Schedulers::State.new
229
+ @task_layout = Array.new
230
+ @sort_mode = :start_time
231
+ @reverse_sort = false
232
+ @show_mode = :all
233
+ @show_future_events = true
234
+ @live = true
235
+ @track_current_time = true
236
+ @horizontal_scroll_bar_down = false
237
+ @display_point = viewport.size.width - live_update_margin
238
+
239
+ viewport = Qt::Widget.new
240
+ pal = Qt::Palette.new(viewport.palette)
241
+ pal.setColor(Qt::Palette::Background, Qt::Color.new('white'))
242
+ viewport.setAutoFillBackground(true);
243
+ viewport.setPalette(pal)
244
+ self.viewport = viewport
245
+
246
+ horizontal_scroll_bar.connect(SIGNAL('sliderMoved(int)')) do
247
+ value = horizontal_scroll_bar.value
248
+ self.track_current_time = live? && (value == horizontal_scroll_bar.maximum)
249
+ time = base_time + Float(value) * pixel_to_time
250
+ update_display_time(time)
251
+ emit timeChanged(time - base_time)
252
+ end
253
+ horizontal_scroll_bar.connect(SIGNAL('sliderPressed()')) do
254
+ self.horizontal_scroll_bar_down = true
255
+ end
256
+ horizontal_scroll_bar.connect(SIGNAL('sliderReleased()')) do
257
+ self.track_current_time = live? && (horizontal_scroll_bar.value == horizontal_scroll_bar.maximum)
258
+ self.horizontal_scroll_bar_down = false
259
+ update_scroll_ranges
260
+ end
261
+ vertical_scroll_bar.connect(SIGNAL('valueChanged(int)')) do
262
+ value = vertical_scroll_bar.value
263
+ if value < current_tasks.size
264
+ self.start_line = value
265
+ update
266
+ end
267
+ end
268
+ end
269
+
270
+ # Signal emitted when the currently displayed time changed. The time
271
+ # is provided as an offset since base_time
272
+ signals 'void timeChanged(float)'
273
+
274
+ # Event handler for wheel event
275
+ def wheelEvent(event)
276
+ if event.modifiers != Qt::ControlModifier
277
+ # Don't let the user scroll with the mouse if vertical
278
+ # scrolling is off
279
+ if vertical_scroll_bar_policy == Qt::ScrollBarAlwaysOff
280
+ event.ignore
281
+ return
282
+ else
283
+ return super
284
+ end
285
+ end
286
+
287
+ # See documentation of wheelEvent
288
+ degrees = event.delta / 8.0
289
+ num_steps = degrees / 15
290
+
291
+ old = self.time_scale
292
+ new = old + num_steps
293
+ if new == 0
294
+ if old > 0
295
+ self.time_scale = -1
296
+ else
297
+ self.time_scale = 1
298
+ end
299
+ else
300
+ self.time_scale = new
301
+ end
302
+ event.accept
303
+ end
304
+
305
+ def clear_tasks_info
306
+ all_tasks.clear
307
+ all_job_info.clear
308
+ self.scheduler_state = Schedulers::State.new
309
+ end
310
+
311
+ def scheduler_state=(state)
312
+ messages_per_task.clear
313
+
314
+ state.pending_non_executable_tasks.each do |msg, *args|
315
+ formatted_msg = Schedulers::State.format_message_into_string(msg, *args)
316
+ args.each do |obj|
317
+ if obj.kind_of?(Roby::Task)
318
+ messages_per_task[obj] << formatted_msg
319
+ end
320
+ end
321
+ end
322
+
323
+ scheduler_state.non_scheduled_tasks.each do |task, messages|
324
+ messages_per_task[task].concat(messages.map { |msg, *args| Schedulers::State.format_message_into_string(msg, task, *args) })
325
+ end
326
+ scheduler_state.actions.each do |task, messages|
327
+ messages_per_task[task].concat(messages.map { |msg, *args| Schedulers::State.format_message_into_string(msg, task, *args) })
328
+ end
329
+ @scheduler_state = state
330
+ end
331
+
332
+ # Add information to the chronicle for the next display update
333
+ #
334
+ # @param [Array<Roby::Task>] tasks the set of tasks to display
335
+ # @param [Hash<Roby::Task,Roby::Task>] mapping from a placeholder
336
+ # task and the job task it represents
337
+ # @param [Roby::Schedulers::State] scheduler information to be displayed
338
+ # on the chronicle
339
+ def add_tasks_info(tasks, job_info)
340
+ tasks.each do |t|
341
+ if base_time && t.addition_time < base_time
342
+ update_base_time(t.addition_time)
343
+ end
344
+ end
345
+
346
+ all_tasks.merge(tasks)
347
+ all_job_info.merge!(job_info)
348
+ end
349
+
350
+ def remove_tasks(tasks)
351
+ tasks.each do |t|
352
+ all_tasks.delete(t)
353
+ all_job_info.delete(t)
354
+ end
355
+ end
356
+
357
+ def contents_height
358
+ update_current_tasks
359
+
360
+ display_start, display_end = displayed_time_range
361
+ fm = Qt::FontMetrics.new(font)
362
+ height = current_tasks.inject(0) do |h, t|
363
+ h + lay_out_task(fm, t).height(display_start, display_end)
364
+ end
365
+ height + current_tasks.size * task_separation + timeline_height
366
+ end
367
+
368
+ # @api private
369
+ #
370
+ # Updates the start and current time
371
+ def update_time_range(start_time, current_time)
372
+ if start_time
373
+ update_base_time(start_time)
374
+ end
375
+ if current_time
376
+ update_current_time(current_time)
377
+ end
378
+ end
379
+
380
+ # @api private
381
+ # Update the time at the start of the chronicle
382
+ def update_base_time(time)
383
+ @base_time = time
384
+ invalidate_current_tasks
385
+ invalidate_layout_cache
386
+ end
387
+
388
+ # @api private
389
+ # Update the time at the end of the chronicle
390
+ def update_current_time(time)
391
+ @current_time = time
392
+ if !base_time
393
+ update_base_time(time)
394
+ end
395
+ if !display_time || track_current_time?
396
+ update_display_time(time)
397
+ else
398
+ update_scroll_ranges
399
+ invalidate_current_tasks
400
+ end
401
+ end
402
+
403
+ # @api private
404
+ # Update the currently displayed time
405
+ def update_display_time(time)
406
+ @display_time = time
407
+ if !base_time
408
+ update_base_time(time)
409
+ end
410
+
411
+ _, end_time = displayed_time_range
412
+ update_display_point
413
+
414
+ if !horizontal_scroll_bar_down?
415
+ update_scroll_ranges
416
+ horizontal_scroll_bar.value = time_to_pixel * (display_time - base_time)
417
+ end
418
+
419
+ invalidate_current_tasks
420
+ end
421
+
422
+ def update_display_point
423
+ display_point = viewport.size.width - live_update_margin -
424
+ (current_time - display_time) * time_to_pixel
425
+ display_point_min = viewport.size.width / 2
426
+ if display_point < display_point_min
427
+ display_point = display_point_min
428
+ end
429
+ @display_point = Integer(display_point)
430
+ update_displayed_time_range
431
+ invalidate_current_tasks
432
+ end
433
+
434
+ def resizeEvent(event)
435
+ if track_current_time?
436
+ @display_point = event.size.width - live_update_margin
437
+ elsif display_time && current_time
438
+ update_display_point
439
+ end
440
+ update_displayed_time_range
441
+ invalidate_current_tasks
442
+ event.accept
443
+ end
444
+
445
+ def update_displayed_time_range
446
+ if display_time
447
+ display_point = self.display_point
448
+ window_width = viewport.size.width
449
+ start_time = display_time - display_point * pixel_to_time
450
+ end_time = start_time + window_width * pixel_to_time
451
+ @displayed_time_range = [start_time, end_time]
452
+ end
453
+ end
454
+
455
+ # The range, in absolute time, currently visible in the view
456
+ #
457
+ # @return [(Time,Time),nil] either said range, or nil if nothing has
458
+ # ever been displayed so far
459
+ attr_reader :displayed_time_range
460
+
461
+ def invalidate_current_tasks
462
+ @current_tasks_dirty = true
463
+ end
464
+
465
+ def current_tasks_dirty?
466
+ @current_tasks_dirty
467
+ end
468
+
469
+ def update_current_tasks(force: false)
470
+ return if !force && !current_tasks_dirty?
471
+
472
+ current_tasks = all_tasks.dup
473
+ if restrict_to_jobs?
474
+ current_tasks = all_job_info.keys.to_set
475
+ end
476
+ if filter
477
+ current_tasks = current_tasks.find_all { |t| t.to_s =~ filter }
478
+ end
479
+ if filter_out
480
+ current_tasks.delete_if { |t| t.to_s =~ filter_out }
481
+ end
482
+ started_tasks, pending_tasks = current_tasks.partition { |t| t.start_time }
483
+
484
+ if sort_mode == :last_event
485
+ not_yet_started, started_tasks = started_tasks.partition { |t| t.start_time > display_time }
486
+ current_tasks =
487
+ started_tasks.sort_by do |t|
488
+ last_event = nil
489
+ t.history.each do |ev|
490
+ if ev.time < display_time
491
+ last_event = ev
492
+ else break
493
+ end
494
+ end
495
+ last_event.time
496
+ end
497
+ current_tasks = current_tasks.reverse
498
+ current_tasks.concat(not_yet_started.sort_by { |t| t.start_time })
499
+ if show_mode == :all
500
+ current_tasks.
501
+ concat(pending_tasks.sort_by { |t| t.addition_time })
502
+ end
503
+ else
504
+ current_tasks =
505
+ (started_tasks + pending_tasks).sort_by { |t| t.start_time || t.addition_time }
506
+ end
507
+
508
+ start_time, end_time = displayed_time_range
509
+
510
+ if start_time && (show_mode == :running || show_mode == :current)
511
+ current_tasks = current_tasks.find_all do |t|
512
+ (t.start_time && t.start_time < end_time) &&
513
+ (!t.end_time || t.end_time > start_time)
514
+ end
515
+
516
+ if show_mode == :current
517
+ current_tasks = current_tasks.find_all do |t|
518
+ t.history.any? { |ev| ev.time > start_time && ev.time < end_time }
519
+ end
520
+ end
521
+ end
522
+
523
+ tasks_in_range, tasks_outside_range =
524
+ current_tasks.partition do |t|
525
+ (t.addition_time <= end_time) &&
526
+ (!t.finalization_time || t.finalization_time >= start_time)
527
+ end
528
+
529
+ if reverse_sort?
530
+ tasks_in_range = tasks_in_range.reverse
531
+ tasks_outside_range = tasks_outside_range.reverse
532
+ end
533
+
534
+ @current_tasks_dirty = false
535
+ if show_mode == :in_range
536
+ @current_tasks = tasks_in_range
537
+ else
538
+ @current_tasks = tasks_in_range + tasks_outside_range
539
+ end
540
+ vertical_scroll_bar.setRange(0, current_tasks.size)
541
+ end
542
+
543
+ def massage_slot_time_argument(time, default)
544
+ # Convert from QDateTime to allow update() to be a slot
545
+ if time.kind_of?(Qt::DateTime)
546
+ return Time.at(Float(time.toMSecsSinceEpoch) / 1000)
547
+ elsif !time
548
+ return default
549
+ else time
550
+ end
551
+ end
552
+
553
+ def setDisplayTime(time = nil)
554
+ time = massage_slot_time_argument(time, display_time)
555
+ return if !time
556
+
557
+ update_base_time(time) if !base_time
558
+ update_current_time(time) if !current_time
559
+ update_display_time(time)
560
+ update
561
+ end
562
+ slots 'setDisplayTime(QDateTime)'
563
+
564
+ def setCurrentTime(time = nil)
565
+ time = massage_slot_time_argument(time, current_time)
566
+ return if !time
567
+
568
+ update_base_time(time) if !base_time
569
+ update_current_time(time)
570
+ update
571
+ end
572
+ slots 'setCurrentTime(QDateTime)'
573
+
574
+ SCALES = [1, 2, 5, 10, 20, 30, 60, 90, 120, 300, 600, 1200, 1800, 3600]
575
+ def paint_timeline(painter, fm)
576
+ text_height = fm.height
577
+ window_size = viewport.size
578
+
579
+ # Display the current cycle time
580
+ central_label = Roby.format_time(display_time)
581
+ central_label_width = fm.width(central_label)
582
+ central_time_max = display_point + central_label_width / 2
583
+ if central_time_max + 3 > window_size.width
584
+ central_time_max = window_size.width - 3
585
+ end
586
+ central_time_min = central_time_max - central_label_width
587
+ if central_time_min < 3
588
+ central_time_min = 3
589
+ central_time_max = central_time_min + central_label_width
590
+ end
591
+ painter.pen = TIMELINE_GRAY_PEN
592
+ painter.drawText(central_time_min, text_height, central_label)
593
+ painter.drawRect(central_time_min - 2, 0, central_label_width + 4, text_height + 2)
594
+
595
+ # First, decide on the scale. We compute a "normal" text width
596
+ # for the time labels, and check what would be a round time-step
597
+ min_step_size = pixel_to_time * 1.5 * central_label_width
598
+ step_size = SCALES.find do |scale|
599
+ scale > min_step_size
600
+ end
601
+ step_size ||= SCALES.last
602
+ # Now display the timeline itself. If a normal ruler collides
603
+ # with the current time, just ignore it
604
+ start_time, end_time = displayed_time_range
605
+ painter.pen = TIMELINE_BLACK_PEN
606
+ ruler_base_time = (start_time.to_f / step_size).floor * step_size
607
+ ruler_base_x = (ruler_base_time - start_time.to_f) * time_to_pixel
608
+ step_count = ((end_time.to_f - ruler_base_time) / step_size).ceil
609
+ step_count.times do |i|
610
+ time = step_size * i + ruler_base_time
611
+ pos = step_size * i * time_to_pixel + ruler_base_x
612
+ time_as_text = Roby.format_time(Time.at(time))
613
+ time_as_text_width = fm.width(time_as_text)
614
+ min_x = pos - time_as_text_width / 2
615
+ max_x = pos + time_as_text_width / 2
616
+ if central_time_min > max_x || central_time_max < min_x
617
+ painter.drawText(min_x, text_height, time_as_text)
618
+ end
619
+ painter.drawLine(pos, text_height + fm.descent, pos, text_height + fm.descent + TIMELINE_RULER_LINE_LENGTH)
620
+ end
621
+ end
622
+
623
+ class TaskLayout
624
+ attr_reader :base_time
625
+ attr_reader :time_to_pixel
626
+ attr_reader :fm
627
+
628
+ attr_reader :task
629
+ attr_reader :state
630
+
631
+ attr_reader :add_point
632
+ attr_reader :start_point
633
+ attr_reader :end_point
634
+ attr_reader :finalization_point
635
+
636
+ attr_reader :event_height
637
+ attr_reader :event_max_x
638
+ attr_reader :events
639
+ attr_accessor :messages
640
+
641
+ attr_accessor :title
642
+
643
+ attr_reader :base_height
644
+
645
+ def initialize(task, base_time, time_to_pixel, fm)
646
+ @task = task
647
+ @base_time = base_time
648
+ @time_to_pixel = time_to_pixel
649
+ @fm = fm
650
+ @event_height = [2 * EVENT_CIRCLE_RADIUS, fm.height].max
651
+
652
+ @add_point = time_to_pixel * (task.addition_time - base_time)
653
+ @start_point = nil
654
+ @end_point = nil
655
+ @finalization_point = nil
656
+
657
+ @state = :pending
658
+ @messages = Array.new
659
+ @events = Array.new
660
+ @event_max_x = Array.new
661
+ @base_height = event_height
662
+ update
663
+ end
664
+
665
+ def update
666
+ return if @finalization_point
667
+ history_size = events.size
668
+ return if !task.finalization_time && task.history.size == history_size
669
+
670
+ last_event = task.last_event
671
+ if !last_event
672
+ @state = :pending
673
+ else
674
+ @state = GUI.task_state_at(task, last_event.time)
675
+ @time_last_event = last_event.time
676
+ if state != :running
677
+ end_time = last_event.time
678
+ end
679
+ end
680
+
681
+
682
+ if start_time = task.start_time
683
+ @time_first_event = start_time
684
+ @start_point = time_to_pixel * (start_time - base_time)
685
+ end
686
+ if end_time
687
+ @end_point = time_to_pixel * (end_time - base_time)
688
+ end
689
+ if finalization_time = task.finalization_time
690
+ @finalization_point = time_to_pixel * (finalization_time - base_time)
691
+ end
692
+
693
+ task.history[history_size..-1].each do |event|
694
+ append_event(event, event_height)
695
+ end
696
+ end
697
+
698
+ def append_event(event, event_height)
699
+ event_x = Integer(time_to_pixel * (event.time - base_time))
700
+ event_current_level = nil
701
+ event_max_x.each_with_index do |x, idx|
702
+ if x < event_x - EVENT_CIRCLE_RADIUS
703
+ event_current_level = idx
704
+ break
705
+ end
706
+ end
707
+ event_current_level ||= event_max_x.size
708
+
709
+ event_y = event_current_level * event_height
710
+ event_max_x[event_current_level] = event_x + 2 * EVENT_CIRCLE_RADIUS + fm.width(event.symbol.to_s)
711
+ events << [event.time, event_x, event_y, event.symbol.to_s]
712
+ end
713
+
714
+ def events_in_range(display_start_time, display_end_time)
715
+ if !@time_first_event
716
+ return
717
+ elsif @time_first_event > display_end_time
718
+ return
719
+ elsif @time_last_event < display_start_time
720
+ return
721
+ else
722
+ result = []
723
+ events.each do |ev|
724
+ time = ev.first
725
+ if time > display_start_time
726
+ if time < display_end_time
727
+ result << ev
728
+ else
729
+ break
730
+ end
731
+ end
732
+ end
733
+ result if !result.empty?
734
+ end
735
+ end
736
+
737
+ def height(display_start_time, display_end_time)
738
+ if events = events_in_range(display_start_time, display_end_time)
739
+ max_event_y = events.max_by { |_, _, y, _| y }
740
+ max_event_y = max_event_y[2]
741
+ end
742
+ (max_event_y || 0) + (messages.size + 1) * fm.height
743
+ end
744
+ end
745
+
746
+ def lay_out_task(fm, task)
747
+ layout = layout_cache[task] ||= TaskLayout.new(task, base_time, time_to_pixel, fm)
748
+ layout.messages = messages_per_task.fetch(task, Array.new)
749
+ layout.update
750
+ layout
751
+ end
752
+
753
+ def paint_tasks(painter, fm, layout, top_y)
754
+ current_point = Integer((current_time - base_time) * time_to_pixel)
755
+ display_offset = Integer(display_point - (display_time - base_time) * time_to_pixel)
756
+ display_start_time, display_end_time = displayed_time_range
757
+ view_height = viewport.size.height
758
+
759
+ fm = Qt::FontMetrics.new(font)
760
+ text_height = fm.height
761
+ text_ascent = fm.ascent
762
+ text_descent = fm.descent
763
+
764
+ update_current_tasks
765
+ current_tasks[start_line..-1].each do |task|
766
+ break if top_y > view_height
767
+
768
+ task_layout = lay_out_task(fm, task)
769
+ add_point, start_point, end_point, finalization_point =
770
+ task_layout.add_point, task_layout.start_point, task_layout.end_point, task_layout.finalization_point
771
+ state = task_layout.state
772
+ task = task_layout.task
773
+ event_height = task_layout.event_height
774
+
775
+ task_line_height = event_height
776
+ if events = task_layout.events_in_range(display_start_time, display_end_time)
777
+ task_line_height += events.max_by { |_, _, y, _| y }[2]
778
+ end
779
+ if task_height > task_line_height
780
+ task_line_height = task_height
781
+ end
782
+
783
+ # Paint the pending stage, i.e. before the task started
784
+ top_task_line = top_y
785
+ painter.brush = TASK_BRUSHES[:pending]
786
+ painter.pen = TASK_PENS[:pending]
787
+ painter.drawRect(
788
+ add_point + display_offset, top_task_line,
789
+ (start_point || finalization_point || current_point) - add_point, task_line_height)
790
+
791
+ if start_point
792
+ painter.brush = TASK_BRUSHES[:running]
793
+ painter.pen = TASK_PENS[:running]
794
+ painter.drawRect(
795
+ start_point + display_offset, top_task_line,
796
+ (end_point || current_point) - start_point, task_line_height)
797
+
798
+ if state && state != :running
799
+ # Final state is shown by "eating" a few pixels at the task
800
+ painter.brush = TASK_BRUSHES[state]
801
+ painter.pen = TASK_PENS[state]
802
+ painter.drawRect(
803
+ end_point - 2 + display_offset, top_task_line,
804
+ 4, task_height)
805
+ end
806
+ end
807
+
808
+ # Display the emitted events
809
+ event_baseline = top_task_line + event_height / 2
810
+ if events
811
+ events.each do |_, x, y, text|
812
+ x += display_offset
813
+ y += event_baseline
814
+ painter.brush, painter.pen = EVENT_STYLES[EVENT_CONTROLABLE | EVENT_EMITTED]
815
+ painter.drawEllipse(Qt::Point.new(x, y),
816
+ EVENT_CIRCLE_RADIUS, EVENT_CIRCLE_RADIUS)
817
+ painter.pen = EVENT_NAME_PEN
818
+ painter.drawText(Qt::Point.new(x + 2 * EVENT_CIRCLE_RADIUS, y), text)
819
+ end
820
+ end
821
+
822
+ # Add the title
823
+ painter.pen = TASK_NAME_PEN
824
+ title_baseline = top_task_line + task_line_height + text_ascent
825
+ task_layout.title ||= task_timeline_title(task)
826
+ painter.drawText(Qt::Point.new(0, title_baseline), task_layout.title)
827
+
828
+ # And finally display associated messages
829
+ messages_baseline = title_baseline + text_height
830
+ painter.pen = TASK_MESSAGE_PEN
831
+ task_layout.messages.each_with_index do |msg|
832
+ messages_baseline += text_height
833
+ painter.drawText(Qt::Point.new(TASK_MESSAGE_MARGIN, messages_baseline), msg)
834
+ end
835
+
836
+ top_y = messages_baseline + text_descent
837
+ end
838
+ end
839
+
840
+ TIMELINE_GRAY_PEN = Qt::Pen.new(Qt::Color.new('gray'))
841
+ TIMELINE_BLACK_PEN = Qt::Pen.new(Qt::Color.new('black'))
842
+
843
+ def timeline_height
844
+ fm = Qt::FontMetrics.new(font)
845
+ fm.height + fm.descent + TIMELINE_RULER_LINE_LENGTH
846
+ end
847
+
848
+ def paintEvent(event)
849
+ return if !display_time
850
+
851
+ painter = Qt::Painter.new(viewport)
852
+ font = painter.font
853
+ font.point_size = 8
854
+ painter.font = font
855
+
856
+ fm = Qt::FontMetrics.new(font)
857
+ update_current_tasks
858
+ paint_timeline(painter, fm)
859
+ paint_tasks(painter, fm, task_layout, timeline_height)
860
+
861
+ # Draw the "zero" line
862
+ painter.pen = TIMELINE_GRAY_PEN
863
+ painter.drawLine(display_point, fm.height + 2, display_point, size.height)
864
+
865
+ ensure
866
+ if painter
867
+ painter.end
868
+ end
869
+ end
870
+
871
+ def task_timeline_title(task)
872
+ text = task.to_s
873
+ if job_task = all_job_info[task]
874
+ job_text = ["[#{job_task.job_id}]"]
875
+ if job_task.action_model
876
+ job_text << job_task.action_model.name.to_s
877
+ end
878
+ if job_task.action_arguments
879
+ job_text << "(" + job_task.action_arguments.map do |k,v|
880
+ "#{k} => #{v}"
881
+ end.join(", ") + ")"
882
+ end
883
+ text = "#{job_text.join(" ")} / #{text}"
884
+ end
885
+ text
886
+ end
887
+
888
+ def clear
889
+ all_tasks.clear
890
+ all_job_info.clear
891
+ end
892
+
893
+ def mouseDoubleClickEvent(event)
894
+ click_y = event.pos.y
895
+ layout = task_layout.find { |layout| layout.top_y < click_y && layout.top_y + layout.height > click_y }
896
+ if layout
897
+ if !@info_view
898
+ @info_view = ObjectInfoView.new
899
+ Qt::Object.connect(@info_view, SIGNAL('selectedTime(QDateTime)'),
900
+ self, SIGNAL('selectedTime(QDateTime)'))
901
+ end
902
+
903
+ if @info_view.display(layout.task)
904
+ @info_view.activate
905
+ end
906
+ end
907
+ event.accept
908
+ end
909
+
910
+ signals 'selectedTime(QDateTime)'
911
+
912
+ def update_scroll_ranges
913
+ vertical_scroll_bar.setRange(0, current_tasks.size - 1)
914
+ return if horizontal_scroll_bar_down?
915
+
916
+ if base_time && current_time && display_time
917
+ horizontal_scroll_bar.value = time_to_pixel * (display_time - base_time)
918
+ horizontal_scroll_bar.setRange(0, time_to_pixel * (current_time - base_time))
919
+ horizontal_scroll_bar.setPageStep(size.width / 4)
920
+ end
921
+ end
922
+ end
923
+ end
924
+ end
925
+