roby 0.7

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 (240) hide show
  1. data/.gitignore +29 -0
  2. data/History.txt +4 -0
  3. data/License-fr.txt +519 -0
  4. data/License.txt +515 -0
  5. data/Manifest.txt +245 -0
  6. data/NOTES +4 -0
  7. data/README.txt +163 -0
  8. data/Rakefile +161 -0
  9. data/TODO.txt +146 -0
  10. data/app/README.txt +24 -0
  11. data/app/Rakefile +8 -0
  12. data/app/config/ROBOT.rb +5 -0
  13. data/app/config/app.yml +91 -0
  14. data/app/config/init.rb +7 -0
  15. data/app/config/roby.yml +3 -0
  16. data/app/controllers/.gitattributes +0 -0
  17. data/app/controllers/ROBOT.rb +2 -0
  18. data/app/data/.gitattributes +0 -0
  19. data/app/planners/ROBOT/main.rb +6 -0
  20. data/app/planners/main.rb +5 -0
  21. data/app/scripts/distributed +3 -0
  22. data/app/scripts/generate/bookmarks +3 -0
  23. data/app/scripts/replay +3 -0
  24. data/app/scripts/results +3 -0
  25. data/app/scripts/run +3 -0
  26. data/app/scripts/server +3 -0
  27. data/app/scripts/shell +3 -0
  28. data/app/scripts/test +3 -0
  29. data/app/tasks/.gitattributes +0 -0
  30. data/app/tasks/ROBOT/.gitattributes +0 -0
  31. data/bin/roby +210 -0
  32. data/bin/roby-log +168 -0
  33. data/bin/roby-shell +25 -0
  34. data/doc/images/event_generalization.png +0 -0
  35. data/doc/images/exception_propagation_1.png +0 -0
  36. data/doc/images/exception_propagation_2.png +0 -0
  37. data/doc/images/exception_propagation_3.png +0 -0
  38. data/doc/images/exception_propagation_4.png +0 -0
  39. data/doc/images/exception_propagation_5.png +0 -0
  40. data/doc/images/replay_handler_error.png +0 -0
  41. data/doc/images/replay_handler_error_0.png +0 -0
  42. data/doc/images/replay_handler_error_1.png +0 -0
  43. data/doc/images/roby_cycle_overview.png +0 -0
  44. data/doc/images/roby_replay_02.png +0 -0
  45. data/doc/images/roby_replay_03.png +0 -0
  46. data/doc/images/roby_replay_04.png +0 -0
  47. data/doc/images/roby_replay_event_representation.png +0 -0
  48. data/doc/images/roby_replay_first_state.png +0 -0
  49. data/doc/images/roby_replay_relations.png +0 -0
  50. data/doc/images/roby_replay_startup.png +0 -0
  51. data/doc/images/task_event_generalization.png +0 -0
  52. data/doc/papers.rdoc +11 -0
  53. data/doc/styles/allison.css +314 -0
  54. data/doc/styles/allison.js +316 -0
  55. data/doc/styles/allison.rb +276 -0
  56. data/doc/styles/jamis.rb +593 -0
  57. data/doc/tutorials/01-GettingStarted.rdoc +86 -0
  58. data/doc/tutorials/02-GoForward.rdoc +220 -0
  59. data/doc/tutorials/03-PlannedPath.rdoc +268 -0
  60. data/doc/tutorials/04-EventPropagation.rdoc +236 -0
  61. data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
  62. data/doc/tutorials/06-Overview.rdoc +40 -0
  63. data/doc/videos.rdoc +69 -0
  64. data/ext/droby/dump.cc +175 -0
  65. data/ext/droby/extconf.rb +3 -0
  66. data/ext/graph/algorithm.cc +746 -0
  67. data/ext/graph/extconf.rb +7 -0
  68. data/ext/graph/graph.cc +529 -0
  69. data/ext/graph/graph.hh +183 -0
  70. data/ext/graph/iterator_sequence.hh +102 -0
  71. data/ext/graph/undirected_dfs.hh +226 -0
  72. data/ext/graph/undirected_graph.hh +421 -0
  73. data/lib/roby.rb +41 -0
  74. data/lib/roby/app.rb +870 -0
  75. data/lib/roby/app/rake.rb +56 -0
  76. data/lib/roby/app/run.rb +14 -0
  77. data/lib/roby/app/scripts/distributed.rb +13 -0
  78. data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
  79. data/lib/roby/app/scripts/replay.rb +31 -0
  80. data/lib/roby/app/scripts/results.rb +15 -0
  81. data/lib/roby/app/scripts/run.rb +26 -0
  82. data/lib/roby/app/scripts/server.rb +18 -0
  83. data/lib/roby/app/scripts/shell.rb +88 -0
  84. data/lib/roby/app/scripts/test.rb +40 -0
  85. data/lib/roby/basic_object.rb +151 -0
  86. data/lib/roby/config.rb +5 -0
  87. data/lib/roby/control.rb +747 -0
  88. data/lib/roby/decision_control.rb +17 -0
  89. data/lib/roby/distributed.rb +32 -0
  90. data/lib/roby/distributed/base.rb +440 -0
  91. data/lib/roby/distributed/communication.rb +871 -0
  92. data/lib/roby/distributed/connection_space.rb +592 -0
  93. data/lib/roby/distributed/distributed_object.rb +206 -0
  94. data/lib/roby/distributed/drb.rb +62 -0
  95. data/lib/roby/distributed/notifications.rb +539 -0
  96. data/lib/roby/distributed/peer.rb +550 -0
  97. data/lib/roby/distributed/protocol.rb +529 -0
  98. data/lib/roby/distributed/proxy.rb +343 -0
  99. data/lib/roby/distributed/subscription.rb +311 -0
  100. data/lib/roby/distributed/transaction.rb +498 -0
  101. data/lib/roby/event.rb +897 -0
  102. data/lib/roby/exceptions.rb +234 -0
  103. data/lib/roby/executives/simple.rb +30 -0
  104. data/lib/roby/graph.rb +166 -0
  105. data/lib/roby/interface.rb +390 -0
  106. data/lib/roby/log.rb +3 -0
  107. data/lib/roby/log/chronicle.rb +303 -0
  108. data/lib/roby/log/console.rb +72 -0
  109. data/lib/roby/log/data_stream.rb +197 -0
  110. data/lib/roby/log/dot.rb +279 -0
  111. data/lib/roby/log/event_stream.rb +151 -0
  112. data/lib/roby/log/file.rb +340 -0
  113. data/lib/roby/log/gui/basic_display.ui +83 -0
  114. data/lib/roby/log/gui/chronicle.rb +26 -0
  115. data/lib/roby/log/gui/chronicle_view.rb +40 -0
  116. data/lib/roby/log/gui/chronicle_view.ui +70 -0
  117. data/lib/roby/log/gui/data_displays.rb +172 -0
  118. data/lib/roby/log/gui/data_displays.ui +155 -0
  119. data/lib/roby/log/gui/notifications.rb +26 -0
  120. data/lib/roby/log/gui/relations.rb +248 -0
  121. data/lib/roby/log/gui/relations.ui +123 -0
  122. data/lib/roby/log/gui/relations_view.rb +185 -0
  123. data/lib/roby/log/gui/relations_view.ui +149 -0
  124. data/lib/roby/log/gui/replay.rb +327 -0
  125. data/lib/roby/log/gui/replay_controls.rb +200 -0
  126. data/lib/roby/log/gui/replay_controls.ui +259 -0
  127. data/lib/roby/log/gui/runtime.rb +130 -0
  128. data/lib/roby/log/hooks.rb +185 -0
  129. data/lib/roby/log/logger.rb +202 -0
  130. data/lib/roby/log/notifications.rb +244 -0
  131. data/lib/roby/log/plan_rebuilder.rb +470 -0
  132. data/lib/roby/log/relations.rb +1056 -0
  133. data/lib/roby/log/server.rb +550 -0
  134. data/lib/roby/log/sqlite.rb +47 -0
  135. data/lib/roby/log/timings.rb +164 -0
  136. data/lib/roby/plan-object.rb +247 -0
  137. data/lib/roby/plan.rb +762 -0
  138. data/lib/roby/planning.rb +13 -0
  139. data/lib/roby/planning/loops.rb +302 -0
  140. data/lib/roby/planning/model.rb +906 -0
  141. data/lib/roby/planning/task.rb +151 -0
  142. data/lib/roby/propagation.rb +562 -0
  143. data/lib/roby/query.rb +619 -0
  144. data/lib/roby/relations.rb +583 -0
  145. data/lib/roby/relations/conflicts.rb +70 -0
  146. data/lib/roby/relations/ensured.rb +20 -0
  147. data/lib/roby/relations/error_handling.rb +23 -0
  148. data/lib/roby/relations/events.rb +9 -0
  149. data/lib/roby/relations/executed_by.rb +193 -0
  150. data/lib/roby/relations/hierarchy.rb +239 -0
  151. data/lib/roby/relations/influence.rb +10 -0
  152. data/lib/roby/relations/planned_by.rb +63 -0
  153. data/lib/roby/robot.rb +7 -0
  154. data/lib/roby/standard_errors.rb +218 -0
  155. data/lib/roby/state.rb +5 -0
  156. data/lib/roby/state/events.rb +221 -0
  157. data/lib/roby/state/information.rb +55 -0
  158. data/lib/roby/state/pos.rb +110 -0
  159. data/lib/roby/state/shapes.rb +32 -0
  160. data/lib/roby/state/state.rb +353 -0
  161. data/lib/roby/support.rb +92 -0
  162. data/lib/roby/task-operations.rb +182 -0
  163. data/lib/roby/task.rb +1618 -0
  164. data/lib/roby/test/common.rb +399 -0
  165. data/lib/roby/test/distributed.rb +214 -0
  166. data/lib/roby/test/tasks/empty_task.rb +9 -0
  167. data/lib/roby/test/tasks/goto.rb +36 -0
  168. data/lib/roby/test/tasks/simple_task.rb +23 -0
  169. data/lib/roby/test/testcase.rb +519 -0
  170. data/lib/roby/test/tools.rb +160 -0
  171. data/lib/roby/thread_task.rb +87 -0
  172. data/lib/roby/transactions.rb +462 -0
  173. data/lib/roby/transactions/proxy.rb +292 -0
  174. data/lib/roby/transactions/updates.rb +139 -0
  175. data/plugins/fault_injection/History.txt +4 -0
  176. data/plugins/fault_injection/README.txt +37 -0
  177. data/plugins/fault_injection/Rakefile +18 -0
  178. data/plugins/fault_injection/TODO.txt +0 -0
  179. data/plugins/fault_injection/app.rb +52 -0
  180. data/plugins/fault_injection/fault_injection.rb +89 -0
  181. data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
  182. data/plugins/subsystems/README.txt +40 -0
  183. data/plugins/subsystems/Rakefile +18 -0
  184. data/plugins/subsystems/app.rb +171 -0
  185. data/plugins/subsystems/test/app/README +24 -0
  186. data/plugins/subsystems/test/app/Rakefile +8 -0
  187. data/plugins/subsystems/test/app/config/app.yml +71 -0
  188. data/plugins/subsystems/test/app/config/init.rb +9 -0
  189. data/plugins/subsystems/test/app/config/roby.yml +3 -0
  190. data/plugins/subsystems/test/app/planners/main.rb +20 -0
  191. data/plugins/subsystems/test/app/scripts/distributed +3 -0
  192. data/plugins/subsystems/test/app/scripts/replay +3 -0
  193. data/plugins/subsystems/test/app/scripts/results +3 -0
  194. data/plugins/subsystems/test/app/scripts/run +3 -0
  195. data/plugins/subsystems/test/app/scripts/server +3 -0
  196. data/plugins/subsystems/test/app/scripts/shell +3 -0
  197. data/plugins/subsystems/test/app/scripts/test +3 -0
  198. data/plugins/subsystems/test/app/tasks/services.rb +15 -0
  199. data/plugins/subsystems/test/test_subsystems.rb +71 -0
  200. data/test/distributed/test_communication.rb +178 -0
  201. data/test/distributed/test_connection.rb +282 -0
  202. data/test/distributed/test_execution.rb +373 -0
  203. data/test/distributed/test_mixed_plan.rb +341 -0
  204. data/test/distributed/test_plan_notifications.rb +238 -0
  205. data/test/distributed/test_protocol.rb +516 -0
  206. data/test/distributed/test_query.rb +102 -0
  207. data/test/distributed/test_remote_plan.rb +491 -0
  208. data/test/distributed/test_transaction.rb +463 -0
  209. data/test/mockups/tasks.rb +27 -0
  210. data/test/planning/test_loops.rb +380 -0
  211. data/test/planning/test_model.rb +427 -0
  212. data/test/planning/test_task.rb +106 -0
  213. data/test/relations/test_conflicts.rb +42 -0
  214. data/test/relations/test_ensured.rb +38 -0
  215. data/test/relations/test_executed_by.rb +149 -0
  216. data/test/relations/test_hierarchy.rb +158 -0
  217. data/test/relations/test_planned_by.rb +54 -0
  218. data/test/suite_core.rb +24 -0
  219. data/test/suite_distributed.rb +9 -0
  220. data/test/suite_planning.rb +3 -0
  221. data/test/suite_relations.rb +8 -0
  222. data/test/test_bgl.rb +508 -0
  223. data/test/test_control.rb +399 -0
  224. data/test/test_event.rb +894 -0
  225. data/test/test_exceptions.rb +592 -0
  226. data/test/test_interface.rb +37 -0
  227. data/test/test_log.rb +114 -0
  228. data/test/test_log_server.rb +132 -0
  229. data/test/test_plan.rb +584 -0
  230. data/test/test_propagation.rb +210 -0
  231. data/test/test_query.rb +266 -0
  232. data/test/test_relations.rb +180 -0
  233. data/test/test_state.rb +414 -0
  234. data/test/test_support.rb +16 -0
  235. data/test/test_task.rb +938 -0
  236. data/test/test_testcase.rb +122 -0
  237. data/test/test_thread_task.rb +73 -0
  238. data/test/test_transactions.rb +569 -0
  239. data/test/test_transactions_proxy.rb +198 -0
  240. metadata +570 -0
@@ -0,0 +1,1056 @@
1
+ require 'Qt4'
2
+ require 'utilrb/module/attr_predicate'
3
+ require 'roby/distributed/protocol'
4
+ require 'roby/log/dot'
5
+ require 'roby/log/plan_rebuilder'
6
+ require 'roby/log/gui/relations_view'
7
+
8
+ module Roby
9
+ class PlanObject::DRoby
10
+ def display_parent; end
11
+ def display_create(display); end
12
+ def display_events; ValueSet.new end
13
+ def display_name(display); remote_name end
14
+ def display(display, graphics_item)
15
+ end
16
+ end
17
+
18
+ module EventGeneratorDisplay
19
+ def self.style(object, flags)
20
+ # This is for backward compatibility only. All events are now marshalled
21
+ # with their controllability.
22
+ if !object.controlable.nil?
23
+ flags |= (object.controlable ? Log::EVENT_CONTROLABLE : Log::EVENT_CONTINGENT)
24
+ elsif (flags & Log::EVENT_CALLED) != 0
25
+ flags |= Log::EVENT_CONTROLABLE
26
+ end
27
+
28
+ if !styles.has_key?(flags)
29
+ raise ArgumentError, "event flags #{flags} have not style"
30
+ end
31
+
32
+ styles[flags]
33
+ end
34
+
35
+ def self.styles
36
+ if defined? @@event_styles
37
+ return @@event_styles
38
+ end
39
+
40
+ @@event_styles = Hash.new
41
+ @@event_styles[Log::EVENT_CONTROLABLE | Log::EVENT_CALLED] =
42
+ [Qt::Brush.new(Qt::Color.new(Log::PENDING_EVENT_COLOR)),
43
+ Qt::Pen.new(Qt::Color.new(Log::PENDING_EVENT_COLOR))]
44
+ @@event_styles[Log::EVENT_CONTROLABLE | Log::EVENT_EMITTED] =
45
+ [Qt::Brush.new(Qt::Color.new(Log::FIRED_EVENT_COLOR)),
46
+ Qt::Pen.new(Qt::Color.new(Log::FIRED_EVENT_COLOR))]
47
+ @@event_styles[Log::EVENT_CONTROLABLE | Log::EVENT_CALLED_AND_EMITTED] =
48
+ [Qt::Brush.new(Qt::Color.new(Log::FIRED_EVENT_COLOR)),
49
+ Qt::Pen.new(Qt::Color.new(Log::PENDING_EVENT_COLOR))]
50
+ @@event_styles[Log::EVENT_CONTINGENT | Log::EVENT_EMITTED] =
51
+ [Qt::Brush.new(Qt::Color.new('white')), Qt::Pen.new(Qt::Color.new(Log::FIRED_EVENT_COLOR))]
52
+ @@event_styles
53
+ end
54
+
55
+ def self.priorities
56
+ @@priorities ||= Hash.new
57
+ end
58
+
59
+ def display_create(display)
60
+ scene = display.scene
61
+ circle = scene.add_ellipse(-Log::EVENT_CIRCLE_RADIUS, -Log::EVENT_CIRCLE_RADIUS, Log::EVENT_CIRCLE_RADIUS * 2, Log::EVENT_CIRCLE_RADIUS * 2)
62
+ text = scene.add_text(display_name(display))
63
+ circle.singleton_class.class_eval { attr_accessor :text }
64
+ circle.z_value = Log::EVENT_LAYER
65
+
66
+ text.parent_item = circle
67
+ text_width = text.bounding_rect.width
68
+ text.set_pos(-text_width / 2, 0)
69
+ circle.text = text
70
+ circle
71
+ end
72
+
73
+ def display_time_start(circle, pos); circle.translate(pos) end
74
+ def display_time_end(circle, pos); end
75
+ end
76
+
77
+ class EventGenerator::DRoby
78
+ include EventGeneratorDisplay
79
+
80
+ def display_name(display)
81
+ name = if model.ancestors[0][0] != 'Roby::EventGenerator'
82
+ [display.filter_prefixes(model.ancestors[0][0].dup)]
83
+ else
84
+ []
85
+ end
86
+
87
+ if display.show_ownership
88
+ name << owners_to_s
89
+ end
90
+ name.join("\n")
91
+ end
92
+
93
+ def display(display, graphics_item)
94
+ graphics_item.text.plain_text = display_name(display).to_s
95
+ end
96
+ end
97
+
98
+ class TaskEventGenerator::DRoby
99
+ include EventGeneratorDisplay
100
+ def display_parent; task end
101
+ def display_name(display); symbol.to_s end
102
+
103
+ def display(display, graphics_item)
104
+ end
105
+ end
106
+
107
+ module LoggedTask
108
+ def layout_events(display)
109
+ graphics_item = display[self]
110
+
111
+ width, height = 0, 0
112
+ events = self.events.map do |_, e|
113
+ next unless display.displayed?(e)
114
+ next unless circle = display[e]
115
+ br = (circle.bounding_rect | circle.children_bounding_rect)
116
+ [e, circle, br]
117
+ end
118
+ events.compact!
119
+ events = events.sort_by { |ev, _| EventGeneratorDisplay.priorities[ev] }
120
+
121
+ events.each do |_, circle, br|
122
+ w, h = br.width, br.height
123
+ height = h if h > height
124
+ width += w
125
+ end
126
+ width += Log::TASK_EVENT_SPACING * (events.size + 1)
127
+ height += Log::TASK_EVENT_SPACING
128
+
129
+ x = -width / 2 + Log::TASK_EVENT_SPACING
130
+ events.each do |e, circle, br|
131
+ w = br.width
132
+ circle.set_pos(x + w / 2, -br.height / 2 + Log::EVENT_CIRCLE_RADIUS + Log::TASK_EVENT_SPACING)
133
+ x += w + Log::TASK_EVENT_SPACING
134
+ end
135
+
136
+ width = Log::DEFAULT_TASK_WIDTH unless width > Log::DEFAULT_TASK_WIDTH
137
+ height = Log::DEFAULT_TASK_HEIGHT unless height > Log::DEFAULT_TASK_HEIGHT
138
+
139
+ if @width != width || @height != height
140
+ @width, @height = width, height
141
+ coords = Qt::RectF.new -(width / 2), -(height / 2), width, height
142
+ graphics_item.rect = coords
143
+ end
144
+
145
+ text = graphics_item.text
146
+ text.set_pos(- text.bounding_rect.width / 2, height / 2 + Log::TASK_EVENT_SPACING)
147
+ end
148
+
149
+ def display_create(display)
150
+ scene = display.scene
151
+ rect = scene.add_rect Qt::RectF.new(0, 0, 0, 0)
152
+ text = scene.add_text display_name(display)
153
+ rect.brush = Qt::Brush.new(Log::TASK_BRUSH_COLORS[:pending])
154
+ rect.pen = Qt::Pen.new(Log::TASK_PEN_COLORS[:pending])
155
+ @displayed_state = :pending
156
+ text.parent_item = rect
157
+ rect.singleton_class.class_eval { attr_accessor :text }
158
+ rect.text = text
159
+ rect.z_value = Log::TASK_LAYER
160
+
161
+ rect.set_data(0, Qt::Variant.new(self.object_id.to_s))
162
+ rect
163
+ end
164
+
165
+ def display_time_start(rect, pos); rect.left = pos end
166
+ def display_time_end(rect, pos); rect.right = pos end
167
+ end
168
+
169
+ class Task::DRoby
170
+ include LoggedTask
171
+ attr_accessor :last_event
172
+
173
+ def display_name(display)
174
+ name = display.filter_prefixes(model.ancestors[0][0].dup)
175
+ if display.show_ownership
176
+ name << "\n#{owners_to_s}"
177
+ end
178
+ name
179
+ end
180
+
181
+ def current_state
182
+ new_state = if plan && plan.finalized_tasks.include?(self)
183
+ :finalized
184
+ else
185
+ [:success, :finished, :started, :pending].
186
+ find { |flag| flags[flag] }
187
+ end
188
+ new_state || :pending
189
+ end
190
+
191
+ attr_reader :displayed_state
192
+ def update_graphics(display, graphics_item)
193
+ new_state = current_state
194
+ if displayed_state != new_state
195
+ graphics_item.brush = Qt::Brush.new(Log::TASK_BRUSH_COLORS[new_state])
196
+ graphics_item.pen = Qt::Pen.new(Log::TASK_PEN_COLORS[new_state])
197
+ displayed_state = new_state
198
+ end
199
+
200
+ graphics_item.text.plain_text = display_name(display).to_s
201
+
202
+ end
203
+
204
+ def display(display, graphics_item)
205
+ update_graphics(display, graphics_item)
206
+ super
207
+ layout_events(display)
208
+ end
209
+ end
210
+
211
+ class Transaction::Proxy::DRoby
212
+ include LoggedTask
213
+
214
+ attr_writer :real_object
215
+ def flags; real_object.flags end
216
+
217
+ def display_parent; end
218
+ def display_name(display); real_object.display_name(display) end
219
+ def display_create(display)
220
+ scene = display.scene
221
+ item = super
222
+
223
+ brush = item.brush
224
+ brush.style = Qt::BDiagPattern
225
+ item.brush = brush
226
+ item
227
+ end
228
+ def display(display, graphics_item)
229
+ graphics_item.text.plain_text = display_name(display).to_s
230
+ layout_events(display)
231
+ end
232
+ end
233
+
234
+ module LoggedPlan
235
+ PLAN_STROKE_WIDTH = 5
236
+ # The plan depth, i.e. its distance from the root plan
237
+ attr_reader :depth
238
+ # The max depth of the plan tree in this branch
239
+ attr_reader :max_depth
240
+
241
+ def display_create(display)
242
+ scene = display.scene
243
+ pen = Qt::Pen.new
244
+ pen.width = PLAN_STROKE_WIDTH
245
+ pen.style = Qt::SolidLine
246
+ pen.cap_style = Qt::SquareCap
247
+ pen.join_style = Qt::RoundJoin
248
+ scene.add_rect Qt::RectF.new(0, 0, 0, 0), pen
249
+ end
250
+ def display_parent; parent_plan end
251
+ def display(display, item)
252
+ #STDERR.puts "DISPLAYING PLAN\n #{caller.join("\n ")}"
253
+ end
254
+ end
255
+
256
+ module Log
257
+ EVENT_CIRCLE_RADIUS = 3
258
+ TASK_EVENT_SPACING = 5
259
+ DEFAULT_TASK_WIDTH = 20
260
+ DEFAULT_TASK_HEIGHT = 10
261
+ ARROW_COLOR = Qt::Color.new('black')
262
+ ARROW_OPENING = 30
263
+ ARROW_SIZE = 10
264
+
265
+ PROPAG_SIGNAL = 1
266
+ PROPAG_FORWARD = 2
267
+ PROPAG_CALLING = 3
268
+ PROPAG_EMITTING = 4
269
+
270
+ EVENT_CONTINGENT = 0
271
+ EVENT_CONTROLABLE = 1
272
+ EVENT_CALLED = 2
273
+ EVENT_EMITTED = 4
274
+ EVENT_CALLED_AND_EMITTED = EVENT_CALLED | EVENT_EMITTED
275
+
276
+ TASK_BRUSH_COLORS = {
277
+ :pending => Qt::Color.new('#6DF3FF'),
278
+ :started => Qt::Color.new('#B0FFA6'),
279
+ :success => Qt::Color.new('#E2E2E2'),
280
+ :finished => Qt::Color.new('#E2A8A8'),
281
+ :finalized => Qt::Color.new('#555555')
282
+ }
283
+ TASK_PEN_COLORS = {
284
+ :pending => Qt::Color.new('#6DF3FF'),
285
+ :started => Qt::Color.new('#B0FFA6'),
286
+ :success => Qt::Color.new('#E2E2E2'),
287
+ :finished => Qt::Color.new('#E2A8A8'),
288
+ :finalized => Qt::Color.new('#555555')
289
+ }
290
+ TASK_NAME_COLOR = 'black'
291
+ TASK_FONTSIZE = 10
292
+
293
+ PENDING_EVENT_COLOR = 'black' # default color for events
294
+ FIRED_EVENT_COLOR = 'red'
295
+ EVENT_FONTSIZE = 8
296
+
297
+ PLAN_LAYER = 0
298
+ TASK_LAYER = PLAN_LAYER + 20
299
+ EVENT_LAYER = PLAN_LAYER + 30
300
+
301
+ FIND_MARGIN = 10
302
+
303
+ class Qt::GraphicsScene
304
+ attr_reader :default_arrow_pen
305
+ attr_reader :default_arrow_brush
306
+ def add_arrow(size, pen = nil, brush = nil)
307
+ @default_arrow_pen ||= Qt::Pen.new(ARROW_COLOR)
308
+ @default_arrow_brush ||= Qt::Brush.new(ARROW_COLOR)
309
+
310
+ @arrow_points ||= (1..4).map { Qt::PointF.new(0, 0) }
311
+ @arrow_points[1].x = -size
312
+ @arrow_points[1].y = size / 2
313
+ @arrow_points[2].x = -size
314
+ @arrow_points[2].y = -size / 2
315
+ polygon = Qt::PolygonF.new(@arrow_points)
316
+ @arrow_line ||= Qt::LineF.new(-1, 0, 0, 0)
317
+
318
+ ending = add_polygon polygon, (pen || default_arrow_pen), (brush || default_arrow_brush)
319
+ line = add_line @arrow_line
320
+
321
+ line.parent_item = ending
322
+ ending.singleton_class.class_eval { attr_accessor :line }
323
+ ending.line = line
324
+ ending
325
+ end
326
+ end
327
+
328
+ def self.intersect_rect(w, h, from, to)
329
+ to_x, to_y = *to
330
+ from_x, from_y = *from
331
+
332
+ # We only use half dimensions since 'to' is supposed to be be the
333
+ # center of the rectangle we are intersecting
334
+ w /= 2
335
+ h /= 2
336
+
337
+ dx = (to_x - from_x)
338
+ dy = (to_y - from_y)
339
+ delta_x = dx / dy * h
340
+ if dy != 0 && delta_x.abs < w
341
+ if dy > 0
342
+ [to_x - delta_x, to_y - h]
343
+ else
344
+ [to_x + delta_x, to_y + h]
345
+ end
346
+ elsif dx != 0
347
+ delta_y = dy / dx * w
348
+ if dx > 0
349
+ [to_x - w, to_y - delta_y]
350
+ else
351
+ [to_x + w, to_y + delta_y]
352
+ end
353
+ else
354
+ [0, 0]
355
+ end
356
+ end
357
+
358
+ def self.correct_line(from, to, rect)
359
+ intersect_rect(rect.width, rect.height, from, to)
360
+ end
361
+
362
+ def self.arrow_set(arrow, start_object, end_object)
363
+ start_br = start_object.scene_bounding_rect
364
+ end_br = end_object.scene_bounding_rect
365
+ start_point = start_br.center
366
+ end_point = end_br.center
367
+
368
+ #from = intersect_rect(start_br.width, start_br.height, end_point, start_point)
369
+ from = [start_point.x, start_point.y]
370
+ to = intersect_rect(end_br.width, end_br.height, from, [end_point.x, end_point.y])
371
+
372
+ dy = to[1] - from[1]
373
+ dx = to[0] - from[0]
374
+ alpha = Math.atan2(dy, dx)
375
+ length = Math.sqrt(dx ** 2 + dy ** 2)
376
+
377
+ #arrow.line.set_line from[0], from[1], to[0], to[1]
378
+ arrow.resetMatrix
379
+ arrow.line.set_line(-length, 0, 0, 0)
380
+ arrow.translate to[0], to[1]
381
+ arrow.rotate(alpha * 180 / Math::PI)
382
+ end
383
+
384
+ module TaskDisplaySupport
385
+ # A regex => boolean map of prefixes that should be removed from
386
+ # the task names
387
+ attribute :removed_prefixes do
388
+ { "Roby::" => false,
389
+ "Roby::Genom::" => false }
390
+ end
391
+
392
+ # Compute the prefixes to remove from in filter_prefixes:
393
+ # enable only the ones that are flagged, and sort them by
394
+ # prefix length
395
+ def update_prefixes_removal
396
+ @prefixes_removal = removed_prefixes.find_all { |p, b| b }.
397
+ map { |p, b| p }.
398
+ sort_by { |p| p.length }.
399
+ reverse
400
+ end
401
+
402
+ def filter_prefixes(string)
403
+ # @prefixes_removal is computed in RelationsDisplay#update
404
+ for prefix in @prefixes_removal
405
+ string = string.gsub(prefix, '')
406
+ end
407
+ string
408
+ end
409
+
410
+ # If true, show the ownership in the task descriptions
411
+ attribute(:show_ownership) { true }
412
+ # If true, show the arguments in the task descriptions
413
+ attribute(:show_arguments) { false }
414
+ end
415
+
416
+ class RelationsDisplay < Qt::Object
417
+ include DataDisplay
418
+ decoder PlanRebuilder
419
+
420
+ include TaskDisplaySupport
421
+
422
+ attr_reader :ui, :scene
423
+
424
+ # A [DRbObject, DRbObject] => GraphicsItem mapping of arrows
425
+ attr_reader :arrows
426
+
427
+ # A DRbObject => GraphicsItem mapping
428
+ attr_reader :graphics
429
+
430
+ # The set of objects that are to be shown permanently
431
+ attr_reader :visible_objects
432
+
433
+ # A set of events that are shown during only two calls of #update
434
+ attr_reader :flashing_objects
435
+
436
+ # The set of signals since the last call to #update
437
+ # Each element is [flag, from, to, event_id]
438
+ attr_reader :propagated_events
439
+
440
+ # The array of events for which a command has been called, or which
441
+ # have been emitted. The order in this array is the arrival order
442
+ # of the corresponding events.
443
+ #
444
+ # An array element is [fired, event], when fired is true if the
445
+ # event has been fired, and false if it is pending
446
+ attr_reader :execution_events
447
+
448
+ # The set of postponed events that have occured since the last call
449
+ # to #update. Each element is [postponed_generator,
450
+ # until_generator]
451
+ attr_reader :postponed_events
452
+
453
+ # A pool of arrows items used to display the event signalling
454
+ attr_reader :signal_arrows
455
+
456
+ # True if the finalized tasks should not be displayed
457
+ attr_accessor :hide_finalized
458
+
459
+ def initialize
460
+ @scene = Qt::GraphicsScene.new
461
+ super()
462
+
463
+ @main = Qt::MainWindow.new
464
+ @ui = Ui::RelationsView.new
465
+
466
+ @graphics = Hash.new
467
+ @visible_objects = ValueSet.new
468
+ @flashing_objects = Hash.new
469
+ @arrows = Hash.new
470
+ @enabled_relations = Set.new
471
+ @layout_relations = Set.new
472
+ @relation_colors = Hash.new
473
+ @relation_pens = Hash.new(Qt::Pen.new(Qt::Color.new(ARROW_COLOR)))
474
+ @relation_brushes = Hash.new(Qt::Brush.new(Qt::Color.new(ARROW_COLOR)))
475
+ @current_color = 0
476
+
477
+ @propagated_events = []
478
+ @execution_events = []
479
+ @postponed_events = ValueSet.new
480
+ @signal_arrows = []
481
+ @hide_finalized = true
482
+
483
+ ui.setupUi(self)
484
+ ui.graphics.scene = scene
485
+
486
+ default_colors = {
487
+ Roby::TaskStructure::Hierarchy => 'grey',
488
+ Roby::TaskStructure::PlannedBy => '#32ba21',
489
+ Roby::TaskStructure::ExecutionAgent => '#5d95cf',
490
+ Roby::TaskStructure::ErrorHandling => '#ff2727'
491
+ }
492
+ default_colors.each do |rel, color|
493
+ update_relation_color(rel, color)
494
+ end
495
+
496
+ @shortcuts = []
497
+ shortcut = Qt::Shortcut.new(Qt::KeySequence.new('f'), main)
498
+ connect(shortcut, SIGNAL('activated()'), self, SLOT('find()'))
499
+ @shortcuts << shortcut
500
+ main.resize 500, 500
501
+ end
502
+
503
+ def object_of(item)
504
+ return if !(id = item.data(0).to_int).valid?
505
+ id = id.to_int
506
+
507
+ obj, _ = graphics.find do |obj, obj_item|
508
+ obj.object_id == id
509
+ end
510
+ obj
511
+ end
512
+
513
+ def stream=(data_stream)
514
+ super
515
+
516
+ # Initialize the display ...
517
+ decoder.plans.each do |plan|
518
+ discovered_tasks(Time.now, plan, plan.known_tasks)
519
+ discovered_events(Time.now, plan, plan.free_events)
520
+ end
521
+ display
522
+ end
523
+
524
+ def [](item); graphics[item] end
525
+ def task_relation(from, to, rel, info)
526
+ arrow(from, to, rel, info, TASK_LAYER)
527
+ end
528
+ def event_relation(form, to, rel, info)
529
+ arrow(from, to, rel, info, EVENT_LAYER)
530
+ end
531
+
532
+ def arrow(from, to, rel, info, base_layer)
533
+ id = [from, to, rel]
534
+ unless item = arrows[id]
535
+ item = (arrows[id] ||= scene.add_arrow(ARROW_SIZE))
536
+ item.z_value = base_layer - 1
537
+ item.pen = item.line.pen = relation_pens[rel]
538
+ item.brush = relation_brushes[rel]
539
+ end
540
+ Log.arrow_set item, self[from], self[to]
541
+ end
542
+
543
+ # Centers the view on the set of object found which matches
544
+ # +regex+. If +regex+ is nil, ask one to the user
545
+ def find(regex = nil)
546
+ unless regex
547
+ regex = Qt::InputDialog.get_text main, 'Find objects in relation view', 'Object name'
548
+ return unless regex && !regex.empty?
549
+ end
550
+ regex = /#{regex.to_str}/i if regex.respond_to?(:to_str)
551
+
552
+ # Get the tasks and events matching the string
553
+ objects = []
554
+ for p in decoder.plans
555
+ objects.concat p.known_tasks.
556
+ find_all { |object| displayed?(object) && regex === object.display_name(self) }
557
+ objects.concat p.free_events.
558
+ find_all { |object| displayed?(object) && regex === object.display_name(self) }
559
+ end
560
+
561
+ return if objects.empty?
562
+
563
+ # Find the graphics items
564
+ bb = objects.inject(Qt::RectF.new) do |bb, object|
565
+ if item = self[object]
566
+ item.selected = true
567
+ bb | item.scene_bounding_rect | item.map_to_scene(item.children_bounding_rect).bounding_rect
568
+ else
569
+ bb
570
+ end
571
+ end
572
+ bb.adjust -FIND_MARGIN, -FIND_MARGIN, FIND_MARGIN, FIND_MARGIN
573
+ ui.graphics.fit_in_view bb, Qt::KeepAspectRatio
574
+ scale = ui.graphics.matrix.m11
575
+ if scale > 1
576
+ ui.graphics.resetMatrix
577
+ ui.graphics.scale 1, 1
578
+ end
579
+ end
580
+ slots 'find()'
581
+
582
+ attr_accessor :keep_signals
583
+
584
+ COLORS = %w{'black' #800000 #008000 #000080 #C05800 #6633FF #CDBE70 #CD8162 #A2B5CD}
585
+ attr_reader :current_color
586
+ # returns the next color in COLORS, cycles if at the end of the array
587
+ def allocate_color
588
+ @current_color = (current_color + 1) % COLORS.size
589
+ COLORS[current_color]
590
+ end
591
+
592
+ def relation_enabled?(relation); @enabled_relations.include?(relation) end
593
+ def layout_relation?(relation); relation_enabled?(relation) || @layout_relations.include?(relation) end
594
+
595
+ def enable_relation(relation)
596
+ return if relation_enabled?(relation)
597
+ @enabled_relations << relation
598
+ arrows.each do |(_, _, rel), arrow|
599
+ if rel == relation
600
+ arrow.visible = true
601
+ end
602
+ end
603
+ end
604
+
605
+ attr_reader :enabled_relations
606
+ def layout_relation(relation)
607
+ disable_relation(relation)
608
+ @layout_relations << relation
609
+ end
610
+ def ignore_relation(relation)
611
+ disable_relation(relation)
612
+ @layout_relations.delete(relation)
613
+ end
614
+
615
+ def disable_relation(relation)
616
+ return unless relation_enabled?(relation)
617
+ @enabled_relations.delete(relation)
618
+ arrows.each do |(_, _, rel), arrow|
619
+ if rel == relation
620
+ arrow.visible = false
621
+ end
622
+ end
623
+ end
624
+
625
+ attr_reader :relation_colors
626
+ attr_reader :relation_pens
627
+ attr_reader :relation_brushes
628
+ def relation_color(relation)
629
+ if !relation_colors.has_key?(relation)
630
+ update_relation_color(relation, allocate_color)
631
+ end
632
+ relation_colors[relation]
633
+ end
634
+ def update_relation_color(relation, color)
635
+ relation_colors[relation] = color
636
+ color = Qt::Color.new(color)
637
+ pen = relation_pens[relation] = Qt::Pen.new(color)
638
+ brush = relation_brushes[relation] = Qt::Brush.new(color)
639
+ arrows.each do |(_, _, rel), arrow|
640
+ if rel == relation
641
+ arrow.pen = arrow.line.pen = pen
642
+ arrow.brush = brush
643
+ end
644
+ end
645
+ end
646
+
647
+ def layout_method=(new_method)
648
+ return if new_method == @layout_method
649
+
650
+ @layout_method = nil
651
+ @layout_options = nil
652
+ if new_method
653
+ new_method =~ /^(\w+)(?: \[(.*)\])?$/
654
+ @layout_method = $1
655
+ if $2
656
+ @layout_options = $2.split(",").inject(Hash.new) do |h, v|
657
+ k, v = v.split("=")
658
+ h[k] = v
659
+ h
660
+ end
661
+ end
662
+ end
663
+ display
664
+ end
665
+ def layout_options
666
+ return @layout_options if @layout_options
667
+ { :rankdir => 'TB' }
668
+ end
669
+ def layout_method
670
+ return @layout_method if @layout_method
671
+ "dot"
672
+ end
673
+
674
+ def displayed?(object)
675
+ visible_objects.include?(object) ||
676
+ flashing_objects.has_key?(object)
677
+ end
678
+ def set_visibility(object, flag)
679
+ return if visible_objects.include?(object) == flag
680
+
681
+ if item = graphics[object]
682
+ item.visible = flag
683
+ end
684
+
685
+ if flag
686
+ visible_objects << object
687
+ else
688
+ visible_objects.delete(object)
689
+ end
690
+ end
691
+
692
+ def create_or_get_item(object)
693
+ unless item = graphics[object]
694
+ item = graphics[object] = object.display_create(self)
695
+ if item
696
+ item.parent_item = self[object.display_parent] if object.display_parent
697
+ yield(item) if block_given?
698
+
699
+ if !displayed?(object)
700
+ item.visible = false
701
+ end
702
+ end
703
+ end
704
+
705
+ item.visible = displayed?(object)
706
+ item
707
+ end
708
+
709
+ # Add +object+ to the list of objects temporarily displayed. If a
710
+ # block is given, the object is removed when the block returns
711
+ # false. Otherwise, it is removed at the next display update
712
+ #
713
+ # If this method is called more than once for the same object, the
714
+ # object is removed when *all* blocks have returned false at least
715
+ # once
716
+ def add_flashing_object(object, &block)
717
+ if block
718
+ flashing_objects[object] ||= []
719
+ flashing_objects[object] << block
720
+ else
721
+ flashing_objects[object] ||= nil
722
+ end
723
+
724
+ create_or_get_item(object)
725
+ end
726
+ def clear_flashing_objects
727
+ (flashing_objects.keys.to_value_set - visible_objects).each do |object|
728
+ if blocks = flashing_objects[object]
729
+ blocks.delete_if { |block| !block.call }
730
+ next unless blocks.empty?
731
+ end
732
+
733
+ if item = graphics[object]
734
+ item.visible = false
735
+ end
736
+ flashing_objects.delete(object)
737
+ end
738
+ end
739
+
740
+ def propagation_style(arrow, flag)
741
+ unless defined? @@propagation_styles
742
+ @@propagation_styles = Hash.new
743
+ @@propagation_styles[PROPAG_FORWARD] =
744
+ [Qt::Brush.new(Qt::Color.new('black')), (forward_pen = Qt::Pen.new)]
745
+ forward_pen.style = Qt::DotLine
746
+ @@propagation_styles[PROPAG_SIGNAL] =
747
+ [Qt::Brush.new(Qt::Color.new('black')), Qt::Pen.new]
748
+ @@propagation_styles[PROPAG_EMITTING] =
749
+ [Qt::Brush.new(Qt::Color.new('blue')), (emitting_pen = Qt::Pen.new(Qt::Color.new('blue')))]
750
+ emitting_pen.style = Qt::DotLine
751
+ @@propagation_styles[PROPAG_CALLING] =
752
+ [Qt::Brush.new(Qt::Color.new('blue')), Qt::Pen.new(Qt::Color.new('blue'))]
753
+ end
754
+ arrow.brush, pen = @@propagation_styles[flag]
755
+ arrow.pen = arrow.line.pen = pen
756
+ end
757
+
758
+ def clear_integrated
759
+ postponed_events.clear
760
+ execution_events.clear
761
+ @execution_events = execution_events.find_all { |fired, ev| !fired }
762
+ end
763
+
764
+ def update
765
+ return unless decoder
766
+
767
+ if keep_signals
768
+ @execution_events = @last_execution_events.concat(execution_events)
769
+ @propagated_events.concat @last_propagated_events
770
+ end
771
+
772
+ update_prefixes_removal
773
+ clear_flashing_objects
774
+
775
+ # The sets of tasks and events know to the data stream
776
+ all_tasks = decoder.plans.inject(ValueSet.new) do |all_tasks, plan|
777
+ all_tasks.merge plan.known_tasks
778
+ all_tasks.merge plan.finalized_tasks
779
+ end
780
+ all_events = decoder.plans.inject(ValueSet.new) do |all_events, plan|
781
+ all_events.merge plan.free_events
782
+ all_events.merge plan.finalized_events
783
+ end
784
+
785
+ # Remove the items for objects that don't exist anymore
786
+ (graphics.keys.to_value_set - all_tasks - all_events).each do |obj|
787
+ visible_objects.delete(obj)
788
+ remove_graphics(graphics.delete(obj))
789
+ clear_arrows(obj)
790
+ end
791
+
792
+ visible_objects.merge(decoder.plans)
793
+
794
+ decoder.plans.each do |plan|
795
+ if hide_finalized
796
+ @visible_objects = visible_objects - plan.finalized_tasks
797
+ @visible_objects = visible_objects - plan.finalized_events
798
+
799
+ all_finalized = plan.finalized_tasks | plan.finalized_events
800
+ flashing_objects.delete_if { |obj, _| all_finalized.include?(obj) }
801
+ else
802
+ visible_objects.merge(plan.finalized_tasks)
803
+ visible_objects.merge(plan.finalized_events)
804
+ end
805
+ end
806
+
807
+ # Create graphics items for tasks and events if necessary, and
808
+ # update their visibility according to the visible_objects set
809
+ [all_tasks, all_events, decoder.plans].each do |object_set|
810
+ object_set.each do |object|
811
+ if displayed?(object)
812
+ create_or_get_item(object)
813
+ elsif !object.display_parent
814
+ if item = graphics[object]
815
+ item.visible = false
816
+ end
817
+ end
818
+ end
819
+ end
820
+
821
+ EventGeneratorDisplay.priorities.clear
822
+ event_priority = 0
823
+ execution_events.each_with_index do |(flags, object), event_priority|
824
+ EventGeneratorDisplay.priorities[object] = event_priority
825
+ next if object.respond_to?(:task) && !displayed?(object.task)
826
+
827
+ graphics = if flashing_objects.has_key?(object)
828
+ self.graphics[object]
829
+ else
830
+ add_flashing_object(object)
831
+ end
832
+
833
+ graphics.brush, graphics.pen = EventGeneratorDisplay.style(object, flags)
834
+ end
835
+
836
+ propagated_events.each do |_, sources, to, _|
837
+ sources.each do |from|
838
+ if !EventGeneratorDisplay.priorities.has_key?(from)
839
+ EventGeneratorDisplay.priorities[from] = (event_priority += 1)
840
+ end
841
+ if !EventGeneratorDisplay.priorities.has_key?(to)
842
+ EventGeneratorDisplay.priorities[to] = (event_priority += 1)
843
+ end
844
+
845
+ if from.respond_to?(:task)
846
+ next if !displayed?(from.task)
847
+ else
848
+ next if !all_events.include?(from)
849
+ end
850
+ if to.respond_to?(:task)
851
+ next if !displayed?(to.task)
852
+ else
853
+ next if !all_events.include?(to)
854
+ end
855
+
856
+ add_flashing_object from
857
+ add_flashing_object to
858
+ end
859
+ end
860
+
861
+
862
+ [all_tasks, all_events, decoder.plans].each do |object_set|
863
+ object_set.each do |object|
864
+ next unless displayed?(object)
865
+ object.display(self, graphics[object])
866
+ end
867
+ end
868
+
869
+ # Update arrow visibility
870
+ arrows.each do |(from, to, rel), item|
871
+ item.visible = (displayed?(from) && displayed?(to))
872
+ end
873
+
874
+ # Layout the graph
875
+ layouts = decoder.plans.find_all { |p| p.root_plan? }.
876
+ map do |p|
877
+ dot = Layout.new
878
+ dot.layout(self, p)
879
+ dot
880
+ end
881
+ layouts.each { |dot| dot.apply }
882
+
883
+ # Display the signals
884
+ signal_arrow_idx = -1
885
+ propagated_events.each_with_index do |(flag, sources, to), signal_arrow_idx|
886
+ sources.each do |from|
887
+ unless arrow = signal_arrows[signal_arrow_idx]
888
+ arrow = signal_arrows[signal_arrow_idx] = scene.add_arrow(ARROW_SIZE)
889
+ arrow.z_value = EVENT_LAYER + 1
890
+ arrow.line.z_value = EVENT_LAYER - 1
891
+ end
892
+
893
+ # It is possible that the objects have been removed in the
894
+ # same display cycle than they have been signalled. Do not
895
+ # display them if it is the case
896
+ unless displayed?(from) && displayed?(to)
897
+ arrow.visible = false
898
+ next
899
+ end
900
+ puts from if !self[from]
901
+ puts to if !self[to]
902
+
903
+ arrow.visible = true
904
+ propagation_style(arrow, flag)
905
+ Log.arrow_set(arrow, self[from], self[to])
906
+ end
907
+ end
908
+ # ... and hide the remaining arrows that are not used anymore
909
+ if signal_arrow_idx + 1 < signal_arrows.size
910
+ signal_arrows[(signal_arrow_idx + 1)..-1].each do |arrow|
911
+ arrow.visible = false
912
+ end
913
+ end
914
+
915
+ @last_propagated_events, @propagated_events = propagated_events, Array.new
916
+ @last_execution_events, @execution_events =
917
+ execution_events.partition { |fired, ev| fired }
918
+
919
+ postponed_events.clear
920
+ end
921
+
922
+ def remove_graphics(item, scene = nil)
923
+ return unless item
924
+ scene ||= item.scene
925
+ scene.remove_item(item) if scene
926
+ end
927
+
928
+ def local_task(obj); decoder.local_task(obj) end
929
+ def local_event(obj); decoder.local_event(obj) end
930
+ def local_plan(obj); decoder.local_plan(obj) end
931
+ def local_object(obj); decoder.local_object(obj) end
932
+
933
+ def add_internal_propagation(flag, generator, source_generators)
934
+ generator = local_event(generator)
935
+ if source_generators && !source_generators.empty?
936
+ source_generators = source_generators.map { |source_generator| local_event(source_generator) }
937
+ source_generators.delete_if do |ev|
938
+ ev == generator ||
939
+ propagated_events.find { |_, from, to| to == generator && from.include?(ev) }
940
+ end
941
+ unless source_generators.empty?
942
+ propagated_events << [flag, source_generators, generator]
943
+ end
944
+ end
945
+ end
946
+ def generator_calling(*args)
947
+ if args.size == 3
948
+ time, generator, context = *args
949
+ source_generators = []
950
+ else
951
+ time, generator, source_generators, context = *args
952
+ end
953
+
954
+ add_internal_propagation(PROPAG_CALLING, generator, source_generators)
955
+ end
956
+ def generator_emitting(*args)
957
+ if args.size == 3
958
+ time, generator, context = *args
959
+ source_generators = []
960
+ else
961
+ time, generator, source_generators, context = *args
962
+ end
963
+
964
+ add_internal_propagation(PROPAG_EMITTING, generator, source_generators)
965
+ end
966
+ def generator_signalling(time, flag, from, to, event_id, event_time, event_context)
967
+ propagated_events << [PROPAG_SIGNAL, [local_event(from)], local_event(to)]
968
+ end
969
+ def generator_forwarding(time, flag, from, to, event_id, event_time, event_context)
970
+ propagated_events << [PROPAG_FORWARD, [local_event(from)], local_event(to)]
971
+ end
972
+
973
+ def generator_called(time, generator, context)
974
+ execution_events << [EVENT_CALLED, local_event(generator)]
975
+ end
976
+ def generator_fired(time, generator, event_id, event_time, event_context)
977
+ generator = local_event(generator)
978
+
979
+ found_pending = false
980
+ execution_events.delete_if do |flags, ev|
981
+ if flags == EVENT_CALLED && generator == ev
982
+ found_pending = true
983
+ end
984
+ end
985
+ execution_events << [(found_pending ? EVENT_CALLED_AND_EMITTED : EVENT_EMITTED), generator]
986
+ end
987
+ def generator_postponed(time, generator, context, until_generator, reason)
988
+ postponed_events << [local_event(generator), local_event(until_generator)]
989
+ end
990
+
991
+
992
+ def removed_task_child(time, parent, rel, child)
993
+ remove_graphics(arrows.delete([local_task(parent), local_task(child), rel]))
994
+ end
995
+ def removed_event_child(time, parent, rel, child)
996
+ remove_graphics(arrows.delete([local_event(parent), local_event(child), rel]))
997
+ end
998
+ def discovered_tasks(time, plan, tasks)
999
+ tasks.each do |obj|
1000
+ obj.flags[:pending] = true if obj.respond_to?(:flags)
1001
+ task = local_task(obj)
1002
+
1003
+ set_visibility(task, true)
1004
+ task.events.each_value do |ev|
1005
+ if item = self[ev]
1006
+ item.visible = false
1007
+ end
1008
+ end
1009
+ end
1010
+ end
1011
+ def clear_arrows(object)
1012
+ arrows.delete_if do |(from, to, _), arrow|
1013
+ if from == object || to == object
1014
+ remove_graphics(arrow)
1015
+ true
1016
+ end
1017
+ end
1018
+ end
1019
+
1020
+ def clear
1021
+ arrows.dup.each_value(&method(:remove_graphics))
1022
+ graphics.dup.each_value(&method(:remove_graphics))
1023
+ arrows.clear
1024
+ graphics.clear
1025
+
1026
+ signal_arrows.each do |arrow|
1027
+ arrow.visible = false
1028
+ end
1029
+
1030
+ visible_objects.clear
1031
+ flashing_objects.clear
1032
+ propagated_events.clear
1033
+ execution_events.clear
1034
+ postponed_events.clear
1035
+
1036
+ scene.update(scene.scene_rect)
1037
+ end
1038
+ end
1039
+ end
1040
+ end
1041
+
1042
+
1043
+ if $0 == __FILE__
1044
+ require 'roby/log/file'
1045
+ include Roby::Log
1046
+ app = Qt::Application.new(ARGV)
1047
+ builder = PlanRebuild.new
1048
+ rel = RelationsDisplay.new(builder)
1049
+ rel.main_widget.show
1050
+ Roby::Log.replay(ARGV[0]) do |method_name, method_args|
1051
+ builder.send(method_name, *method_args) if builder.respond_to?(method_name)
1052
+ rel.send(method_name, *method_args) if rel.respond_to?(method_name)
1053
+ end
1054
+ app.exec
1055
+ end
1056
+