roby 0.7.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +7 -5
- data/Manifest.txt +91 -16
- data/README.txt +24 -24
- data/Rakefile +92 -64
- data/app/config/app.yml +42 -43
- data/app/config/init.rb +26 -0
- data/benchmark/alloc_misc.rb +123 -0
- data/benchmark/discovery_latency.rb +67 -0
- data/benchmark/garbage_collection.rb +48 -0
- data/benchmark/genom.rb +31 -0
- data/benchmark/transactions.rb +62 -0
- data/bin/roby +1 -1
- data/bin/roby-log +16 -6
- data/doc/guide/.gitignore +2 -0
- data/doc/guide/config.yaml +34 -0
- data/doc/guide/ext/init.rb +14 -0
- data/doc/guide/ext/previous_next.rb +40 -0
- data/doc/guide/ext/rdoc_links.rb +33 -0
- data/doc/guide/index.rdoc +16 -0
- data/doc/guide/overview.rdoc +62 -0
- data/doc/guide/plan_modifications.rdoc +67 -0
- data/doc/guide/src/abstraction/achieve_with.page +8 -0
- data/doc/guide/src/abstraction/forwarding.page +8 -0
- data/doc/guide/src/abstraction/hierarchy.page +19 -0
- data/doc/guide/src/abstraction/index.page +28 -0
- data/doc/guide/src/abstraction/task_models.page +13 -0
- data/doc/guide/src/basics.template +6 -0
- data/doc/guide/src/basics/app.page +139 -0
- data/doc/guide/src/basics/code_examples.page +33 -0
- data/doc/guide/src/basics/dry.page +69 -0
- data/doc/guide/src/basics/errors.page +443 -0
- data/doc/guide/src/basics/events.page +179 -0
- data/doc/guide/src/basics/hierarchy.page +275 -0
- data/doc/guide/src/basics/index.page +11 -0
- data/doc/guide/src/basics/log_replay/goForward_1.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_2.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_3.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_4.png +0 -0
- data/doc/guide/src/basics/log_replay/goForward_5.png +0 -0
- data/doc/guide/src/basics/log_replay/hierarchy_error_1.png +0 -0
- data/doc/guide/src/basics/log_replay/hierarchy_error_2.png +0 -0
- data/doc/guide/src/basics/log_replay/hierarchy_error_3.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_1.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_2.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_3.png +0 -0
- data/doc/guide/src/basics/log_replay/plan_repair_4.png +0 -0
- data/doc/guide/src/basics/log_replay/roby_log_main_window.png +0 -0
- data/doc/guide/src/basics/log_replay/roby_log_relation_window.png +0 -0
- data/doc/guide/src/basics/log_replay/roby_replay_event_representation.png +0 -0
- data/doc/guide/src/basics/plan_objects.page +71 -0
- data/doc/guide/src/basics/relations_display.page +203 -0
- data/doc/guide/src/basics/roby_cycle_overview.png +0 -0
- data/doc/guide/src/basics/shell.page +102 -0
- data/doc/guide/src/basics/summary.page +32 -0
- data/doc/guide/src/basics/tasks.page +357 -0
- data/doc/guide/src/basics_shell_header.txt +16 -0
- data/doc/guide/src/cycle/cycle-overview.png +0 -0
- data/doc/guide/src/cycle/cycle-overview.svg +208 -0
- data/doc/guide/src/cycle/error_handling.page +168 -0
- data/doc/guide/src/cycle/error_instantaneous_repair.png +0 -0
- data/doc/guide/src/cycle/error_instantaneous_repair.svg +1224 -0
- data/doc/guide/src/cycle/garbage_collection.page +10 -0
- data/doc/guide/src/cycle/index.page +23 -0
- data/doc/guide/src/cycle/propagation.page +154 -0
- data/doc/guide/src/cycle/propagation_diamond.png +0 -0
- data/doc/guide/src/cycle/propagation_diamond.svg +1279 -0
- data/doc/guide/src/default.css +319 -0
- data/doc/guide/src/default.template +74 -0
- data/doc/guide/src/htmldoc.metainfo +20 -0
- data/doc/guide/src/htmldoc.virtual +18 -0
- data/doc/guide/src/images/bodybg.png +0 -0
- data/doc/guide/src/images/contbg.png +0 -0
- data/doc/guide/src/images/footerbg.png +0 -0
- data/doc/guide/src/images/gradient1.png +0 -0
- data/doc/guide/src/images/gradient2.png +0 -0
- data/doc/guide/src/index.page +7 -0
- data/doc/guide/src/introduction/index.page +29 -0
- data/doc/guide/src/introduction/install.page +133 -0
- data/doc/{papers.rdoc → guide/src/introduction/publications.page} +5 -2
- data/doc/{videos.rdoc → guide/src/introduction/videos.page} +4 -2
- data/doc/guide/src/plugins/fault_tolerance.page +44 -0
- data/doc/guide/src/plugins/index.page +11 -0
- data/doc/guide/src/plugins/subsystems.page +45 -0
- data/doc/guide/src/relations/dependency.page +89 -0
- data/doc/guide/src/relations/index.page +12 -0
- data/doc/misc/update_github +24 -0
- data/doc/tutorials/02-GoForward.rdoc +3 -3
- data/ext/graph/graph.cc +46 -0
- data/lib/roby.rb +57 -22
- data/lib/roby/app.rb +132 -112
- data/lib/roby/app/plugins/rake.rb +21 -0
- data/lib/roby/app/rake.rb +0 -7
- data/lib/roby/app/run.rb +1 -1
- data/lib/roby/app/scripts/distributed.rb +1 -2
- data/lib/roby/app/scripts/generate/bookmarks.rb +1 -1
- data/lib/roby/app/scripts/results.rb +2 -1
- data/lib/roby/app/scripts/run.rb +6 -2
- data/lib/roby/app/scripts/shell.rb +11 -11
- data/lib/roby/config.rb +1 -1
- data/lib/roby/decision_control.rb +62 -3
- data/lib/roby/distributed.rb +4 -0
- data/lib/roby/distributed/base.rb +8 -0
- data/lib/roby/distributed/communication.rb +12 -8
- data/lib/roby/distributed/connection_space.rb +61 -44
- data/lib/roby/distributed/distributed_object.rb +1 -1
- data/lib/roby/distributed/notifications.rb +22 -30
- data/lib/roby/distributed/peer.rb +13 -8
- data/lib/roby/distributed/proxy.rb +5 -5
- data/lib/roby/distributed/subscription.rb +4 -4
- data/lib/roby/distributed/transaction.rb +3 -3
- data/lib/roby/event.rb +176 -110
- data/lib/roby/exceptions.rb +12 -4
- data/lib/roby/execution_engine.rb +1604 -0
- data/lib/roby/external_process_task.rb +225 -0
- data/lib/roby/graph.rb +0 -6
- data/lib/roby/interface.rb +221 -137
- data/lib/roby/log/console.rb +5 -3
- data/lib/roby/log/data_stream.rb +94 -16
- data/lib/roby/log/dot.rb +8 -8
- data/lib/roby/log/event_stream.rb +13 -3
- data/lib/roby/log/file.rb +43 -18
- data/lib/roby/log/gui/basic_display_ui.rb +89 -0
- data/lib/roby/log/gui/chronicle_view_ui.rb +90 -0
- data/lib/roby/log/gui/data_displays.rb +4 -5
- data/lib/roby/log/gui/data_displays_ui.rb +146 -0
- data/lib/roby/log/gui/relations.rb +18 -18
- data/lib/roby/log/gui/relations_ui.rb +120 -0
- data/lib/roby/log/gui/relations_view_ui.rb +144 -0
- data/lib/roby/log/gui/replay.rb +41 -13
- data/lib/roby/log/gui/replay_controls.rb +3 -0
- data/lib/roby/log/gui/replay_controls.ui +133 -110
- data/lib/roby/log/gui/replay_controls_ui.rb +249 -0
- data/lib/roby/log/hooks.rb +19 -18
- data/lib/roby/log/logger.rb +7 -6
- data/lib/roby/log/notifications.rb +4 -4
- data/lib/roby/log/plan_rebuilder.rb +20 -22
- data/lib/roby/log/relations.rb +44 -16
- data/lib/roby/log/server.rb +1 -4
- data/lib/roby/log/timings.rb +88 -19
- data/lib/roby/plan-object.rb +135 -11
- data/lib/roby/plan.rb +408 -224
- data/lib/roby/planning/loops.rb +32 -25
- data/lib/roby/planning/model.rb +157 -51
- data/lib/roby/planning/task.rb +47 -20
- data/lib/roby/query.rb +128 -92
- data/lib/roby/relations.rb +254 -136
- data/lib/roby/relations/conflicts.rb +6 -9
- data/lib/roby/relations/dependency.rb +358 -0
- data/lib/roby/relations/ensured.rb +0 -1
- data/lib/roby/relations/error_handling.rb +0 -1
- data/lib/roby/relations/events.rb +0 -2
- data/lib/roby/relations/executed_by.rb +26 -11
- data/lib/roby/relations/planned_by.rb +14 -14
- data/lib/roby/robot.rb +46 -0
- data/lib/roby/schedulers/basic.rb +34 -0
- data/lib/roby/standalone.rb +4 -0
- data/lib/roby/standard_errors.rb +21 -15
- data/lib/roby/state/events.rb +5 -4
- data/lib/roby/support.rb +107 -6
- data/lib/roby/task-operations.rb +23 -19
- data/lib/roby/task.rb +522 -148
- data/lib/roby/task_index.rb +80 -0
- data/lib/roby/test/common.rb +283 -44
- data/lib/roby/test/distributed.rb +53 -37
- data/lib/roby/test/testcase.rb +9 -204
- data/lib/roby/test/tools.rb +3 -3
- data/lib/roby/transactions.rb +154 -111
- data/lib/roby/transactions/proxy.rb +40 -7
- data/manifest.xml +20 -0
- data/plugins/fault_injection/README.txt +0 -3
- data/plugins/fault_injection/Rakefile +2 -8
- data/plugins/fault_injection/app.rb +1 -1
- data/plugins/fault_injection/fault_injection.rb +3 -3
- data/plugins/fault_injection/test/test_fault_injection.rb +19 -25
- data/plugins/subsystems/README.txt +0 -3
- data/plugins/subsystems/Rakefile +2 -7
- data/plugins/subsystems/app.rb +27 -16
- data/plugins/subsystems/test/app/config/init.rb +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +1 -1
- data/plugins/subsystems/test/app/tasks/services.rb +1 -1
- data/plugins/subsystems/test/test_subsystems.rb +23 -16
- data/test/distributed/test_communication.rb +32 -15
- data/test/distributed/test_connection.rb +28 -26
- data/test/distributed/test_execution.rb +59 -54
- data/test/distributed/test_mixed_plan.rb +34 -34
- data/test/distributed/test_plan_notifications.rb +26 -26
- data/test/distributed/test_protocol.rb +57 -48
- data/test/distributed/test_query.rb +11 -7
- data/test/distributed/test_remote_plan.rb +71 -71
- data/test/distributed/test_transaction.rb +50 -47
- data/test/mockups/external_process +28 -0
- data/test/planning/test_loops.rb +163 -119
- data/test/planning/test_model.rb +3 -3
- data/test/planning/test_task.rb +27 -7
- data/test/relations/test_conflicts.rb +3 -3
- data/test/relations/test_dependency.rb +324 -0
- data/test/relations/test_ensured.rb +2 -2
- data/test/relations/test_executed_by.rb +94 -19
- data/test/relations/test_planned_by.rb +11 -9
- data/test/suite_core.rb +6 -3
- data/test/suite_distributed.rb +1 -0
- data/test/suite_planning.rb +1 -0
- data/test/suite_relations.rb +2 -2
- data/test/tasks/test_external_process.rb +126 -0
- data/test/{test_thread_task.rb → tasks/test_thread_task.rb} +17 -20
- data/test/test_bgl.rb +21 -1
- data/test/test_event.rb +229 -155
- data/test/test_exceptions.rb +79 -80
- data/test/test_execution_engine.rb +987 -0
- data/test/test_gui.rb +1 -1
- data/test/test_interface.rb +11 -5
- data/test/test_log.rb +18 -7
- data/test/test_log_server.rb +1 -0
- data/test/test_plan.rb +229 -395
- data/test/test_query.rb +193 -35
- data/test/test_relations.rb +88 -8
- data/test/test_state.rb +55 -37
- data/test/test_support.rb +1 -1
- data/test/test_task.rb +371 -218
- data/test/test_testcase.rb +32 -16
- data/test/test_transactions.rb +211 -170
- data/test/test_transactions_proxy.rb +37 -19
- metadata +169 -71
- data/.gitignore +0 -29
- data/doc/styles/allison.css +0 -314
- data/doc/styles/allison.js +0 -316
- data/doc/styles/allison.rb +0 -276
- data/doc/styles/jamis.rb +0 -593
- data/lib/roby/control.rb +0 -746
- data/lib/roby/executives/simple.rb +0 -30
- data/lib/roby/propagation.rb +0 -562
- data/lib/roby/relations/hierarchy.rb +0 -239
- data/lib/roby/transactions/updates.rb +0 -139
- data/test/relations/test_hierarchy.rb +0 -158
- data/test/test_control.rb +0 -399
- data/test/test_propagation.rb +0 -210
data/lib/roby/task.rb
CHANGED
@@ -1,17 +1,14 @@
|
|
1
|
-
require 'roby/plan-object'
|
2
|
-
require 'roby/exceptions'
|
3
|
-
require 'roby/event'
|
4
|
-
require 'utilrb/module/attr_predicate'
|
5
|
-
|
6
1
|
module Roby
|
7
2
|
class TaskModelTag < Module
|
8
3
|
module ClassExtension
|
9
4
|
# Returns the list of static arguments required by this task model
|
10
5
|
def arguments(*new_arguments)
|
11
6
|
new_arguments.each do |arg_name|
|
7
|
+
arg_name = arg_name.to_sym
|
12
8
|
argument_set << arg_name.to_sym
|
13
9
|
unless method_defined?(arg_name)
|
14
10
|
define_method(arg_name) { arguments[arg_name] }
|
11
|
+
define_method("#{arg_name}=") { |value| arguments[arg_name] = value }
|
15
12
|
end
|
16
13
|
end
|
17
14
|
|
@@ -19,6 +16,27 @@ module Roby
|
|
19
16
|
end
|
20
17
|
# Declares a set of arguments required by this task model
|
21
18
|
def argument(*args); arguments(*args) end
|
19
|
+
# The part of +arguments+ that is meaningful for this task model
|
20
|
+
def meaningful_arguments(arguments)
|
21
|
+
self_arguments = self.arguments.to_set
|
22
|
+
arguments.to_hash.delete_if do |key, _|
|
23
|
+
!self_arguments.include?(key)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Checks if this model fullfills everything in +models+
|
28
|
+
def fullfills?(models)
|
29
|
+
if !models.respond_to?(:each)
|
30
|
+
models = [models]
|
31
|
+
end
|
32
|
+
|
33
|
+
for tag in models
|
34
|
+
if !has_ancestor?(tag)
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
true
|
39
|
+
end
|
22
40
|
end
|
23
41
|
include TaskModelTag::ClassExtension
|
24
42
|
|
@@ -46,6 +64,8 @@ module Roby
|
|
46
64
|
# The task which fired this event
|
47
65
|
attr_reader :task
|
48
66
|
|
67
|
+
def model; self.class end
|
68
|
+
|
49
69
|
def initialize(task, generator, propagation_id, context, time = Time.now)
|
50
70
|
@task = task
|
51
71
|
@terminal_flag = generator.terminal_flag
|
@@ -88,7 +108,7 @@ module Roby
|
|
88
108
|
# responds to #call
|
89
109
|
def self.controlable?; respond_to?(:call) end
|
90
110
|
# If the event is controlable
|
91
|
-
def controlable?;
|
111
|
+
def controlable?; model.controlable? end
|
92
112
|
class << self
|
93
113
|
# Called by Task.update_terminal_flag to update the flag
|
94
114
|
attr_writer :terminal
|
@@ -104,7 +124,7 @@ module Roby
|
|
104
124
|
# The event symbol
|
105
125
|
def self.symbol; @symbol end
|
106
126
|
# The event symbol
|
107
|
-
def symbol;
|
127
|
+
def symbol; model.symbol end
|
108
128
|
end
|
109
129
|
|
110
130
|
# A task event model bound to a particular task instance
|
@@ -140,9 +160,22 @@ module Roby
|
|
140
160
|
# by task.plan=. It is redefined here for performance reasons.
|
141
161
|
attr_accessor :plan
|
142
162
|
|
143
|
-
#
|
163
|
+
# Check that the event can be emitted
|
164
|
+
def emitting(context)
|
165
|
+
task.emitting_event(self, context)
|
166
|
+
super if defined? super
|
167
|
+
end
|
168
|
+
|
144
169
|
def fire(event)
|
145
170
|
task.fire_event(event)
|
171
|
+
super if defined? super
|
172
|
+
end
|
173
|
+
|
174
|
+
def emit_failed(*reason)
|
175
|
+
if symbol == :start
|
176
|
+
task.failed_to_start = true
|
177
|
+
task.plan.task_index.set_state(task, :failed?)
|
178
|
+
end
|
146
179
|
super
|
147
180
|
end
|
148
181
|
|
@@ -153,13 +186,13 @@ module Roby
|
|
153
186
|
super if defined? super
|
154
187
|
if task.finished? && !terminal?
|
155
188
|
raise CommandFailed.new(nil, self),
|
156
|
-
"#{symbol}!(#{context})
|
189
|
+
"#{symbol}!(#{context}) called by #{plan.engine.propagation_sources.to_a} but the task has finished. Task has been terminated by #{task.event(:stop).history.first.sources}."
|
157
190
|
elsif task.pending? && symbol != :start
|
158
191
|
raise CommandFailed.new(nil, self),
|
159
|
-
"#{symbol}!(#{context})
|
192
|
+
"#{symbol}!(#{context}) called by #{plan.engine.propagation_sources.to_a} but the task has never been started"
|
160
193
|
elsif task.running? && symbol == :start
|
161
194
|
raise CommandFailed.new(nil, self),
|
162
|
-
"#{symbol}!(#{context})
|
195
|
+
"#{symbol}!(#{context}) called by #{plan.engine.propagation_sources.to_a} but the task is already running. Task has been started by #{task.event(:start).history.first.sources}."
|
163
196
|
end
|
164
197
|
end
|
165
198
|
|
@@ -219,7 +252,7 @@ module Roby
|
|
219
252
|
task.update_terminal_flag
|
220
253
|
end
|
221
254
|
end
|
222
|
-
def new(context); event_model.new(task, self,
|
255
|
+
def new(context); event_model.new(task, self, plan.engine.propagation_id, context) end
|
223
256
|
|
224
257
|
def to_s
|
225
258
|
"#{task}/#{symbol}"
|
@@ -238,8 +271,8 @@ module Roby
|
|
238
271
|
end
|
239
272
|
|
240
273
|
if child_task
|
241
|
-
unless task.
|
242
|
-
task.
|
274
|
+
unless task.depends_on?(child_task, false)
|
275
|
+
task.depends_on child_task,
|
243
276
|
:success => [child_event.symbol],
|
244
277
|
:remove_when_done => true
|
245
278
|
end
|
@@ -249,6 +282,13 @@ module Roby
|
|
249
282
|
end
|
250
283
|
end
|
251
284
|
|
285
|
+
# Refines exceptions that may be thrown by #call_without_propagation
|
286
|
+
def call_without_propagation(context)
|
287
|
+
super
|
288
|
+
rescue EventNotExecutable => e
|
289
|
+
refine_exception(e)
|
290
|
+
end
|
291
|
+
|
252
292
|
# Checks that the event can be called. Raises various exception
|
253
293
|
# when it is not the case.
|
254
294
|
def check_call_validity
|
@@ -268,23 +308,29 @@ module Roby
|
|
268
308
|
|
269
309
|
def refine_exception (e)
|
270
310
|
if task.partially_instanciated?
|
271
|
-
raise EventNotExecutable.new(self), "#{
|
311
|
+
raise EventNotExecutable.new(self), "#{symbol}! called on #{task} which is partially instanciated\n" +
|
272
312
|
"The following arguments were not set: \n" +
|
273
313
|
task.list_unset_arguments.map {|n| "\t#{n}"}.join("\n")+"\n"
|
274
314
|
#
|
275
315
|
elsif !plan
|
276
|
-
raise EventNotExecutable.new(self), "#{
|
316
|
+
raise EventNotExecutable.new(self), "#{symbol}! called on #{task} but the task is in no plan"
|
277
317
|
elsif !plan.executable?
|
278
|
-
raise EventNotExecutable.new(self), "#{
|
318
|
+
raise EventNotExecutable.new(self), "#{symbol}! called on #{task} but the plan is not executable"
|
279
319
|
elsif task.abstract?
|
280
|
-
raise EventNotExecutable.new(self), "#{
|
320
|
+
raise EventNotExecutable.new(self), "#{symbol}! called on #{task} but the task is abstract"
|
281
321
|
else
|
282
|
-
raise EventNotExecutable.new(self), "#{
|
322
|
+
raise EventNotExecutable.new(self), "#{symbol}! called on #{task} which is not executable: #{e.message}"
|
283
323
|
end
|
284
324
|
end
|
285
325
|
|
286
326
|
end
|
287
327
|
|
328
|
+
# Class that handles task arguments. They are handled specially as the
|
329
|
+
# arguments cannot be overwritten and can not be changed by a task that is
|
330
|
+
# not owned.
|
331
|
+
#
|
332
|
+
# Moreover, two hooks #updating and #updated allow to hook into the argument
|
333
|
+
# update system.
|
288
334
|
class TaskArguments < Hash
|
289
335
|
private :delete, :delete_if
|
290
336
|
|
@@ -305,6 +351,7 @@ module Roby
|
|
305
351
|
|
306
352
|
alias :update! :[]=
|
307
353
|
def []=(key, value)
|
354
|
+
key = key.to_sym if key.respond_to?(:to_str)
|
308
355
|
if writable?(key)
|
309
356
|
if !task.read_write?
|
310
357
|
raise OwnershipError, "cannot change the argument set of a task which is not owned #{task} is owned by #{task.owners} and #{task.plan} by #{task.plan.owners}"
|
@@ -320,6 +367,11 @@ module Roby
|
|
320
367
|
def updating; super if defined? super end
|
321
368
|
def updated; super if defined? super end
|
322
369
|
|
370
|
+
def [](key)
|
371
|
+
key = key.to_sym if key.respond_to?(:to_str)
|
372
|
+
super(key)
|
373
|
+
end
|
374
|
+
|
323
375
|
alias :do_merge! :merge!
|
324
376
|
def merge!(hash)
|
325
377
|
super do |key, old, new|
|
@@ -363,7 +415,7 @@ module Roby
|
|
363
415
|
# end
|
364
416
|
#
|
365
417
|
# event :other_event do |context|
|
366
|
-
#
|
418
|
+
# engine.once { emit :other_event }
|
367
419
|
# end
|
368
420
|
# end
|
369
421
|
#
|
@@ -371,6 +423,13 @@ module Roby
|
|
371
423
|
# immediately emitted, and in the second case it will be emitted at the
|
372
424
|
# beginning of the next execution cycle.
|
373
425
|
#
|
426
|
+
# === Task relations
|
427
|
+
#
|
428
|
+
# Task relations are defined in the TaskStructure RelationSpace instance.
|
429
|
+
# See TaskStructure documentation for the list of special methods defined
|
430
|
+
# by the various graphs, and the TaskStructure namespace for the name and
|
431
|
+
# purpose of the various relation graphs themselves.
|
432
|
+
#
|
374
433
|
# === Executability
|
375
434
|
#
|
376
435
|
# By default, a task is not executable, which means that no event command
|
@@ -420,7 +479,7 @@ module Roby
|
|
420
479
|
class_eval do
|
421
480
|
# Remove event models
|
422
481
|
events.each_key do |ev_symbol|
|
423
|
-
remove_const ev_symbol.to_s.
|
482
|
+
remove_const ev_symbol.to_s.camelcase(true)
|
424
483
|
end
|
425
484
|
|
426
485
|
[@events, @signal_sets, @forwarding_sets, @causal_link_sets,
|
@@ -461,6 +520,123 @@ module Roby
|
|
461
520
|
EOD
|
462
521
|
end
|
463
522
|
|
523
|
+
##
|
524
|
+
# :singleton-method: signals
|
525
|
+
# :call-seq:
|
526
|
+
# task_model.signals(event_model) => [target_event_models]
|
527
|
+
#
|
528
|
+
# Returns the set of model-level signal targets for the given event.
|
529
|
+
|
530
|
+
##
|
531
|
+
# :singleton-method: each_signal
|
532
|
+
# :call-seq:
|
533
|
+
# task_model.each_signal(event_model) do |target_event_model|
|
534
|
+
# end
|
535
|
+
#
|
536
|
+
# Enumerates the set of model-level causal links that are defined for
|
537
|
+
# the given event. It enumerates all the ones defined on this model
|
538
|
+
# (using Task::signal) and also on its parent classes.
|
539
|
+
|
540
|
+
##
|
541
|
+
# :method: each_signal
|
542
|
+
# :call-seq:
|
543
|
+
# task.each_signal(event_model) do |target_event_model|
|
544
|
+
# end
|
545
|
+
#
|
546
|
+
# Enumerates the set of model-level causal links that are defined for
|
547
|
+
# the given event. It enumerates all the ones defined on this task's model
|
548
|
+
# (using Task::signal) and also on its parent classes.
|
549
|
+
|
550
|
+
|
551
|
+
|
552
|
+
|
553
|
+
##
|
554
|
+
# :singleton-method: forwardings
|
555
|
+
# :call-seq:
|
556
|
+
# task_model.forwardings(event_model) => [target_event_models]
|
557
|
+
#
|
558
|
+
# Returns the set of model-level forwarding targets for the given event.
|
559
|
+
|
560
|
+
##
|
561
|
+
# :singleton-method: each_forwarding
|
562
|
+
# :call-seq:
|
563
|
+
# task_model.each_forwarding(event_model) do |target_event_model|
|
564
|
+
# end
|
565
|
+
#
|
566
|
+
# Enumerates the set of model-level causal links that are defined for
|
567
|
+
# the given event. It enumerates all the ones defined on this model
|
568
|
+
# (using Task::forward) and also on its parent classes.
|
569
|
+
|
570
|
+
##
|
571
|
+
# :method: each_forwarding
|
572
|
+
# :call-seq:
|
573
|
+
# task.each_forwarding(event_model) do |target_event_model|
|
574
|
+
# end
|
575
|
+
#
|
576
|
+
# Enumerates the set of model-level causal links that are defined for
|
577
|
+
# the given event. It enumerates all the ones defined on this task's
|
578
|
+
# model (using Task::forward) and also on its parent classes.
|
579
|
+
|
580
|
+
|
581
|
+
|
582
|
+
|
583
|
+
##
|
584
|
+
# :singleton-method: causal_links
|
585
|
+
# :call-seq:
|
586
|
+
# task_model.causal_links(event_model) => [target_event_models]
|
587
|
+
#
|
588
|
+
# Returns the set of model-level causal_link targets for the given event.
|
589
|
+
|
590
|
+
##
|
591
|
+
# :singleton-method: each_causal_link
|
592
|
+
# :call-seq:
|
593
|
+
# task_model.each_causal_link(event_model) do |target_event_model|
|
594
|
+
# end
|
595
|
+
#
|
596
|
+
# Enumerates the set of model-level causal links that are defined for
|
597
|
+
# the given event. It enumerates all the ones defined on this model
|
598
|
+
# (using Task::causal_link) and also on its parent classes.
|
599
|
+
|
600
|
+
##
|
601
|
+
# :method: each_causal_link
|
602
|
+
# :call-seq:
|
603
|
+
# task.each_causal_link(event_model) do |target_event_model|
|
604
|
+
# end
|
605
|
+
#
|
606
|
+
# Enumerates the set of model-level causal links that are defined for
|
607
|
+
# the given event. It enumerates all the ones defined on this task's model
|
608
|
+
# (using Task::causal_link) and also on its parent classes.
|
609
|
+
|
610
|
+
|
611
|
+
|
612
|
+
|
613
|
+
##
|
614
|
+
# :singleton-method: handlers
|
615
|
+
# :call-seq:
|
616
|
+
# task_model.handlers(event_model) => [target_event_models]
|
617
|
+
#
|
618
|
+
# Returns the set of model-level event handlers for the given event.
|
619
|
+
|
620
|
+
##
|
621
|
+
# :singleton-method: each_handler
|
622
|
+
# :call-seq:
|
623
|
+
# task_model.each_handler(event_model) do |target_event_model|
|
624
|
+
# end
|
625
|
+
#
|
626
|
+
# Enumerates the set of model-level event handlers that are defined for
|
627
|
+
# the given event. It enumerates all handlers defined on the instance's
|
628
|
+
# task model and its parent classes.
|
629
|
+
|
630
|
+
##
|
631
|
+
# :method: each_handler
|
632
|
+
# :call-seq:
|
633
|
+
# task.each_handler(event_model) do |target_event_model|
|
634
|
+
# end
|
635
|
+
#
|
636
|
+
# Enumerates the set of model-level event handlers that are defined for
|
637
|
+
# the given event. It enumerates all handlers defined on the instance's
|
638
|
+
# task model and its parent classes.
|
639
|
+
|
464
640
|
model_attribute_list('signal')
|
465
641
|
model_attribute_list('forwarding')
|
466
642
|
model_attribute_list('causal_link')
|
@@ -469,13 +645,17 @@ module Roby
|
|
469
645
|
|
470
646
|
# The task arguments as symbol => value associative container
|
471
647
|
attr_reader :arguments
|
472
|
-
|
648
|
+
|
649
|
+
# The part of +arguments+ that is meaningful for this task model. I.e.
|
650
|
+
# it returns the set of elements in the +arguments+ property that define
|
651
|
+
# arguments listed in the task model
|
473
652
|
def meaningful_arguments(task_model = self.model)
|
474
653
|
arguments.slice(*task_model.arguments)
|
475
654
|
end
|
655
|
+
|
476
656
|
# The task name
|
477
657
|
def name
|
478
|
-
@name ||= "#{model.name || self.class.name}
|
658
|
+
@name ||= "#{model.name || self.class.name}:0x#{address.to_s(16)}"
|
479
659
|
end
|
480
660
|
|
481
661
|
# This predicate is true if this task is a mission for its owners. If
|
@@ -484,6 +664,7 @@ module Roby
|
|
484
664
|
|
485
665
|
def inspect
|
486
666
|
state = if pending? then 'pending'
|
667
|
+
elsif failed_to_start? then 'failed to start'
|
487
668
|
elsif starting? then 'starting'
|
488
669
|
elsif running? then 'running'
|
489
670
|
elsif finishing? then 'finishing'
|
@@ -499,23 +680,34 @@ module Roby
|
|
499
680
|
# * the task shall have a +start+ event
|
500
681
|
# * the task shall have at least one terminal event. If no +stop+ event
|
501
682
|
# is defined, then all terminal events are aliased to +stop+
|
502
|
-
def initialize(arguments =
|
683
|
+
def initialize(arguments = Hash.new) #:yields: task_object
|
503
684
|
super() if defined? super
|
504
685
|
|
505
686
|
@arguments = TaskArguments.new(self)
|
506
|
-
|
687
|
+
arguments.each do |key, value|
|
688
|
+
if self.respond_to?("#{key}=")
|
689
|
+
self.send("#{key}=", value)
|
690
|
+
else
|
691
|
+
@arguments[key] = value
|
692
|
+
end
|
693
|
+
end
|
507
694
|
|
508
695
|
@model = self.class
|
509
696
|
|
510
697
|
yield(self) if block_given?
|
698
|
+
# Create the EventGenerator instances that represent this task's
|
699
|
+
# events. Note that the event relations are instanciated by
|
700
|
+
# Plan#discover when this task is included in a plan, thus avoiding
|
701
|
+
# filling up the relation graphs with unused relations.
|
511
702
|
initialize_events
|
512
703
|
end
|
513
704
|
|
514
705
|
|
515
706
|
# Lists all arguments, that are set to be needed via the :argument
|
516
707
|
# syntax but are not set.
|
708
|
+
#
|
517
709
|
# This is needed for debugging purposes.
|
518
|
-
def list_unset_arguments
|
710
|
+
def list_unset_arguments # :nodoc:
|
519
711
|
ret = Array.new
|
520
712
|
model.arguments.each { |name|
|
521
713
|
if !arguments.has_key?(name) then
|
@@ -523,7 +715,6 @@ module Roby
|
|
523
715
|
end }
|
524
716
|
ret
|
525
717
|
end
|
526
|
-
|
527
718
|
|
528
719
|
# Helper methods which creates all the necessary TaskEventGenerator
|
529
720
|
# objects and stores them in the #bound_events map
|
@@ -540,6 +731,25 @@ module Roby
|
|
540
731
|
|
541
732
|
def model; self.class end
|
542
733
|
|
734
|
+
# Returns for how many seconds this task is running. Returns nil if
|
735
|
+
# the task is not running.
|
736
|
+
def lifetime
|
737
|
+
if running?
|
738
|
+
Time.now - history.first.time
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
# Returns when this task has been started
|
743
|
+
def start_time
|
744
|
+
if running?
|
745
|
+
history.first.time
|
746
|
+
end
|
747
|
+
end
|
748
|
+
|
749
|
+
def create_fresh_copy
|
750
|
+
model.new(arguments.dup)
|
751
|
+
end
|
752
|
+
|
543
753
|
def initialize_copy(old) # :nodoc:
|
544
754
|
super
|
545
755
|
|
@@ -551,7 +761,7 @@ module Roby
|
|
551
761
|
arguments.instance_variable_set(:@task, self)
|
552
762
|
|
553
763
|
initialize_events
|
554
|
-
plan.
|
764
|
+
plan.add(self)
|
555
765
|
end
|
556
766
|
|
557
767
|
def instantiate_model_event_relations
|
@@ -569,7 +779,7 @@ module Roby
|
|
569
779
|
|
570
780
|
for signalled in signalled_events
|
571
781
|
signalled = bound_events[signalled]
|
572
|
-
generator.
|
782
|
+
generator.signals signalled
|
573
783
|
left_border.delete(signalled)
|
574
784
|
end
|
575
785
|
end
|
@@ -582,7 +792,7 @@ module Roby
|
|
582
792
|
|
583
793
|
for signalled in signalled_events
|
584
794
|
signalled = bound_events[signalled]
|
585
|
-
generator.
|
795
|
+
generator.forward_to signalled
|
586
796
|
left_border.delete(signalled)
|
587
797
|
end
|
588
798
|
end
|
@@ -646,29 +856,44 @@ module Roby
|
|
646
856
|
end
|
647
857
|
|
648
858
|
class << self
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
859
|
+
##
|
860
|
+
# :singleton-method: abstract?
|
861
|
+
#
|
862
|
+
# True if this task is an abstract task.
|
863
|
+
#
|
864
|
+
# See Task::abstract() for more information.
|
865
|
+
attr_predicate :abstract
|
866
|
+
|
867
|
+
# Declare that this task model defines abstract tasks. Abstract
|
868
|
+
# tasks can be used to represent an action, without specifically
|
869
|
+
# representing how this action should be done.
|
870
|
+
#
|
871
|
+
# Instances of abstract task models are not executable, i.e. they
|
872
|
+
# cannot be started.
|
873
|
+
#
|
874
|
+
# See also #abstract? and #executable?
|
656
875
|
def abstract
|
657
876
|
@abstract = true
|
658
877
|
end
|
659
878
|
|
660
|
-
# Declare that
|
661
|
-
#
|
662
|
-
#
|
663
|
-
#
|
879
|
+
# Declare that tasks of this model can finish by simply emitting
|
880
|
+
# +stop+. Use it this way:
|
881
|
+
#
|
882
|
+
# class MyTask < Roby::Task
|
883
|
+
# terminates
|
884
|
+
# end
|
885
|
+
#
|
886
|
+
# It adds a +stop!+ command that emits the +failed+ event.
|
664
887
|
def terminates
|
665
888
|
event :failed, :command => true, :terminal => true
|
666
889
|
interruptible
|
667
890
|
end
|
668
891
|
|
669
|
-
#
|
670
|
-
#
|
671
|
-
#
|
892
|
+
# Declare that tasks of this model can be interrupted. It does so by
|
893
|
+
# defining a command for +stop+, which in effect calls the command
|
894
|
+
# for +failed+.
|
895
|
+
#
|
896
|
+
# Raises ArgumentError if failed is not controlable.
|
672
897
|
def interruptible
|
673
898
|
if !has_event?(:failed) || !event_model(:failed).controlable?
|
674
899
|
raise ArgumentError, "failed is not controlable"
|
@@ -683,7 +908,7 @@ module Roby
|
|
683
908
|
end
|
684
909
|
|
685
910
|
def setup_poll_method(block) # :nodoc:
|
686
|
-
define_method(:poll) do
|
911
|
+
define_method(:poll) do |plan|
|
687
912
|
return unless self_owned?
|
688
913
|
begin
|
689
914
|
poll_handler
|
@@ -695,9 +920,17 @@ module Roby
|
|
695
920
|
define_method(:poll_handler, &block)
|
696
921
|
end
|
697
922
|
|
698
|
-
#
|
699
|
-
#
|
700
|
-
#
|
923
|
+
# Declares that the given block should be called at each execution
|
924
|
+
# cycle, when the task is running. Use it that way:
|
925
|
+
#
|
926
|
+
# class MyTask < Roby::Task
|
927
|
+
# poll do
|
928
|
+
# ... do something ...
|
929
|
+
# end
|
930
|
+
# end
|
931
|
+
#
|
932
|
+
# If the given polling block raises an exception, the task will be
|
933
|
+
# terminated by emitting its +failed+ event.
|
701
934
|
def poll(&block)
|
702
935
|
if !block_given?
|
703
936
|
raise "no block given"
|
@@ -705,8 +938,8 @@ module Roby
|
|
705
938
|
|
706
939
|
setup_poll_method(block)
|
707
940
|
|
708
|
-
on(:start) {
|
709
|
-
on(:stop) {
|
941
|
+
on(:start) { |ev| ev.task.plan.engine.propagation_handlers << method(:poll) }
|
942
|
+
on(:stop) { |ev| ev.task.plan.engine.propagation_handlers.delete(method(:poll)) }
|
710
943
|
end
|
711
944
|
end
|
712
945
|
|
@@ -715,8 +948,13 @@ module Roby
|
|
715
948
|
|
716
949
|
# Returns true if this task is from an abstract model. If it is the
|
717
950
|
# case, the task is not executable.
|
718
|
-
|
719
|
-
|
951
|
+
#
|
952
|
+
# See Task::abstract for more details.
|
953
|
+
def abstract?; model.abstract? end
|
954
|
+
# True if this task is executable. A task is not executable if it is
|
955
|
+
# abstract or partially instanciated.
|
956
|
+
#
|
957
|
+
# See #abstract? and #partially_instanciated?
|
720
958
|
def executable?; !abstract? && !partially_instanciated? && super end
|
721
959
|
# Returns true if this task's stop event is controlable
|
722
960
|
def interruptible?; event(:stop).controlable? end
|
@@ -745,14 +983,14 @@ module Roby
|
|
745
983
|
# can either be a event class or an event name.
|
746
984
|
def has_event?(event_model)
|
747
985
|
bound_events.has_key?(event_model) ||
|
748
|
-
|
986
|
+
model.has_event?(event_model)
|
749
987
|
end
|
750
988
|
|
751
989
|
# True if this task is starting, i.e. if its start event is pending
|
752
990
|
# (has been called, but is not emitted yet)
|
753
991
|
def starting?; event(:start).pending? end
|
754
992
|
# True if this task has never been started
|
755
|
-
def pending?; !starting? && !started? end
|
993
|
+
def pending?; !failed_to_start? && !starting? && !started? end
|
756
994
|
# True if this task is currently running (i.e. is has already started,
|
757
995
|
# and is not finished)
|
758
996
|
def running?; started? && !finished? end
|
@@ -767,14 +1005,24 @@ module Roby
|
|
767
1005
|
attr_predicate :started?, true
|
768
1006
|
attr_predicate :finished?, true
|
769
1007
|
attr_predicate :success?, true
|
1008
|
+
attr_predicate :failed_to_start?, true
|
770
1009
|
|
771
1010
|
# True if the +failed+ event of this task has been fired
|
772
|
-
def failed?; finished? && @success == false end
|
1011
|
+
def failed?; failed_to_start? || (finished? && @success == false) end
|
773
1012
|
|
774
|
-
#
|
775
|
-
|
776
|
-
|
777
|
-
|
1013
|
+
# call-seq:
|
1014
|
+
# task.clear_relations => task
|
1015
|
+
#
|
1016
|
+
# Remove all relations in which +self+ or its event are involved
|
1017
|
+
#--
|
1018
|
+
# The including_events flag is here for the benefit of
|
1019
|
+
# Transactions::Proxy::Task only
|
1020
|
+
def clear_relations(including_events = true)
|
1021
|
+
if including_events
|
1022
|
+
each_event { |ev| ev.clear_relations }
|
1023
|
+
end
|
1024
|
+
super()
|
1025
|
+
self
|
778
1026
|
end
|
779
1027
|
|
780
1028
|
# Update the terminal flag for the event models that are defined in
|
@@ -863,8 +1111,8 @@ module Roby
|
|
863
1111
|
end
|
864
1112
|
end
|
865
1113
|
|
866
|
-
# Returns a
|
867
|
-
#
|
1114
|
+
# Returns a list of Event objects, for all events that have been fired
|
1115
|
+
# by this task. The list is sorted by emission times.
|
868
1116
|
def history
|
869
1117
|
history = []
|
870
1118
|
each_event do |event|
|
@@ -874,9 +1122,9 @@ module Roby
|
|
874
1122
|
history.sort_by { |ev| ev.time }
|
875
1123
|
end
|
876
1124
|
|
877
|
-
# Returns the set of tasks directly related to this task, either
|
878
|
-
#
|
879
|
-
#
|
1125
|
+
# Returns the set of tasks directly related to this task, either because
|
1126
|
+
# of task relations or because of task events that are related to other
|
1127
|
+
# task events
|
880
1128
|
def related_tasks(result = nil)
|
881
1129
|
result = related_objects(nil, result)
|
882
1130
|
each_event do |ev|
|
@@ -898,29 +1146,41 @@ module Roby
|
|
898
1146
|
|
899
1147
|
# This method is called by TaskEventGenerator#fire just before the event handlers
|
900
1148
|
# and commands are called
|
901
|
-
def
|
1149
|
+
def emitting_event(event, context) # :nodoc:
|
902
1150
|
if !executable?
|
903
|
-
raise TaskNotExecutable.new(self), "trying to
|
1151
|
+
raise TaskNotExecutable.new(self), "trying to emit #{symbol} on #{self} but #{self} is not executable"
|
904
1152
|
end
|
905
1153
|
|
906
1154
|
if finished? && !event.terminal?
|
907
|
-
raise EmissionFailed.new(nil, self),
|
908
|
-
"emit(#{event.symbol}
|
1155
|
+
raise EmissionFailed.new(nil, self),
|
1156
|
+
"emit(#{event.symbol}, #{context}) called by #{plan.engine.propagation_sources.to_a} but the task has finished. Task has been terminated by #{event(:stop).history.first.sources}."
|
909
1157
|
elsif pending? && event.symbol != :start
|
910
|
-
raise EmissionFailed.new(nil, self),
|
911
|
-
"emit(#{event.symbol}
|
1158
|
+
raise EmissionFailed.new(nil, self),
|
1159
|
+
"emit(#{event.symbol}, #{context}) called by #{plan.engine.propagation_sources.to_a} but the task has never been started"
|
912
1160
|
elsif running? && event.symbol == :start
|
913
|
-
raise EmissionFailed.new(nil, self),
|
914
|
-
"emit(#{event.symbol}
|
1161
|
+
raise EmissionFailed.new(nil, self),
|
1162
|
+
"emit(#{event.symbol}, #{context}) called by #{plan.engine.propagation_sources.to_a} but the task is already running. Task has been started by #{event(:start).history.first.sources}."
|
915
1163
|
end
|
916
1164
|
|
917
|
-
|
1165
|
+
super if defined? super
|
1166
|
+
end
|
918
1167
|
|
1168
|
+
# Hook called by TaskEventGenerator#fired when one of this task's events
|
1169
|
+
# is fired.
|
1170
|
+
def fire_event(event)
|
1171
|
+
update_task_status(event)
|
919
1172
|
super if defined? super
|
920
1173
|
end
|
921
1174
|
|
922
1175
|
# The event which has finished the task (if there is one)
|
923
1176
|
attr_reader :terminal_event
|
1177
|
+
|
1178
|
+
# The event that caused this task to fail. This is equivalent to taking
|
1179
|
+
# the first emitted element of
|
1180
|
+
# task.event(:failed).last.task_sources
|
1181
|
+
#
|
1182
|
+
# It is only much more efficient
|
1183
|
+
attr_reader :failure_event
|
924
1184
|
|
925
1185
|
# Call to update the task status because of +event+
|
926
1186
|
def update_task_status(event) # :nodoc:
|
@@ -934,6 +1194,7 @@ module Roby
|
|
934
1194
|
self.success = false
|
935
1195
|
self.finished = true
|
936
1196
|
@terminal_event ||= event
|
1197
|
+
@failure_event ||= event
|
937
1198
|
elsif event.terminal? && !finished?
|
938
1199
|
plan.task_index.set_state(self, :finished?)
|
939
1200
|
self.finished = true
|
@@ -974,22 +1235,42 @@ module Roby
|
|
974
1235
|
end
|
975
1236
|
|
976
1237
|
# call-seq:
|
977
|
-
# on(event, task[, event1, event2, ...])
|
978
1238
|
# on(event) { |event| ... }
|
979
|
-
# on(event[, task, event1, event2, ...]) { |event| ... }
|
980
|
-
# on(event, task[, event1, event2, ...], delay)
|
981
|
-
#
|
982
|
-
# Adds a signal from the given event to the specified targets, and/or
|
983
|
-
# defines an event handler. Note that <tt>on(event, task)</tt> is
|
984
|
-
# equivalent to <tt>on(event, task, event)</tt>
|
985
1239
|
#
|
986
|
-
#
|
987
|
-
# much time as specified. See EventGenerator#signal for valid values.
|
1240
|
+
# Defines an event handler for the given event.
|
988
1241
|
def on(event_model, to = nil, *to_task_events, &user_handler)
|
989
|
-
|
1242
|
+
if to
|
1243
|
+
Roby.warn_deprecated "on(event_name, task, target_events) has been replaced by #signals"
|
1244
|
+
elsif !(to || user_handler)
|
990
1245
|
raise ArgumentError, "you must provide either a task or an event handler (got nil for both)"
|
991
1246
|
end
|
992
1247
|
|
1248
|
+
if to
|
1249
|
+
signals(event_model, to, *to_task_events)
|
1250
|
+
end
|
1251
|
+
if user_handler
|
1252
|
+
generator = event(event_model)
|
1253
|
+
generator.on(&user_handler)
|
1254
|
+
end
|
1255
|
+
self
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
# call-seq:
|
1259
|
+
# signals source_event, dest_task, ev1, ev2, ev3, ...
|
1260
|
+
# signals source_event, dest_task, ev1, ev2, ev3, delay_options
|
1261
|
+
#
|
1262
|
+
# Creates a signal from +source_event+, which is an event name of
|
1263
|
+
# +self+, to the listed events of +dest_task+. The destination events
|
1264
|
+
# will be called when the source event is emitted.
|
1265
|
+
#
|
1266
|
+
# To simply emit target events (i.e. not calling the event's commands),
|
1267
|
+
# use #forwards
|
1268
|
+
#
|
1269
|
+
# Optionally, a delay can be added to the signal. +delay_options+ can be
|
1270
|
+
# either:
|
1271
|
+
# :delay => relative_delay_in_seconds
|
1272
|
+
# :at => absolute_time
|
1273
|
+
def signals(event_model, to, *to_task_events)
|
993
1274
|
generator = event(event_model)
|
994
1275
|
if Hash === to_task_events.last
|
995
1276
|
delay = to_task_events.pop
|
@@ -997,6 +1278,7 @@ module Roby
|
|
997
1278
|
to_events = case to
|
998
1279
|
when Task
|
999
1280
|
if to_task_events.empty?
|
1281
|
+
Roby.warn_deprecated "signals(event_name, target_task) is deprecated. You must now always specify the target event name"
|
1000
1282
|
[to.event(generator.symbol)]
|
1001
1283
|
else
|
1002
1284
|
to_task_events.map { |ev_model| to.event(ev_model) }
|
@@ -1006,27 +1288,35 @@ module Roby
|
|
1006
1288
|
end
|
1007
1289
|
|
1008
1290
|
to_events.push delay if delay
|
1009
|
-
generator.
|
1291
|
+
generator.signals(*to_events)
|
1010
1292
|
self
|
1011
1293
|
end
|
1012
1294
|
|
1295
|
+
def forward(name, to, *to_task_events)
|
1296
|
+
Roby.warn_deprecated "Task#forward has been renamed into Task#forward_to"
|
1297
|
+
if to_task_events.empty?
|
1298
|
+
Roby.warn_deprecated "the Task#forward(event_name, target_task) form is deprecated. Use Task#forward_to and specify the target event name"
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
forward_to(name, to, *to_task_events)
|
1302
|
+
end
|
1303
|
+
|
1013
1304
|
# call-seq:
|
1014
|
-
#
|
1015
|
-
#
|
1305
|
+
# forward_to source_event, dest_task, ev1, ev2, ev3, ...
|
1306
|
+
# forward_to source_event, dest_task, ev1, ev2, ev3, delay_options
|
1016
1307
|
#
|
1017
|
-
# Fowards +
|
1018
|
-
#
|
1019
|
-
#
|
1308
|
+
# Fowards the +source_event+, which is the name of an event of +self+,
|
1309
|
+
# to the listed events in +dest_task+. The target events will be emitted
|
1310
|
+
# as soon as the source event is emitted,
|
1020
1311
|
#
|
1021
1312
|
# To call an event whenever other events are emitted, use the Signal
|
1022
|
-
# relation. See Task#
|
1023
|
-
# <tt>forward(:start, task)</tt> is a shortcut to <tt>forward(:start,
|
1024
|
-
# task, :start)</tt>.
|
1313
|
+
# relation. See Task#signals, Task.signal and EventGenerator#signals.
|
1025
1314
|
#
|
1026
|
-
#
|
1027
|
-
#
|
1028
|
-
#
|
1029
|
-
|
1315
|
+
# Optionally, a delay can be added to the signal. +delay_options+ can be
|
1316
|
+
# either:
|
1317
|
+
# :delay => relative_delay_in_seconds
|
1318
|
+
# :at => absolute_time
|
1319
|
+
def forward_to(name, to, *to_task_events)
|
1030
1320
|
generator = event(name)
|
1031
1321
|
if Hash === to_task_events.last
|
1032
1322
|
delay = to_task_events.pop
|
@@ -1034,10 +1324,11 @@ module Roby
|
|
1034
1324
|
|
1035
1325
|
to_events = if to.respond_to?(:event)
|
1036
1326
|
if to_task_events.empty?
|
1327
|
+
Roby.warn_deprecated "forward_to(event_name, target_task) is deprecated. You must now always specify the target event name"
|
1037
1328
|
[to.event(generator.symbol)]
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1329
|
+
else
|
1330
|
+
to_task_events.map { |ev| to.event(ev) }
|
1331
|
+
end
|
1041
1332
|
elsif to.kind_of?(EventGenerator)
|
1042
1333
|
[to]
|
1043
1334
|
else
|
@@ -1045,31 +1336,44 @@ module Roby
|
|
1045
1336
|
end
|
1046
1337
|
|
1047
1338
|
to_events.each do |ev|
|
1048
|
-
generator.
|
1339
|
+
generator.forward_to ev, delay
|
1049
1340
|
end
|
1050
1341
|
end
|
1051
1342
|
|
1343
|
+
# :stopdoc:
|
1052
1344
|
attr_accessor :calling_event
|
1345
|
+
|
1053
1346
|
def method_missing(name, *args, &block) # :nodoc:
|
1054
1347
|
if calling_event && calling_event.respond_to?(name)
|
1055
1348
|
calling_event.send(name, *args, &block)
|
1056
1349
|
else
|
1057
1350
|
super
|
1058
1351
|
end
|
1059
|
-
rescue
|
1060
|
-
raise
|
1352
|
+
rescue NameError => e
|
1353
|
+
raise e, e.message, caller(1)
|
1354
|
+
rescue NoMethodError => e
|
1355
|
+
raise e, e.message, caller(1)
|
1061
1356
|
end
|
1062
1357
|
|
1358
|
+
# Declares that this task model provides the given interface. +model+
|
1359
|
+
# must be an instance of TaskModelTag
|
1360
|
+
def self.provides(model)
|
1361
|
+
include model
|
1362
|
+
end
|
1363
|
+
|
1364
|
+
|
1063
1365
|
@@event_command_id = 0
|
1064
1366
|
def self.allocate_event_command_id # :nodoc:
|
1065
1367
|
@@event_command_id += 1
|
1066
1368
|
end
|
1369
|
+
# :startdoc:
|
1370
|
+
|
1067
1371
|
# call-seq:
|
1068
|
-
# self.event(name, options = nil) { ... }
|
1372
|
+
# self.event(name, options = nil) { ... } => event class or nil
|
1069
1373
|
#
|
1070
1374
|
# Define a new event in this task.
|
1071
1375
|
#
|
1072
|
-
#
|
1376
|
+
# <b>Available options</b>
|
1073
1377
|
#
|
1074
1378
|
# <tt>command</tt>::
|
1075
1379
|
# either true, false or an event command for the new event. In that
|
@@ -1086,7 +1390,7 @@ module Roby
|
|
1086
1390
|
# base class for the event model (see "Event models" below). The default is the
|
1087
1391
|
# TaskEvent class
|
1088
1392
|
#
|
1089
|
-
#
|
1393
|
+
# <b>Event models</b>
|
1090
1394
|
#
|
1091
1395
|
# When a task event (for instance +start+) is emitted, a Roby::Event
|
1092
1396
|
# object is created to describe the information related to this
|
@@ -1130,10 +1434,10 @@ module Roby
|
|
1130
1434
|
@symbol = ev
|
1131
1435
|
@command_handler = command_handler
|
1132
1436
|
|
1133
|
-
define_method(:name) { "#{task.name}::#{ev_s.
|
1437
|
+
define_method(:name) { "#{task.name}::#{ev_s.camelcase(true)}" }
|
1134
1438
|
singleton_class.class_eval do
|
1135
1439
|
attr_reader :command_handler
|
1136
|
-
define_method(:name) { "#{task_klass.name}::#{ev_s.
|
1440
|
+
define_method(:name) { "#{task_klass.name}::#{ev_s.camelcase(true)}" }
|
1137
1441
|
def to_s; name end
|
1138
1442
|
end
|
1139
1443
|
end
|
@@ -1148,7 +1452,7 @@ module Roby
|
|
1148
1452
|
if setup_terminal_handler
|
1149
1453
|
forward(new_event => :stop)
|
1150
1454
|
end
|
1151
|
-
const_set(ev_s.
|
1455
|
+
const_set(ev_s.camelcase(true), new_event)
|
1152
1456
|
|
1153
1457
|
if options[:command]
|
1154
1458
|
# check that the supplied command handler can take two arguments
|
@@ -1172,6 +1476,17 @@ module Roby
|
|
1172
1476
|
end
|
1173
1477
|
end
|
1174
1478
|
|
1479
|
+
if !method_defined?("#{ev_s}_event")
|
1480
|
+
define_method("#{ev_s}_event") do
|
1481
|
+
event(ev)
|
1482
|
+
end
|
1483
|
+
end
|
1484
|
+
if !method_defined?("#{ev_s}?")
|
1485
|
+
define_method("#{ev_s}?") do
|
1486
|
+
event(ev).happened?
|
1487
|
+
end
|
1488
|
+
end
|
1489
|
+
|
1175
1490
|
new_event
|
1176
1491
|
end
|
1177
1492
|
|
@@ -1201,15 +1516,23 @@ module Roby
|
|
1201
1516
|
|
1202
1517
|
# Events defined by the task model
|
1203
1518
|
inherited_enumerable(:event, :events, :map => true) { Hash.new }
|
1204
|
-
|
1519
|
+
|
1520
|
+
def self.enum_events # :nodoc
|
1205
1521
|
@__enum_events__ ||= enum_for(:each_event)
|
1206
1522
|
end
|
1207
1523
|
|
1524
|
+
# call-seq:
|
1525
|
+
# task.each_event { |event_object| ... } => task
|
1526
|
+
#
|
1208
1527
|
# Iterates on all the events defined for this task
|
1209
|
-
|
1528
|
+
#--
|
1529
|
+
# The +only_wrapped+ flag is here for consistency with transaction
|
1530
|
+
# proxies, and should probably not be used in user code.
|
1531
|
+
def each_event(only_wrapped = true) # :yield:bound_event
|
1210
1532
|
for _, ev in bound_events
|
1211
1533
|
yield(ev)
|
1212
1534
|
end
|
1535
|
+
self
|
1213
1536
|
end
|
1214
1537
|
alias :each_plan_child :each_event
|
1215
1538
|
|
@@ -1271,8 +1594,34 @@ module Roby
|
|
1271
1594
|
end
|
1272
1595
|
|
1273
1596
|
# call-seq:
|
1274
|
-
#
|
1275
|
-
#
|
1597
|
+
# signal(name1 => name2, name3 => [name4, name5])
|
1598
|
+
#
|
1599
|
+
# Establish model-level signals between events of that task. These
|
1600
|
+
# signals will be established on all the instances of this task model
|
1601
|
+
# (and its subclasses).
|
1602
|
+
def self.signal(mappings)
|
1603
|
+
mappings.each do |from, to|
|
1604
|
+
from = event_model(from)
|
1605
|
+
targets = Array[*to].map { |ev| event_model(ev) }
|
1606
|
+
|
1607
|
+
if from.terminal?
|
1608
|
+
non_terminal = targets.find_all { |ev| !ev.terminal? }
|
1609
|
+
if !non_terminal.empty?
|
1610
|
+
raise ArgumentError, "trying to establish a forwarding relation from the terminal event #{from} to the non-terminal events #{non_terminal}"
|
1611
|
+
end
|
1612
|
+
end
|
1613
|
+
non_controlable = targets.find_all { |ev| !ev.controlable? }
|
1614
|
+
if !non_controlable.empty?
|
1615
|
+
raise ArgumentError, "trying to signal #{non_controlable.join(" ")} which is/are not controlable"
|
1616
|
+
end
|
1617
|
+
|
1618
|
+
signal_sets[from.symbol].merge targets.map { |ev| ev.symbol }.to_value_set
|
1619
|
+
end
|
1620
|
+
update_terminal_flag
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
# call-seq:
|
1624
|
+
# on(event_name) { |event| ... }
|
1276
1625
|
#
|
1277
1626
|
# Adds an event handler for the given event model. When the event is fired,
|
1278
1627
|
# all events given in argument will be called. If they are controlable,
|
@@ -1282,21 +1631,14 @@ module Roby
|
|
1282
1631
|
check_arity(user_handler, 1)
|
1283
1632
|
end
|
1284
1633
|
|
1634
|
+
if mappings.kind_of?(Hash)
|
1635
|
+
Roby.warn_deprecated "the on(event => event) form of Task.on is deprecated. Use #signal to establish signals"
|
1636
|
+
signal(mappings)
|
1637
|
+
end
|
1638
|
+
|
1285
1639
|
mappings = [*mappings].zip([]) unless Hash === mappings
|
1286
|
-
mappings.each do |from,
|
1640
|
+
mappings.each do |from, _|
|
1287
1641
|
from = event_model(from).symbol
|
1288
|
-
to = if to
|
1289
|
-
Array[*to].map do |ev|
|
1290
|
-
model = event_model(ev)
|
1291
|
-
raise ArgumentError, "trying to signal #{ev} which is not controlable" unless model.controlable?
|
1292
|
-
model.symbol
|
1293
|
-
end
|
1294
|
-
else; []
|
1295
|
-
end
|
1296
|
-
|
1297
|
-
signal_sets[from].merge to.to_value_set
|
1298
|
-
update_terminal_flag
|
1299
|
-
|
1300
1642
|
if user_handler
|
1301
1643
|
method_name = "event_handler_#{from}_#{Object.address_from_id(user_handler.object_id).to_s(16)}"
|
1302
1644
|
define_method(method_name, &user_handler)
|
@@ -1329,8 +1671,17 @@ module Roby
|
|
1329
1671
|
# See also Task#forward and EventGenerator#forward.
|
1330
1672
|
def self.forward(mappings)
|
1331
1673
|
mappings.each do |from, to|
|
1332
|
-
from
|
1333
|
-
|
1674
|
+
from = event_model(from).symbol
|
1675
|
+
targets = Array[*to].map { |ev| event_model(ev).symbol }
|
1676
|
+
|
1677
|
+
if event_model(from).terminal?
|
1678
|
+
non_terminal = targets.find_all { |name| !event_model(name).terminal? }
|
1679
|
+
if !non_terminal.empty?
|
1680
|
+
raise ArgumentError, "trying to establish a forwarding relation from the terminal event #{from} to the non-terminal event(s) #{targets}"
|
1681
|
+
end
|
1682
|
+
end
|
1683
|
+
|
1684
|
+
forwarding_sets[from].merge targets.to_value_set
|
1334
1685
|
end
|
1335
1686
|
update_terminal_flag
|
1336
1687
|
end
|
@@ -1341,7 +1692,7 @@ module Roby
|
|
1341
1692
|
end
|
1342
1693
|
|
1343
1694
|
def to_s # :nodoc:
|
1344
|
-
s = name.dup
|
1695
|
+
s = name.dup + arguments.to_s
|
1345
1696
|
id = owners.map do |owner|
|
1346
1697
|
next if owner == Roby::Distributed
|
1347
1698
|
sibling = remote_siblings[owner]
|
@@ -1353,17 +1704,22 @@ module Roby
|
|
1353
1704
|
s
|
1354
1705
|
end
|
1355
1706
|
|
1356
|
-
def pretty_print(pp) # :nodoc:
|
1357
|
-
pp.text "#{
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1707
|
+
def pretty_print(pp, with_owners = true) # :nodoc:
|
1708
|
+
pp.text "#{model.name}:0x#{self.address.to_s(16)}"
|
1709
|
+
if with_owners
|
1710
|
+
pp.breakable
|
1711
|
+
pp.nest(2) do
|
1712
|
+
pp.text " owners: "
|
1713
|
+
pp.seplist(owners) { |r| pp.text r.to_s }
|
1714
|
+
pp.breakable
|
1362
1715
|
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1716
|
+
pp.text "arguments: "
|
1717
|
+
arguments.pretty_print(pp)
|
1718
|
+
end
|
1719
|
+
else
|
1720
|
+
pp.text " "
|
1721
|
+
arguments.pretty_print(pp)
|
1722
|
+
end
|
1367
1723
|
end
|
1368
1724
|
|
1369
1725
|
# True if this task is a null task. See NullTask.
|
@@ -1417,7 +1773,7 @@ module Roby
|
|
1417
1773
|
def fullfills?(models, args = {})
|
1418
1774
|
if models.kind_of?(Task)
|
1419
1775
|
klass, args =
|
1420
|
-
models.
|
1776
|
+
models.model,
|
1421
1777
|
models.meaningful_arguments
|
1422
1778
|
models = [klass]
|
1423
1779
|
else
|
@@ -1429,7 +1785,7 @@ module Roby
|
|
1429
1785
|
|
1430
1786
|
# Check the arguments that are required by the model
|
1431
1787
|
for tag in models
|
1432
|
-
|
1788
|
+
if !self_model.has_ancestor?(tag)
|
1433
1789
|
return false
|
1434
1790
|
end
|
1435
1791
|
|
@@ -1449,6 +1805,25 @@ module Roby
|
|
1449
1805
|
true
|
1450
1806
|
end
|
1451
1807
|
|
1808
|
+
# True if +self+ can be used to replace +target+
|
1809
|
+
def can_replace?(target)
|
1810
|
+
fullfills?(*target.fullfilled_model)
|
1811
|
+
end
|
1812
|
+
|
1813
|
+
def can_merge?(target)
|
1814
|
+
target_model = target.fullfilled_model
|
1815
|
+
if !fullfills?(target_model.first)
|
1816
|
+
return false
|
1817
|
+
end
|
1818
|
+
|
1819
|
+
target_model.last.each do |key, val|
|
1820
|
+
if arguments.has_key?(key) && arguments[key] != val
|
1821
|
+
return false
|
1822
|
+
end
|
1823
|
+
end
|
1824
|
+
true
|
1825
|
+
end
|
1826
|
+
|
1452
1827
|
include ExceptionHandlingObject
|
1453
1828
|
inherited_enumerable('exception_handler', 'exception_handlers') { Array.new }
|
1454
1829
|
|
@@ -1457,9 +1832,10 @@ module Roby
|
|
1457
1832
|
|
1458
1833
|
@@exception_handler_id = 0
|
1459
1834
|
|
1460
|
-
|
1461
|
-
|
1462
|
-
#
|
1835
|
+
##
|
1836
|
+
# :call-seq:
|
1837
|
+
# on_exception(exception_class, ...) { |task, exception_object| ... }
|
1838
|
+
#
|
1463
1839
|
# Defines an exception handler. matcher === exception_object is used to
|
1464
1840
|
# determine if the handler should be called when +exception_object+ has
|
1465
1841
|
# been fired. The first matching handler is called. Call #pass_exception to pass
|
@@ -1576,8 +1952,8 @@ module Roby
|
|
1576
1952
|
singleton_class.class_eval do
|
1577
1953
|
setup_poll_method(block)
|
1578
1954
|
end
|
1579
|
-
|
1580
|
-
|
1955
|
+
on(:start) { |ev| @poll_handler_id = plan.engine.add_propagation_handler(method(:poll)) }
|
1956
|
+
on(:stop) { |ev| plan.engine.remove_propagation_handler(@poll_handler_id) }
|
1581
1957
|
end
|
1582
1958
|
end
|
1583
1959
|
|
@@ -1617,7 +1993,7 @@ module Roby
|
|
1617
1993
|
start_event.call
|
1618
1994
|
end
|
1619
1995
|
on :start do
|
1620
|
-
success_event.
|
1996
|
+
success_event.forward_to_once event(:success)
|
1621
1997
|
success_event.if_unreachable(true) do
|
1622
1998
|
emit :failed if executable?
|
1623
1999
|
end
|
@@ -1648,5 +2024,3 @@ module Roby
|
|
1648
2024
|
|
1649
2025
|
end
|
1650
2026
|
|
1651
|
-
require 'roby/task-operations'
|
1652
|
-
|