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.
- data/.gitignore +29 -0
- data/History.txt +4 -0
- data/License-fr.txt +519 -0
- data/License.txt +515 -0
- data/Manifest.txt +245 -0
- data/NOTES +4 -0
- data/README.txt +163 -0
- data/Rakefile +161 -0
- data/TODO.txt +146 -0
- data/app/README.txt +24 -0
- data/app/Rakefile +8 -0
- data/app/config/ROBOT.rb +5 -0
- data/app/config/app.yml +91 -0
- data/app/config/init.rb +7 -0
- data/app/config/roby.yml +3 -0
- data/app/controllers/.gitattributes +0 -0
- data/app/controllers/ROBOT.rb +2 -0
- data/app/data/.gitattributes +0 -0
- data/app/planners/ROBOT/main.rb +6 -0
- data/app/planners/main.rb +5 -0
- data/app/scripts/distributed +3 -0
- data/app/scripts/generate/bookmarks +3 -0
- data/app/scripts/replay +3 -0
- data/app/scripts/results +3 -0
- data/app/scripts/run +3 -0
- data/app/scripts/server +3 -0
- data/app/scripts/shell +3 -0
- data/app/scripts/test +3 -0
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby +210 -0
- data/bin/roby-log +168 -0
- data/bin/roby-shell +25 -0
- data/doc/images/event_generalization.png +0 -0
- data/doc/images/exception_propagation_1.png +0 -0
- data/doc/images/exception_propagation_2.png +0 -0
- data/doc/images/exception_propagation_3.png +0 -0
- data/doc/images/exception_propagation_4.png +0 -0
- data/doc/images/exception_propagation_5.png +0 -0
- data/doc/images/replay_handler_error.png +0 -0
- data/doc/images/replay_handler_error_0.png +0 -0
- data/doc/images/replay_handler_error_1.png +0 -0
- data/doc/images/roby_cycle_overview.png +0 -0
- data/doc/images/roby_replay_02.png +0 -0
- data/doc/images/roby_replay_03.png +0 -0
- data/doc/images/roby_replay_04.png +0 -0
- data/doc/images/roby_replay_event_representation.png +0 -0
- data/doc/images/roby_replay_first_state.png +0 -0
- data/doc/images/roby_replay_relations.png +0 -0
- data/doc/images/roby_replay_startup.png +0 -0
- data/doc/images/task_event_generalization.png +0 -0
- data/doc/papers.rdoc +11 -0
- data/doc/styles/allison.css +314 -0
- data/doc/styles/allison.js +316 -0
- data/doc/styles/allison.rb +276 -0
- data/doc/styles/jamis.rb +593 -0
- data/doc/tutorials/01-GettingStarted.rdoc +86 -0
- data/doc/tutorials/02-GoForward.rdoc +220 -0
- data/doc/tutorials/03-PlannedPath.rdoc +268 -0
- data/doc/tutorials/04-EventPropagation.rdoc +236 -0
- data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
- data/doc/tutorials/06-Overview.rdoc +40 -0
- data/doc/videos.rdoc +69 -0
- data/ext/droby/dump.cc +175 -0
- data/ext/droby/extconf.rb +3 -0
- data/ext/graph/algorithm.cc +746 -0
- data/ext/graph/extconf.rb +7 -0
- data/ext/graph/graph.cc +529 -0
- data/ext/graph/graph.hh +183 -0
- data/ext/graph/iterator_sequence.hh +102 -0
- data/ext/graph/undirected_dfs.hh +226 -0
- data/ext/graph/undirected_graph.hh +421 -0
- data/lib/roby.rb +41 -0
- data/lib/roby/app.rb +870 -0
- data/lib/roby/app/rake.rb +56 -0
- data/lib/roby/app/run.rb +14 -0
- data/lib/roby/app/scripts/distributed.rb +13 -0
- data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
- data/lib/roby/app/scripts/replay.rb +31 -0
- data/lib/roby/app/scripts/results.rb +15 -0
- data/lib/roby/app/scripts/run.rb +26 -0
- data/lib/roby/app/scripts/server.rb +18 -0
- data/lib/roby/app/scripts/shell.rb +88 -0
- data/lib/roby/app/scripts/test.rb +40 -0
- data/lib/roby/basic_object.rb +151 -0
- data/lib/roby/config.rb +5 -0
- data/lib/roby/control.rb +747 -0
- data/lib/roby/decision_control.rb +17 -0
- data/lib/roby/distributed.rb +32 -0
- data/lib/roby/distributed/base.rb +440 -0
- data/lib/roby/distributed/communication.rb +871 -0
- data/lib/roby/distributed/connection_space.rb +592 -0
- data/lib/roby/distributed/distributed_object.rb +206 -0
- data/lib/roby/distributed/drb.rb +62 -0
- data/lib/roby/distributed/notifications.rb +539 -0
- data/lib/roby/distributed/peer.rb +550 -0
- data/lib/roby/distributed/protocol.rb +529 -0
- data/lib/roby/distributed/proxy.rb +343 -0
- data/lib/roby/distributed/subscription.rb +311 -0
- data/lib/roby/distributed/transaction.rb +498 -0
- data/lib/roby/event.rb +897 -0
- data/lib/roby/exceptions.rb +234 -0
- data/lib/roby/executives/simple.rb +30 -0
- data/lib/roby/graph.rb +166 -0
- data/lib/roby/interface.rb +390 -0
- data/lib/roby/log.rb +3 -0
- data/lib/roby/log/chronicle.rb +303 -0
- data/lib/roby/log/console.rb +72 -0
- data/lib/roby/log/data_stream.rb +197 -0
- data/lib/roby/log/dot.rb +279 -0
- data/lib/roby/log/event_stream.rb +151 -0
- data/lib/roby/log/file.rb +340 -0
- data/lib/roby/log/gui/basic_display.ui +83 -0
- data/lib/roby/log/gui/chronicle.rb +26 -0
- data/lib/roby/log/gui/chronicle_view.rb +40 -0
- data/lib/roby/log/gui/chronicle_view.ui +70 -0
- data/lib/roby/log/gui/data_displays.rb +172 -0
- data/lib/roby/log/gui/data_displays.ui +155 -0
- data/lib/roby/log/gui/notifications.rb +26 -0
- data/lib/roby/log/gui/relations.rb +248 -0
- data/lib/roby/log/gui/relations.ui +123 -0
- data/lib/roby/log/gui/relations_view.rb +185 -0
- data/lib/roby/log/gui/relations_view.ui +149 -0
- data/lib/roby/log/gui/replay.rb +327 -0
- data/lib/roby/log/gui/replay_controls.rb +200 -0
- data/lib/roby/log/gui/replay_controls.ui +259 -0
- data/lib/roby/log/gui/runtime.rb +130 -0
- data/lib/roby/log/hooks.rb +185 -0
- data/lib/roby/log/logger.rb +202 -0
- data/lib/roby/log/notifications.rb +244 -0
- data/lib/roby/log/plan_rebuilder.rb +470 -0
- data/lib/roby/log/relations.rb +1056 -0
- data/lib/roby/log/server.rb +550 -0
- data/lib/roby/log/sqlite.rb +47 -0
- data/lib/roby/log/timings.rb +164 -0
- data/lib/roby/plan-object.rb +247 -0
- data/lib/roby/plan.rb +762 -0
- data/lib/roby/planning.rb +13 -0
- data/lib/roby/planning/loops.rb +302 -0
- data/lib/roby/planning/model.rb +906 -0
- data/lib/roby/planning/task.rb +151 -0
- data/lib/roby/propagation.rb +562 -0
- data/lib/roby/query.rb +619 -0
- data/lib/roby/relations.rb +583 -0
- data/lib/roby/relations/conflicts.rb +70 -0
- data/lib/roby/relations/ensured.rb +20 -0
- data/lib/roby/relations/error_handling.rb +23 -0
- data/lib/roby/relations/events.rb +9 -0
- data/lib/roby/relations/executed_by.rb +193 -0
- data/lib/roby/relations/hierarchy.rb +239 -0
- data/lib/roby/relations/influence.rb +10 -0
- data/lib/roby/relations/planned_by.rb +63 -0
- data/lib/roby/robot.rb +7 -0
- data/lib/roby/standard_errors.rb +218 -0
- data/lib/roby/state.rb +5 -0
- data/lib/roby/state/events.rb +221 -0
- data/lib/roby/state/information.rb +55 -0
- data/lib/roby/state/pos.rb +110 -0
- data/lib/roby/state/shapes.rb +32 -0
- data/lib/roby/state/state.rb +353 -0
- data/lib/roby/support.rb +92 -0
- data/lib/roby/task-operations.rb +182 -0
- data/lib/roby/task.rb +1618 -0
- data/lib/roby/test/common.rb +399 -0
- data/lib/roby/test/distributed.rb +214 -0
- data/lib/roby/test/tasks/empty_task.rb +9 -0
- data/lib/roby/test/tasks/goto.rb +36 -0
- data/lib/roby/test/tasks/simple_task.rb +23 -0
- data/lib/roby/test/testcase.rb +519 -0
- data/lib/roby/test/tools.rb +160 -0
- data/lib/roby/thread_task.rb +87 -0
- data/lib/roby/transactions.rb +462 -0
- data/lib/roby/transactions/proxy.rb +292 -0
- data/lib/roby/transactions/updates.rb +139 -0
- data/plugins/fault_injection/History.txt +4 -0
- data/plugins/fault_injection/README.txt +37 -0
- data/plugins/fault_injection/Rakefile +18 -0
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +52 -0
- data/plugins/fault_injection/fault_injection.rb +89 -0
- data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
- data/plugins/subsystems/README.txt +40 -0
- data/plugins/subsystems/Rakefile +18 -0
- data/plugins/subsystems/app.rb +171 -0
- data/plugins/subsystems/test/app/README +24 -0
- data/plugins/subsystems/test/app/Rakefile +8 -0
- data/plugins/subsystems/test/app/config/app.yml +71 -0
- data/plugins/subsystems/test/app/config/init.rb +9 -0
- data/plugins/subsystems/test/app/config/roby.yml +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +20 -0
- data/plugins/subsystems/test/app/scripts/distributed +3 -0
- data/plugins/subsystems/test/app/scripts/replay +3 -0
- data/plugins/subsystems/test/app/scripts/results +3 -0
- data/plugins/subsystems/test/app/scripts/run +3 -0
- data/plugins/subsystems/test/app/scripts/server +3 -0
- data/plugins/subsystems/test/app/scripts/shell +3 -0
- data/plugins/subsystems/test/app/scripts/test +3 -0
- data/plugins/subsystems/test/app/tasks/services.rb +15 -0
- data/plugins/subsystems/test/test_subsystems.rb +71 -0
- data/test/distributed/test_communication.rb +178 -0
- data/test/distributed/test_connection.rb +282 -0
- data/test/distributed/test_execution.rb +373 -0
- data/test/distributed/test_mixed_plan.rb +341 -0
- data/test/distributed/test_plan_notifications.rb +238 -0
- data/test/distributed/test_protocol.rb +516 -0
- data/test/distributed/test_query.rb +102 -0
- data/test/distributed/test_remote_plan.rb +491 -0
- data/test/distributed/test_transaction.rb +463 -0
- data/test/mockups/tasks.rb +27 -0
- data/test/planning/test_loops.rb +380 -0
- data/test/planning/test_model.rb +427 -0
- data/test/planning/test_task.rb +106 -0
- data/test/relations/test_conflicts.rb +42 -0
- data/test/relations/test_ensured.rb +38 -0
- data/test/relations/test_executed_by.rb +149 -0
- data/test/relations/test_hierarchy.rb +158 -0
- data/test/relations/test_planned_by.rb +54 -0
- data/test/suite_core.rb +24 -0
- data/test/suite_distributed.rb +9 -0
- data/test/suite_planning.rb +3 -0
- data/test/suite_relations.rb +8 -0
- data/test/test_bgl.rb +508 -0
- data/test/test_control.rb +399 -0
- data/test/test_event.rb +894 -0
- data/test/test_exceptions.rb +592 -0
- data/test/test_interface.rb +37 -0
- data/test/test_log.rb +114 -0
- data/test/test_log_server.rb +132 -0
- data/test/test_plan.rb +584 -0
- data/test/test_propagation.rb +210 -0
- data/test/test_query.rb +266 -0
- data/test/test_relations.rb +180 -0
- data/test/test_state.rb +414 -0
- data/test/test_support.rb +16 -0
- data/test/test_task.rb +938 -0
- data/test/test_testcase.rb +122 -0
- data/test/test_thread_task.rb +73 -0
- data/test/test_transactions.rb +569 -0
- data/test/test_transactions_proxy.rb +198 -0
- 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
|
+
|