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,234 @@
|
|
|
1
|
+
class Exception
|
|
2
|
+
def pretty_print(pp)
|
|
3
|
+
pp.text "#{message} (#{self.class.name})"
|
|
4
|
+
pp.breakable
|
|
5
|
+
Roby.pretty_print_backtrace(pp, backtrace)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# True if +obj+ is involved in this error
|
|
9
|
+
def involved_plan_object?(obj)
|
|
10
|
+
false
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Roby
|
|
15
|
+
class ConfigError < RuntimeError; end
|
|
16
|
+
class ModelViolation < RuntimeError; end
|
|
17
|
+
|
|
18
|
+
# ExecutionException objects are used during the exception handling stage
|
|
19
|
+
# to keep information about the propagation.
|
|
20
|
+
#
|
|
21
|
+
# When a propagation fork is found (for instance, a task with two parents),
|
|
22
|
+
# two or more siblings are created with #fork. If at some point two
|
|
23
|
+
# siblings are to be handled by the same task, coming for instance from two
|
|
24
|
+
# different children, then they are merged with #merge to from one single
|
|
25
|
+
# ExecutionException object.
|
|
26
|
+
class ExecutionException
|
|
27
|
+
# The propagation trace. Because of forks and merges, this should be a
|
|
28
|
+
# graph. We don't use graph properties (at least not yet), so consider
|
|
29
|
+
# this as the list of objects which did not handle the exeption. Only
|
|
30
|
+
# trace.last and trace.first have a definite meaning: the former
|
|
31
|
+
# is the last object(s) that handled the propagation and the latter
|
|
32
|
+
# is the object from which the exception originated. They can be
|
|
33
|
+
# accessed through #task and #origin.
|
|
34
|
+
attr_reader :trace
|
|
35
|
+
# The last object(s) that handled the exception. This is either a
|
|
36
|
+
# single object or an array
|
|
37
|
+
def task; trace.last end
|
|
38
|
+
# The object from which the exception originates
|
|
39
|
+
def origin; trace.first end
|
|
40
|
+
|
|
41
|
+
# The exception siblings (the ExecutionException objects
|
|
42
|
+
# that come from the same exception object)
|
|
43
|
+
attr_reader :siblings
|
|
44
|
+
# The origin EventGenerator if there is one
|
|
45
|
+
attr_reader :generator
|
|
46
|
+
# The exception object
|
|
47
|
+
attr_reader :exception
|
|
48
|
+
|
|
49
|
+
# If this specific exception has been marked has handled
|
|
50
|
+
attr_accessor :handled
|
|
51
|
+
# If this exception or one of its siblings has been marked as handled
|
|
52
|
+
def handled?
|
|
53
|
+
siblings.find { |s| s.handled }
|
|
54
|
+
end
|
|
55
|
+
# Enumerates this exception's siblings
|
|
56
|
+
def each_sibling
|
|
57
|
+
for e in siblings
|
|
58
|
+
yield(e) unless e == self
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Creates a new execution exception object with the specified source
|
|
63
|
+
# If +source+ is nil, tries to guess the source from +exception+: if
|
|
64
|
+
# +exception+ responds to #task or #generator we use either #task or
|
|
65
|
+
# call #generator.task
|
|
66
|
+
def initialize(exception)
|
|
67
|
+
@exception = exception
|
|
68
|
+
@trace = Array.new
|
|
69
|
+
@siblings = [self]
|
|
70
|
+
|
|
71
|
+
if task = exception.failed_task
|
|
72
|
+
@trace << exception.failed_task
|
|
73
|
+
end
|
|
74
|
+
if generator = exception.failed_generator
|
|
75
|
+
@generator = exception.failed_generator
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if !task && !generator
|
|
79
|
+
raise ArgumentError, "invalid exception specification: cannot get the exception source"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Create a sibling from this exception
|
|
84
|
+
def fork
|
|
85
|
+
sibling = dup
|
|
86
|
+
self.siblings << sibling
|
|
87
|
+
sibling
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Merges +sibling+ into this object
|
|
91
|
+
def merge(sibling)
|
|
92
|
+
siblings.delete(sibling)
|
|
93
|
+
|
|
94
|
+
topstack = trace.pop
|
|
95
|
+
s_topstack = sibling.trace.pop
|
|
96
|
+
|
|
97
|
+
origin = trace.shift
|
|
98
|
+
s_origin = sibling.trace.shift
|
|
99
|
+
origin = origin || s_origin || topstack
|
|
100
|
+
|
|
101
|
+
new_top = *(Array[*topstack] | Array[*s_topstack])
|
|
102
|
+
@trace = [origin] + (trace | sibling.trace) << new_top
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def initialize_copy(from)
|
|
106
|
+
super
|
|
107
|
+
@trace = from.trace.dup
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# This module is to be included in all objects that are
|
|
112
|
+
# able to handle exception. These objects should define
|
|
113
|
+
# #each_exception_handler { |matchers, handler| ... }
|
|
114
|
+
#
|
|
115
|
+
# See Task::on_exception and Task#on_exception
|
|
116
|
+
module ExceptionHandlingObject
|
|
117
|
+
# To be used in exception handlers themselves. Passes the exception to
|
|
118
|
+
# the next matching exception handler
|
|
119
|
+
def pass_exception
|
|
120
|
+
throw :next_exception_handler
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Calls the exception handlers defined in this task for +exception_object.exception+
|
|
124
|
+
# Returns true if the exception has been handled, false otherwise
|
|
125
|
+
def handle_exception(exception_object)
|
|
126
|
+
each_exception_handler do |matchers, handler|
|
|
127
|
+
if matchers.find { |m| m === exception_object.exception }
|
|
128
|
+
catch(:next_exception_handler) do
|
|
129
|
+
begin
|
|
130
|
+
handler.call(self, exception_object)
|
|
131
|
+
return true
|
|
132
|
+
rescue Exception => e
|
|
133
|
+
if self == Roby
|
|
134
|
+
Propagation.add_framework_error(e, 'global exception handling')
|
|
135
|
+
else
|
|
136
|
+
Propagation.add_error(FailedExceptionHandler.new(e, self, exception_object))
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
return false
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
RX_IN_FRAMEWORK = /^((?:\s*\(druby:\/\/.+\)\s*)?#{Regexp.quote(ROBY_LIB_DIR)}\/)/
|
|
147
|
+
def self.filter_backtrace(original_backtrace)
|
|
148
|
+
if Roby.app.filter_backtraces? && original_backtrace
|
|
149
|
+
app_dir = if defined? APP_DIR then Regexp.quote(APP_DIR) end
|
|
150
|
+
|
|
151
|
+
original_backtrace = original_backtrace.dup
|
|
152
|
+
backtrace_bottom = []
|
|
153
|
+
while !original_backtrace.empty? && original_backtrace.last !~ RX_IN_FRAMEWORK
|
|
154
|
+
backtrace_bottom.unshift original_backtrace.pop
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
backtrace = original_backtrace.enum_for(:each_with_index).map do |line, idx|
|
|
158
|
+
case line
|
|
159
|
+
when /in `poll_handler'$/
|
|
160
|
+
line.gsub /:in.*/, ':in the polling handler'
|
|
161
|
+
when /in `event_command_(\w+)'$/
|
|
162
|
+
line.gsub /:in.*/, ":in command for '#{$1}'"
|
|
163
|
+
when /in `event_handler_(\w+)_(?:[a-f0-9]+)'$/
|
|
164
|
+
line.gsub /:in.*/, ":in event handler for '#{$1}'"
|
|
165
|
+
else
|
|
166
|
+
if original_backtrace.size > idx + 4 &&
|
|
167
|
+
original_backtrace[idx + 1] =~ /in `call'$/ &&
|
|
168
|
+
original_backtrace[idx + 2] =~ /in `call_handlers'$/ &&
|
|
169
|
+
original_backtrace[idx + 3] =~ /`each'$/ &&
|
|
170
|
+
original_backtrace[idx + 4] =~ /`each_handler'$/
|
|
171
|
+
|
|
172
|
+
line.gsub /:in /, ":in event handler, "
|
|
173
|
+
else
|
|
174
|
+
case line
|
|
175
|
+
when /in `(gem_original_)?require'$/
|
|
176
|
+
when /^((?:\s*\(druby:\/\/.+\)\s*)?#{Regexp.quote(ROBY_LIB_DIR)}\/)/
|
|
177
|
+
when /^(#{app_dir}\/)?scripts\//
|
|
178
|
+
when /^\(eval\):\d+:in `each(?:_handler)?'/
|
|
179
|
+
else
|
|
180
|
+
line
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
while !backtrace.empty? && !backtrace.last
|
|
187
|
+
backtrace.pop
|
|
188
|
+
end
|
|
189
|
+
backtrace.each_with_index do |line, i|
|
|
190
|
+
backtrace[i] = line || original_backtrace[i]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
if app_dir
|
|
194
|
+
backtrace = backtrace.map do |line|
|
|
195
|
+
line.gsub /^#{app_dir}\/?/, './'
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
backtrace.concat backtrace_bottom
|
|
199
|
+
end
|
|
200
|
+
backtrace || original_backtrace || []
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def self.pretty_print_backtrace(pp, backtrace)
|
|
204
|
+
if backtrace && !backtrace.empty?
|
|
205
|
+
pp.group(2) do
|
|
206
|
+
pp.seplist(filter_backtrace(backtrace)) { |line| pp.text line }
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def self.format_exception(exception)
|
|
212
|
+
message = begin
|
|
213
|
+
PP.pp(exception, "")
|
|
214
|
+
rescue Exception => formatting_error
|
|
215
|
+
begin
|
|
216
|
+
"error formatting exception\n" +
|
|
217
|
+
exception.full_message +
|
|
218
|
+
"\nplease report the formatting error: \n" +
|
|
219
|
+
formatting_error.full_message
|
|
220
|
+
rescue Exception => formatting_error
|
|
221
|
+
"\nerror formatting exception\n" +
|
|
222
|
+
formatting_error.full_message
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
message.split("\n")
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def self.log_exception(e, logger, level)
|
|
229
|
+
format_exception(e).each do |line|
|
|
230
|
+
logger.send(level, line)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
module Executives
|
|
3
|
+
class Simple
|
|
4
|
+
attr_reader :query
|
|
5
|
+
def initialize
|
|
6
|
+
@query = Roby.plan.find_tasks.
|
|
7
|
+
executable.
|
|
8
|
+
pending.
|
|
9
|
+
self_owned
|
|
10
|
+
end
|
|
11
|
+
def initial_events
|
|
12
|
+
query.reset.each do |task|
|
|
13
|
+
next unless task.event(:start).root? && task.event(:start).controlable?
|
|
14
|
+
root_task = task.enum_for(:each_relation).all? do |rel|
|
|
15
|
+
if task.root?(rel)
|
|
16
|
+
true
|
|
17
|
+
elsif rel == TaskStructure::PlannedBy
|
|
18
|
+
task.planned_tasks.all? { |t| !t.executable? }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if root_task
|
|
23
|
+
task.start!
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
data/lib/roby/graph.rb
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
require 'utilrb/module'
|
|
2
|
+
require 'utilrb/kernel'
|
|
3
|
+
require 'utilrb/enumerable'
|
|
4
|
+
require 'utilrb/value_set'
|
|
5
|
+
require 'roby_bgl'
|
|
6
|
+
|
|
7
|
+
Utilrb.unless_ext do
|
|
8
|
+
raise LoadError, "Roby needs Utilrb's C extension to be compiled"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module BGL
|
|
12
|
+
module Vertex
|
|
13
|
+
def initialize_copy(old)
|
|
14
|
+
super
|
|
15
|
+
@__bgl_graphs__ = nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Removes +self+ from all the graphs it is included in.
|
|
19
|
+
def clear_vertex
|
|
20
|
+
each_graph { |g| g.remove(self) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
attribute(:singleton_set) { [self].to_value_set.freeze }
|
|
24
|
+
# Returns the connected component +self+ is part of in +graph+
|
|
25
|
+
def component(graph)
|
|
26
|
+
graph.components(singleton_set, false).first || singleton_set
|
|
27
|
+
end
|
|
28
|
+
# Returns the vertex set which are reachable from +self+ in +graph+
|
|
29
|
+
def generated_subgraph(graph)
|
|
30
|
+
graph.generated_subgraphs(singleton_set, false).first || singleton_set
|
|
31
|
+
end
|
|
32
|
+
# Returns the vertex set which can reach +self+ in +graph+
|
|
33
|
+
def reverse_generated_subgraph(graph)
|
|
34
|
+
graph.reverse.generated_subgraphs(singleton_set, false).first || singleton_set
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Replace this vertex by +to+ in all graphs. See Graph#replace_vertex.
|
|
38
|
+
def replace_vertex_by(to)
|
|
39
|
+
each_graph { |g| g.replace_vertex(self, to) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns an array of [graph, [parent, child, info], [parent, child,
|
|
43
|
+
# info], ...] elements for all edges +self+ is involved in
|
|
44
|
+
def edges
|
|
45
|
+
result = []
|
|
46
|
+
each_graph do |graph|
|
|
47
|
+
graph_edges = []
|
|
48
|
+
each_child_object do |child|
|
|
49
|
+
graph_edges << [self, child, self[child, graph]]
|
|
50
|
+
end
|
|
51
|
+
each_parent_object do |parent|
|
|
52
|
+
graph_edges << [parent, self, parent[self, graph]]
|
|
53
|
+
end
|
|
54
|
+
result << [graph, graph_edges]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
result
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# call-seq:
|
|
61
|
+
# neighborhood(distance, graph) => [[graph, v, v1, data], [graph, v2, v, data], ...]
|
|
62
|
+
# neighborhood(distance) => [[g1, v, v1, data], [g2, v2, v, data], ...]
|
|
63
|
+
#
|
|
64
|
+
# Returns a list of [graph, edge] representing all edges at a maximum distance
|
|
65
|
+
# of +distance+ from +self+. If +graph+ is given, only enumerate the neighborhood
|
|
66
|
+
# in +graph+.
|
|
67
|
+
def neighborhood(distance, graph = nil)
|
|
68
|
+
if graph
|
|
69
|
+
graph.neighborhood(self, distance).
|
|
70
|
+
map! { |args| args.unshift(graph) }
|
|
71
|
+
else
|
|
72
|
+
edges = []
|
|
73
|
+
each_graph do |graph|
|
|
74
|
+
edges += neighborhood(distance, graph)
|
|
75
|
+
end
|
|
76
|
+
edges
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class Graph
|
|
82
|
+
# This class is an adaptor which transforms a directed graph by
|
|
83
|
+
# swapping its edges
|
|
84
|
+
class Reverse
|
|
85
|
+
# Create a directed graph whose edges are the ones of +g+, but with
|
|
86
|
+
# source and destination swapped.
|
|
87
|
+
def initialize(g)
|
|
88
|
+
@__bgl_real_graph__ = g
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
attribute(:reverse) { @reverse = Graph::Reverse.new(self) }
|
|
92
|
+
|
|
93
|
+
# This class is a graph adaptor which transforms a directed graph into
|
|
94
|
+
# an undirected graph
|
|
95
|
+
class Undirected
|
|
96
|
+
# Create an undirected graph which has the same edge set than +g+
|
|
97
|
+
def initialize(g)
|
|
98
|
+
@__bgl_real_graph__ = g
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
attribute(:undirected) { @undirected = Graph::Undirected.new(self) }
|
|
102
|
+
|
|
103
|
+
def initialize_copy(source) # :nodoc:
|
|
104
|
+
super
|
|
105
|
+
|
|
106
|
+
source.each_vertex { |v| insert(v) }
|
|
107
|
+
source.each_edge { |s, t, i| link(s, t, i) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Replaces +from+ by +to+. This means +to+ takes the role of +from+ in
|
|
111
|
+
# all edges +from+ is involved in. +from+ is removed from the graph.
|
|
112
|
+
def replace_vertex(from, to)
|
|
113
|
+
from.each_parent_vertex(self) do |parent|
|
|
114
|
+
link(parent, to, parent[from, self])
|
|
115
|
+
end
|
|
116
|
+
from.each_child_vertex(self) do |child|
|
|
117
|
+
link(to, child, from[child, self])
|
|
118
|
+
end
|
|
119
|
+
remove(from)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Returns a list of [parent, child, info] for all edges that are at a
|
|
123
|
+
# distance no more than +distance+ from +vertex+.
|
|
124
|
+
def neighborhood(vertex, distance)
|
|
125
|
+
result = []
|
|
126
|
+
seen = Set.new
|
|
127
|
+
depth = { vertex => 0 }
|
|
128
|
+
undirected.each_bfs(vertex, ALL) do |from, to, info, kind|
|
|
129
|
+
new_depth = depth[from] + 1
|
|
130
|
+
if kind == TREE
|
|
131
|
+
depth[to] = new_depth
|
|
132
|
+
else
|
|
133
|
+
next if seen.include?(to)
|
|
134
|
+
end
|
|
135
|
+
seen << from
|
|
136
|
+
|
|
137
|
+
if depth[from] > distance
|
|
138
|
+
break
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
if new_depth <= distance
|
|
142
|
+
if linked?(from, to)
|
|
143
|
+
result << [from, to, info]
|
|
144
|
+
else
|
|
145
|
+
result << [to, from, info]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
result
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Two graphs are the same if they have the same vertex set
|
|
153
|
+
# and the same edge set
|
|
154
|
+
def same_graph?(other)
|
|
155
|
+
unless other.respond_to?(:each_vertex) && other.respond_to?(:each_edge)
|
|
156
|
+
return false
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# cannot use to_value_set for edges since we are comparing arrays (and ValueSet
|
|
160
|
+
# bases its comparison on VALUE)
|
|
161
|
+
(other.enum_for(:each_vertex).to_value_set == enum_for(:each_vertex).to_value_set) &&
|
|
162
|
+
(other.enum_for(:each_edge).to_set == enum_for(:each_edge).to_set)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
require 'roby'
|
|
3
|
+
require 'roby/planning'
|
|
4
|
+
require 'facets/basicobject'
|
|
5
|
+
require 'utilrb/column_formatter'
|
|
6
|
+
require 'stringio'
|
|
7
|
+
require 'roby/robot'
|
|
8
|
+
|
|
9
|
+
module Robot
|
|
10
|
+
def self.prepare_action(name, arguments)
|
|
11
|
+
control = Roby.control
|
|
12
|
+
|
|
13
|
+
# Check if +name+ is a planner method, and in that case
|
|
14
|
+
# add a planning method for it and plan it
|
|
15
|
+
planner_model = control.planners.find do |planner_model|
|
|
16
|
+
planner_model.has_method?(name)
|
|
17
|
+
end
|
|
18
|
+
if !planner_model
|
|
19
|
+
raise ArgumentError, "no such planning method #{name}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
m = planner_model.model_of(name, arguments)
|
|
23
|
+
|
|
24
|
+
# HACK: m.returns should not be nil, but it sometimes happen
|
|
25
|
+
returns_model = (m.returns if m && m.returns) || Task
|
|
26
|
+
|
|
27
|
+
if returns_model.kind_of?(Roby::TaskModelTag)
|
|
28
|
+
task = Roby::Task.new
|
|
29
|
+
task.extend returns_model
|
|
30
|
+
else
|
|
31
|
+
# Create an abstract task which will be planned
|
|
32
|
+
task = returns_model.new
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
planner = Roby::PlanningTask.new(:planner_model => planner_model, :method_name => name, :method_options => arguments)
|
|
36
|
+
task.planned_by planner
|
|
37
|
+
return task, planner
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.method_missing(name, *args)
|
|
41
|
+
if name.to_s =~ /!$/
|
|
42
|
+
name = $`.to_sym
|
|
43
|
+
else
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if args.size > 1
|
|
48
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1) in #{name}!"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
options = args.first || {}
|
|
52
|
+
task, planner = Robot.prepare_action(name, options)
|
|
53
|
+
Roby.control.plan.insert(task)
|
|
54
|
+
|
|
55
|
+
return task, planner
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
module Roby
|
|
60
|
+
# An augmented DRbObject which allow to properly interface with remotely
|
|
61
|
+
# running plan objects.
|
|
62
|
+
class RemoteObjectProxy < DRbObject
|
|
63
|
+
attr_accessor :remote_interface
|
|
64
|
+
|
|
65
|
+
def to_s
|
|
66
|
+
__method_missing__(:to_s)
|
|
67
|
+
end
|
|
68
|
+
def pretty_print(pp)
|
|
69
|
+
pp.text to_s
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
alias __method_missing__ method_missing
|
|
73
|
+
def method_missing(*args, &block)
|
|
74
|
+
if remote_interface
|
|
75
|
+
remote_interface.call(self, *args, &block)
|
|
76
|
+
else
|
|
77
|
+
super
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# RemoteInterface objects are used as local representation of remote
|
|
83
|
+
# interface objects. They offer a seamless interface to a remotely running
|
|
84
|
+
# Roby controller.
|
|
85
|
+
class RemoteInterface
|
|
86
|
+
# Create a RemoteInterface object for the remote object represented by
|
|
87
|
+
# +interface+, where +interface+ is a DRbObject for a remote Interface
|
|
88
|
+
# object.
|
|
89
|
+
def initialize(interface)
|
|
90
|
+
@interface = interface
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Returns a Query object which can be used to interactively query the
|
|
94
|
+
# running plan
|
|
95
|
+
def find_tasks(model = nil, args = nil)
|
|
96
|
+
q = Query.new(self)
|
|
97
|
+
if model
|
|
98
|
+
q.which_fullfills(model, args)
|
|
99
|
+
end
|
|
100
|
+
q
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Defined for remotes queries to work
|
|
104
|
+
def query_result_set(query) # :nodoc:
|
|
105
|
+
@interface.remote_query_result_set(Distributed.format(query)).each do |t|
|
|
106
|
+
t.remote_interface = self
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
# Defined for remotes queries to work
|
|
110
|
+
def query_each(result_set) # :nodoc:
|
|
111
|
+
result_set.each do |t|
|
|
112
|
+
yield(t)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
# Defined for remotes queries to work
|
|
116
|
+
def query_roots(result_set, relation) # :nodoc:
|
|
117
|
+
@interface.remote_query_roots(result_set, Distributed.format(relation)).each do |t|
|
|
118
|
+
t.remote_interface = self
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Returns the DRbObject for the remote controller state object
|
|
123
|
+
def state
|
|
124
|
+
remote_constant('State')
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def instance_methods(include_super = false) # :nodoc:
|
|
128
|
+
Interface.instance_methods(false).
|
|
129
|
+
actions.map { |name| "#{name}!" }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def method_missing(m, *args) # :nodoc:
|
|
134
|
+
result = @interface.send(m, *args)
|
|
135
|
+
if result.kind_of?(RemoteObjectProxy)
|
|
136
|
+
result.remote_interface = @interface
|
|
137
|
+
end
|
|
138
|
+
result
|
|
139
|
+
|
|
140
|
+
rescue Exception => e
|
|
141
|
+
raise e, e.message, Roby.filter_backtrace(e.backtrace)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# This class is used to interface with the Roby event loop and plan. It is the
|
|
146
|
+
# main front object when accessing a Roby core remotely
|
|
147
|
+
class Interface
|
|
148
|
+
module GatherExceptions
|
|
149
|
+
# The set of Interface objects that have been registered to us
|
|
150
|
+
attribute(:interfaces) { Array.new }
|
|
151
|
+
|
|
152
|
+
# Register a new Interface object so that it gets feedback information
|
|
153
|
+
# from the running controller.
|
|
154
|
+
def register_interface(iface)
|
|
155
|
+
Roby::Control.synchronize do
|
|
156
|
+
interfaces << iface
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Pushes a exception message to all the already registered remote interfaces.
|
|
161
|
+
def push_exception_message(name, error, tasks)
|
|
162
|
+
Roby::Control.synchronize do
|
|
163
|
+
msg = Roby.format_exception(error.exception).join("\n")
|
|
164
|
+
msg << "\nThe following tasks have been killed:\n"
|
|
165
|
+
tasks.each do |t|
|
|
166
|
+
msg << " "
|
|
167
|
+
if error.exception.involved_plan_object?(t)
|
|
168
|
+
msg << "#{t.class}:0x#{t.address.to_s(16)}\n"
|
|
169
|
+
else
|
|
170
|
+
PP.pp(t, msg)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
interfaces.each do |iface|
|
|
175
|
+
iface.pending_messages << msg
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Pushes an exception information on all remote interfaces connected to us
|
|
181
|
+
def handled_exception(error, task)
|
|
182
|
+
super if defined? super
|
|
183
|
+
push_exception_message("exception", error, [task])
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Pushes an exception information on all remote interfaces connected to us
|
|
187
|
+
def fatal_exception(error, tasks)
|
|
188
|
+
super if defined? super
|
|
189
|
+
push_exception_message("fatal exception", error, tasks)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# The Roby::Control object this interface is working on
|
|
194
|
+
attr_reader :control
|
|
195
|
+
# The set of pending messages that are to be displayed on the remote interface
|
|
196
|
+
attr_reader :pending_messages
|
|
197
|
+
# Creates a local server for a remote interface, acting on +control+
|
|
198
|
+
def initialize(control)
|
|
199
|
+
@control = control
|
|
200
|
+
@pending_messages = Queue.new
|
|
201
|
+
|
|
202
|
+
Roby::Control.extend GatherExceptions
|
|
203
|
+
Roby::Control.register_interface self
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Clear the current plan: remove all running and permanent tasks.
|
|
207
|
+
def clear
|
|
208
|
+
Roby.execute do
|
|
209
|
+
plan.missions.dup.each { |t| plan.discard(t) }
|
|
210
|
+
plan.keepalive.dup.each { |t| plan.auto(t) }
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Make the Roby event loop quit
|
|
215
|
+
def stop; control.quit; nil end
|
|
216
|
+
# The Roby plan
|
|
217
|
+
def plan; Roby.plan end
|
|
218
|
+
|
|
219
|
+
# Synchronously call +m+ on +tasks+ with the given arguments. This,
|
|
220
|
+
# along with the implementation of RemoteInterface#method_missing,
|
|
221
|
+
# ensures that no interactive operations are performed outside the
|
|
222
|
+
# control thread.
|
|
223
|
+
def call(task, m, *args)
|
|
224
|
+
Roby.execute do
|
|
225
|
+
if m.to_s =~ /!$/
|
|
226
|
+
event_name = $`
|
|
227
|
+
# Check if the called event is terminal. If it is the case,
|
|
228
|
+
# discard the task before calling it, and make sure the user
|
|
229
|
+
# will get a message
|
|
230
|
+
#
|
|
231
|
+
if task.event(event_name).terminal?
|
|
232
|
+
plan.discard(task)
|
|
233
|
+
task.on(:stop) { |ev| pending_messages << "task #{ev.task} stopped by user request" }
|
|
234
|
+
else
|
|
235
|
+
task.on(event_name) { |ev| pending_messages << "done emitting #{ev.generator}" }
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
task.send(m, *args)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def find_tasks(model = nil, args = nil)
|
|
244
|
+
plan.find_tasks(model, args)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# For using Query on Interface objects
|
|
248
|
+
def remote_query_result_set(m_query) # :nodoc:
|
|
249
|
+
plan.query_result_set(m_query.to_query(plan)).
|
|
250
|
+
map { |t| RemoteObjectProxy.new(t) }
|
|
251
|
+
end
|
|
252
|
+
# For using Query on Interface objects
|
|
253
|
+
def remote_query_roots(result_set, m_relation) # :nodoc:
|
|
254
|
+
plan.query_roots(result_set, m_relation.proxy(nil)).
|
|
255
|
+
map { |t| RemoteObjectProxy.new(t) }
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Returns a DRbObject on the given named constant. Use this to get a
|
|
259
|
+
# remote interface to a given object, not taking into account its
|
|
260
|
+
# 'marshallability'
|
|
261
|
+
def remote_constant(name)
|
|
262
|
+
DRbObject.new(name.to_s.constantize)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Reload the Roby framework code
|
|
266
|
+
#
|
|
267
|
+
# WARNING: does not work for now
|
|
268
|
+
def reload
|
|
269
|
+
Roby.app.reload
|
|
270
|
+
nil
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Displays the set of models as well as their superclasses
|
|
274
|
+
def models
|
|
275
|
+
task_models = []
|
|
276
|
+
Roby.execute do
|
|
277
|
+
ObjectSpace.each_object(Class) do |obj|
|
|
278
|
+
task_models << obj if obj <= Roby::Task && obj.name !~ /^Roby::/
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
task_models.map do |model|
|
|
283
|
+
"#{model} #{model.superclass}"
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Displays the set of actions which are available through the planners
|
|
288
|
+
# registered on #control. See Control#planners
|
|
289
|
+
def actions
|
|
290
|
+
control.planners.
|
|
291
|
+
map { |p| p.planning_methods_names.to_a }.
|
|
292
|
+
flatten.
|
|
293
|
+
sort
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Pretty-prints a set of tasks
|
|
297
|
+
def task_set_to_s(task_set) # :nodoc:
|
|
298
|
+
if task_set.empty?
|
|
299
|
+
return "no tasks"
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
task = task_set.map do |task|
|
|
303
|
+
state_name = %w{pending starting running finishing finished}.find do |state_name|
|
|
304
|
+
task.send("#{state_name}?")
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
start_event = task.history.find { |ev| ev.symbol == :start }
|
|
308
|
+
since = if start_event then start_event.time
|
|
309
|
+
else 'N/A'
|
|
310
|
+
end
|
|
311
|
+
{ 'Task' => task.to_s, 'Since' => since, 'State' => state_name }
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
io = StringIO.new
|
|
315
|
+
ColumnFormatter.from_hashes(task, io) { %w{Task Since State} }
|
|
316
|
+
"\n#{io.string}"
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Returns a string representing the set of running tasks
|
|
320
|
+
def running_tasks
|
|
321
|
+
Roby.execute do
|
|
322
|
+
task_set_to_s(Roby.plan.find_tasks.running.to_a)
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Returns a string representing the set of missions
|
|
327
|
+
def missions
|
|
328
|
+
Roby.execute do
|
|
329
|
+
task_set_to_s(control.plan.missions)
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Returns a string representing the set of tasks present in the plan
|
|
334
|
+
def tasks
|
|
335
|
+
Roby.execute do
|
|
336
|
+
task_set_to_s(Roby.plan.known_tasks)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def methods
|
|
341
|
+
result = super
|
|
342
|
+
result + actions.map { |n| "#{n}!" }
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Called every once in a while by RemoteInterface to read and clear the
|
|
346
|
+
# set of pending messages.
|
|
347
|
+
def poll_messages
|
|
348
|
+
result = []
|
|
349
|
+
while !pending_messages.empty?
|
|
350
|
+
msg = pending_messages.pop
|
|
351
|
+
result << msg
|
|
352
|
+
end
|
|
353
|
+
result
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Tries to find a planner method which matches +name+ with +args+. If it finds
|
|
357
|
+
# one, creates a task planned by a planning task and yields both
|
|
358
|
+
def method_missing(name, *args)
|
|
359
|
+
if name.to_s =~ /!$/
|
|
360
|
+
name = $`.to_sym
|
|
361
|
+
else
|
|
362
|
+
super
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
if args.size > 1
|
|
366
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1) in #{name}!"
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
options = args.first || {}
|
|
370
|
+
task, planner = Robot.prepare_action(name, options)
|
|
371
|
+
begin
|
|
372
|
+
Roby.wait_until(planner.event(:success)) do
|
|
373
|
+
control.plan.insert(task)
|
|
374
|
+
yield(task, planner) if block_given?
|
|
375
|
+
end
|
|
376
|
+
rescue Roby::UnreachableEvent
|
|
377
|
+
raise RuntimeError, "cannot start #{name}: #{planner.terminal_event.context.first}"
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
Roby.execute do
|
|
381
|
+
result = planner.result
|
|
382
|
+
result.on(:failed) { |ev| pending_messages << "task #{ev.task} failed" }
|
|
383
|
+
result.on(:success) { |ev| pending_messages << "task #{ev.task} finished successfully" }
|
|
384
|
+
RemoteObjectProxy.new(result)
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
|