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,70 @@
|
|
|
1
|
+
|
|
2
|
+
module Roby
|
|
3
|
+
module TaskStructure
|
|
4
|
+
Roby::Task.inherited_enumerable(:conflicting_model, :conflicting_models) { ValueSet.new }
|
|
5
|
+
module ModelConflicts
|
|
6
|
+
def conflicts_with(model)
|
|
7
|
+
conflicting_models << model
|
|
8
|
+
model.conflicting_models << self
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def conflicts_with?(model)
|
|
12
|
+
each_conflicting_model do |m|
|
|
13
|
+
return true if m == model
|
|
14
|
+
end
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
Roby::Task.extend ModelConflicts
|
|
19
|
+
|
|
20
|
+
relation :Conflicts, :noinfo => true do
|
|
21
|
+
def conflicts_with(task)
|
|
22
|
+
task.event(:stop).add_precedence event(:start)
|
|
23
|
+
add_conflicts(task)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.included(klass) # :nodoc:
|
|
27
|
+
klass.extend ModelConflicts
|
|
28
|
+
super
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module ConflictEventHandling
|
|
34
|
+
def calling(context)
|
|
35
|
+
super if defined? super
|
|
36
|
+
return unless symbol == :start
|
|
37
|
+
|
|
38
|
+
# Check for conflicting tasks
|
|
39
|
+
result = nil
|
|
40
|
+
task.each_conflicts do |conflicting_task|
|
|
41
|
+
result ||= ValueSet.new
|
|
42
|
+
result << conflicting_task
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if result
|
|
46
|
+
Roby.decision_control.conflict(task, result)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Add the needed conflict relations
|
|
50
|
+
models = task.class.conflicting_models
|
|
51
|
+
for model in models
|
|
52
|
+
if candidates = plan.task_index.by_model[model]
|
|
53
|
+
for t in candidates
|
|
54
|
+
t.conflicts_with task if t.pending?
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def fired(event)
|
|
61
|
+
super if defined? super
|
|
62
|
+
|
|
63
|
+
if symbol == :stop
|
|
64
|
+
TaskStructure::Conflicts.remove(task)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
Roby::TaskEventGenerator.include ConflictEventHandling
|
|
69
|
+
end
|
|
70
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'roby/event'
|
|
2
|
+
module Roby::EventStructure
|
|
3
|
+
relation :EnsuredEvent, :noinfo => true do
|
|
4
|
+
def calling(context)
|
|
5
|
+
super if defined? super
|
|
6
|
+
each_ensured_event do |ev|
|
|
7
|
+
if !ev.happened?
|
|
8
|
+
postpone(ev, "waiting for ensured event #{ev}") do
|
|
9
|
+
ev.call(context) if ev.controlable?
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ensure(event)
|
|
16
|
+
add_ensured_event event
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
module Roby::TaskStructure
|
|
3
|
+
class Roby::TaskEventGenerator
|
|
4
|
+
# Mark this event as being handled by the task +task+
|
|
5
|
+
def handle_with(repairing_task)
|
|
6
|
+
if !task.child_object?(repairing_task, ErrorHandling)
|
|
7
|
+
task.add_error_handler repairing_task, ValueSet.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
task[repairing_task, ErrorHandling] << symbol
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
relation :ErrorHandling, :child_name => :error_handler do
|
|
15
|
+
def failed_task
|
|
16
|
+
each_parent_object(ErrorHandling) do |task|
|
|
17
|
+
return task
|
|
18
|
+
end
|
|
19
|
+
nil
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require 'roby/event'
|
|
2
|
+
|
|
3
|
+
module Roby::EventStructure
|
|
4
|
+
relation :Signal, :noinfo => true
|
|
5
|
+
relation :Forwarding, :noinfo => true
|
|
6
|
+
relation :CausalLink, :subsets => [Signal, Forwarding], :noinfo => true
|
|
7
|
+
relation :Precedence, :subsets => [CausalLink], :noinfo => true
|
|
8
|
+
end
|
|
9
|
+
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
require 'roby/task'
|
|
2
|
+
|
|
3
|
+
module Roby::TaskStructure
|
|
4
|
+
# This module defines model-level definition of execution agent, for
|
|
5
|
+
# instance to Roby::Task
|
|
6
|
+
module ModelLevelExecutionAgent
|
|
7
|
+
# The model of execution agent for this class
|
|
8
|
+
def execution_agent
|
|
9
|
+
for klass in ancestors
|
|
10
|
+
if klass.instance_variable_defined?(:@execution_agent)
|
|
11
|
+
return klass.instance_variable_get(:@execution_agent)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Defines a model of execution agent. Doing
|
|
18
|
+
#
|
|
19
|
+
# TaskModel.executed_by ExecutionAgentModel
|
|
20
|
+
#
|
|
21
|
+
# is equivalent to
|
|
22
|
+
#
|
|
23
|
+
# task = TaskModel.new
|
|
24
|
+
# exec = <find a suitable ExecutionAgentModel instance in the plan or
|
|
25
|
+
# create a new one>
|
|
26
|
+
# task.executed_by exec
|
|
27
|
+
#
|
|
28
|
+
# for all instances of TaskModel. The actual job is done in the
|
|
29
|
+
# ExecutionAgentSpawn module
|
|
30
|
+
def executed_by(agent)
|
|
31
|
+
@execution_agent = agent
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The execution_agent defines an agent (process or otherwise) a given
|
|
36
|
+
# task is executed by. It allows to define a class of these execution agent,
|
|
37
|
+
# so that the specific agents are managed externally (load-balancing, ...)
|
|
38
|
+
relation :ExecutionAgent, :parent_name => :executed_task, :child_name => :execution_agent,
|
|
39
|
+
:noinfo => true, :distribute => false, :single_child => true do
|
|
40
|
+
|
|
41
|
+
# When ExecutionAgent support is included in a model (for instance Roby::Task), add
|
|
42
|
+
# the model-level classes
|
|
43
|
+
def self.included(klass) # :nodoc:
|
|
44
|
+
klass.extend Roby::TaskStructure::ModelLevelExecutionAgent
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Defines a new execution agent for this task.
|
|
49
|
+
def executed_by(agent)
|
|
50
|
+
return if execution_agent == agent
|
|
51
|
+
if !agent.event(:start).controlable? && !agent.running?
|
|
52
|
+
raise ArgumentError, "the start event of #{self}'s execution agent #{agent} is not controlable"
|
|
53
|
+
end
|
|
54
|
+
# Check that agent defines the :ready event
|
|
55
|
+
if !agent.has_event?(:ready)
|
|
56
|
+
raise ArgumentError, "execution agent tasks should define the :ready event"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
old_agent = execution_agent
|
|
60
|
+
if old_agent && old_agent != agent
|
|
61
|
+
Roby.debug "an agent is already defined for this task"
|
|
62
|
+
remove_execution_agent old_agent
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
unless old_agent
|
|
66
|
+
# If the task did have an agent already, these event handlers
|
|
67
|
+
# are already set up
|
|
68
|
+
if running?
|
|
69
|
+
Roby::Distributed.update(self) do
|
|
70
|
+
agent.forward(:stop, self, :aborted)
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
on(:start) do
|
|
74
|
+
# The event handler will be called even if the
|
|
75
|
+
# execution agent has been removed. Check that there is
|
|
76
|
+
# actually an execution agent
|
|
77
|
+
if execution_agent
|
|
78
|
+
Roby::Distributed.update(self) do
|
|
79
|
+
execution_agent.forward(:stop, self, :aborted)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
on(:stop) do
|
|
86
|
+
if execution_agent
|
|
87
|
+
Roby::Distributed.update(self) do
|
|
88
|
+
execution_agent.event(:stop).remove_forwarding event(:aborted)
|
|
89
|
+
remove_execution_agent execution_agent
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
add_execution_agent(agent)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class ExecutionAgentSpawningFailed < Roby::LocalizedError
|
|
101
|
+
attr_reader :agent_model, :error
|
|
102
|
+
def initialize(task, agent_model, error)
|
|
103
|
+
super(task)
|
|
104
|
+
@agent_model, @error = agent_model, error
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Add a suitable execution agent to +task+ if its model has a execution
|
|
109
|
+
# agent model (see ModelLevelExecutionAgent), either by reusing one
|
|
110
|
+
# that is already in the plan, or by creating a new one.
|
|
111
|
+
def ExecutionAgent.spawn(task)
|
|
112
|
+
agent_model = task.model.execution_agent
|
|
113
|
+
candidates = task.plan.find_tasks.
|
|
114
|
+
with_model(agent_model).
|
|
115
|
+
self_owned.
|
|
116
|
+
not_finished
|
|
117
|
+
|
|
118
|
+
agent = nil
|
|
119
|
+
|
|
120
|
+
if candidates.empty?
|
|
121
|
+
begin
|
|
122
|
+
agent = agent_model.new
|
|
123
|
+
agent.on(:stop) do
|
|
124
|
+
agent.each_executed_task do |task|
|
|
125
|
+
if task.running?
|
|
126
|
+
task.emit(:aborted, "execution agent #{self} failed")
|
|
127
|
+
elsif task.pending?
|
|
128
|
+
task.remove_execution_agent agent
|
|
129
|
+
spawn(task)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
rescue Exception => e
|
|
134
|
+
Roby::Propagation.add_error(ExecutionAgentSpawningFailed.new(task, agent_model, e))
|
|
135
|
+
end
|
|
136
|
+
else
|
|
137
|
+
running, pending = candidates.partition { |t| t.running? }
|
|
138
|
+
agent = if running.empty? then pending.first
|
|
139
|
+
else running.first
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
task.executed_by agent
|
|
143
|
+
agent
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# This module is hooked in Roby::TaskEventGenerator to check that a task
|
|
147
|
+
# which is being started has a suitable execution agent, and to start it if
|
|
148
|
+
# it's not the case
|
|
149
|
+
module ExecutionAgentStart
|
|
150
|
+
def calling(context)
|
|
151
|
+
super if defined? super
|
|
152
|
+
return unless symbol == :start
|
|
153
|
+
return unless agent = task.execution_agent
|
|
154
|
+
|
|
155
|
+
if agent.finished? || agent.finishing?
|
|
156
|
+
raise CommandFailed.new(nil, self), "task #{task} has an execution agent but it is dead"
|
|
157
|
+
elsif !agent.event(:ready).happened? && !agent.depends_on?(task)
|
|
158
|
+
postpone(agent.event(:ready), "spawning execution agent #{agent} for #{self}") do
|
|
159
|
+
if agent.pending?
|
|
160
|
+
agent.event(:ready).if_unreachable(true) do |reason|
|
|
161
|
+
self.emit_failed "execution agent #{agent} failed to initialize: #{reason}"
|
|
162
|
+
end
|
|
163
|
+
agent.start!
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
Roby::TaskEventGenerator.include ExecutionAgentStart
|
|
170
|
+
|
|
171
|
+
# This module is included in Roby::Plan to automatically add execution agents
|
|
172
|
+
# to tasks that require it and are discovered in the executable plan.
|
|
173
|
+
module ExecutionAgentSpawn
|
|
174
|
+
# Hook into plan discovery to add execution agents to new tasks.
|
|
175
|
+
# See ExecutionAgentSpawn.spawn
|
|
176
|
+
def discovered_tasks(tasks)
|
|
177
|
+
# For now, settle on adding the execution agents only in the
|
|
178
|
+
# main plan. Otherwise, it is possible that two transactions
|
|
179
|
+
# will try to add two different agents
|
|
180
|
+
#
|
|
181
|
+
# Note that it would be solved by plan merging ...
|
|
182
|
+
return unless executable?
|
|
183
|
+
|
|
184
|
+
for task in tasks
|
|
185
|
+
if !task.execution_agent && task.model.execution_agent && task.self_owned?
|
|
186
|
+
ExecutionAgent.spawn(task)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
Roby::Plan.include ExecutionAgentSpawn
|
|
192
|
+
end
|
|
193
|
+
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
require 'roby/task'
|
|
2
|
+
require 'roby/control'
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Roby::TaskStructure
|
|
6
|
+
# Document-module: Hierarchy
|
|
7
|
+
relation :Hierarchy, :child_name => :child, :parent_name => :parent_task do
|
|
8
|
+
# True if +obj+ is a parent of this object in the hierarchy relation
|
|
9
|
+
# (+obj+ is realized by +self+)
|
|
10
|
+
def realizes?(obj); parent_object?(obj, Hierarchy) end
|
|
11
|
+
# True if +obj+ is a child of this object in the hierarchy relation
|
|
12
|
+
def realized_by?(obj); child_object?(obj, Hierarchy) end
|
|
13
|
+
# True if +obj+ can be reached through the Hierarchy relation by
|
|
14
|
+
# starting from this object
|
|
15
|
+
def depends_on?(obj)
|
|
16
|
+
generated_subgraph(Hierarchy).include?(obj)
|
|
17
|
+
end
|
|
18
|
+
# The set of parent objects in the Hierarchy relation
|
|
19
|
+
def parents; parent_objects(Hierarchy) end
|
|
20
|
+
# The set of child objects in the Hierarchy relation
|
|
21
|
+
def children; child_objects(Hierarchy) end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Adds +task+ as a child of +self+ in the Hierarchy relation. The
|
|
25
|
+
# following options are allowed:
|
|
26
|
+
#
|
|
27
|
+
# success:: the list of success events. The default is [:success]
|
|
28
|
+
# failure:: the list of failing events. The default is [:failed]
|
|
29
|
+
# model:: a <tt>[task_model, arguments]</tt> pair which defines the task model the parent is expecting.
|
|
30
|
+
# The default value is to get these parameters from +task+
|
|
31
|
+
#
|
|
32
|
+
# The +success+ set describes the events of the child task that are
|
|
33
|
+
# _required_ by the parent task. More specifically, the child task
|
|
34
|
+
# remains useful for the parent task as long as none of these events are
|
|
35
|
+
# emitted. By default, it is the +success+ event. Of course, an error
|
|
36
|
+
# condition is encountered when all events of +success+ become
|
|
37
|
+
# unreachable. In addition, the relation is removed if the
|
|
38
|
+
# +remove_when_done+ flag is set to true (false by default).
|
|
39
|
+
#
|
|
40
|
+
# The +failure+ set describes the events of the child task which are an
|
|
41
|
+
# error condition from the parent task point of view.
|
|
42
|
+
#
|
|
43
|
+
# In both error cases, a +ChildFailedError+ exception is raised.
|
|
44
|
+
def realized_by(task, options = {})
|
|
45
|
+
options = validate_options options,
|
|
46
|
+
:model => [task.model, task.meaningful_arguments],
|
|
47
|
+
:success => [:success],
|
|
48
|
+
:failure => [:failed],
|
|
49
|
+
:remove_when_done => false
|
|
50
|
+
|
|
51
|
+
options[:success] = Array[*options[:success]]
|
|
52
|
+
options[:failure] = Array[*options[:failure]]
|
|
53
|
+
|
|
54
|
+
# Validate failure and success event names
|
|
55
|
+
options[:success].each { |ev| task.event(ev) }
|
|
56
|
+
options[:failure].each { |ev| task.event(ev) }
|
|
57
|
+
|
|
58
|
+
options[:model] = [options[:model], {}] unless Array === options[:model]
|
|
59
|
+
required_model, required_args = *options[:model]
|
|
60
|
+
if !task.fullfills?(required_model, required_args)
|
|
61
|
+
raise ArgumentError, "task #{task} does not fullfills the provided model #{options[:model]}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
add_child(task, options)
|
|
65
|
+
self
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Set up the event gathering needed by Hierarchy.check_structure
|
|
69
|
+
def added_child_object(child, relations, info) # :nodoc:
|
|
70
|
+
super if defined? super
|
|
71
|
+
if relations.include?(Hierarchy) && !respond_to?(:__getobj__) && !child.respond_to?(:__getobj__)
|
|
72
|
+
events = info[:success].map { |ev| child.event(ev) }
|
|
73
|
+
events.concat info[:failure].map { |ev| child.event(ev) }
|
|
74
|
+
Roby::EventGenerator.gather_events(Hierarchy.interesting_events, events)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Return the set of this task children for which the :start event has
|
|
79
|
+
# no parent in CausalLinks
|
|
80
|
+
def first_children
|
|
81
|
+
result = ValueSet.new
|
|
82
|
+
|
|
83
|
+
generated_subgraph(Hierarchy).each do |task|
|
|
84
|
+
next if task == self
|
|
85
|
+
if task.event(:start).root?(Roby::EventStructure::CausalLink)
|
|
86
|
+
result << task
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
result
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# The set of events that are needed by the parent tasks
|
|
93
|
+
def fullfilled_events
|
|
94
|
+
needed = ValueSet.new
|
|
95
|
+
each_parent_task do |parent|
|
|
96
|
+
needed.merge(parent[self, Hierarchy][:success])
|
|
97
|
+
end
|
|
98
|
+
needed
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Return [tags, arguments] where +tags+ is a list of task models which
|
|
102
|
+
# are required by the parent tasks of this task, and arguments the
|
|
103
|
+
# required arguments
|
|
104
|
+
#
|
|
105
|
+
# If there is a task class in the required models, it is always the
|
|
106
|
+
# first element of +tags+
|
|
107
|
+
def fullfilled_model
|
|
108
|
+
model, tags, arguments = Roby::Task, [], {}
|
|
109
|
+
|
|
110
|
+
each_parent_task do |parent|
|
|
111
|
+
m, a = parent[self, Hierarchy][:model]
|
|
112
|
+
if m.instance_of?(Roby::TaskModelTag)
|
|
113
|
+
tags << m
|
|
114
|
+
elsif m.has_ancestor?(model)
|
|
115
|
+
model = m
|
|
116
|
+
elsif !model.has_ancestor?(m)
|
|
117
|
+
raise "inconsistency in fullfilled models: #{model} and #{m} are incompatible"
|
|
118
|
+
end
|
|
119
|
+
a.merge!(arguments) do |old, new|
|
|
120
|
+
if old != new
|
|
121
|
+
raise "inconsistency in fullfilled models: #{old} and #{new}"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
tags.unshift(model)
|
|
127
|
+
[tags, arguments]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Remove all children that have successfully finished
|
|
131
|
+
def remove_finished_children
|
|
132
|
+
# We call #to_a to get a copy of children, since we will remove
|
|
133
|
+
# children in the block. Note that we can't use #delete_if here
|
|
134
|
+
# since #children is a relation enumerator (not the relation list
|
|
135
|
+
# itself)
|
|
136
|
+
children.to_a.each do |child|
|
|
137
|
+
success_events = self[child, Hierarchy][:success]
|
|
138
|
+
if success_events.any? { |ev| child.event(ev).happened? }
|
|
139
|
+
remove_child(child)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Checks the structure of +plan+ w.r.t. the constraints of the hierarchy
|
|
146
|
+
# relations. It returns an array of ChildFailedError for all failed
|
|
147
|
+
# hierarchy relations
|
|
148
|
+
def Hierarchy.check_structure(plan)
|
|
149
|
+
result = []
|
|
150
|
+
|
|
151
|
+
events = Hierarchy.interesting_events
|
|
152
|
+
return result if events.empty? && failing_tasks.empty?
|
|
153
|
+
|
|
154
|
+
# Get the set of tasks for which a possible failure has been
|
|
155
|
+
# registered The tasks that are failing the hierarchy requirements
|
|
156
|
+
# are registered in Hierarchy.failing_tasks. The interesting_events
|
|
157
|
+
# set is cleared at cycle end (see below)
|
|
158
|
+
tasks = events.inject(failing_tasks) { |set, event| set << event.generator.task }
|
|
159
|
+
@failing_tasks = ValueSet.new
|
|
160
|
+
tasks.each do |child|
|
|
161
|
+
# Check if the task has been removed from the plan
|
|
162
|
+
next unless child.plan
|
|
163
|
+
|
|
164
|
+
has_error = false
|
|
165
|
+
child.each_parent_task do |parent|
|
|
166
|
+
next unless parent.self_owned?
|
|
167
|
+
next if parent.finished? || parent.finishing?
|
|
168
|
+
|
|
169
|
+
options = parent[child, Hierarchy]
|
|
170
|
+
success = options[:success]
|
|
171
|
+
failure = options[:failure]
|
|
172
|
+
|
|
173
|
+
if success.any? { |e| child.event(e).happened? }
|
|
174
|
+
if options[:remove_when_done]
|
|
175
|
+
parent.remove_child child
|
|
176
|
+
end
|
|
177
|
+
elsif failing_event = failure.find { |e| child.event(e).happened? }
|
|
178
|
+
result << Roby::ChildFailedError.new(parent, child.event(failing_event).last)
|
|
179
|
+
failing_tasks << child
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
events.clear
|
|
185
|
+
result
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
class << Hierarchy
|
|
189
|
+
# The set of events that have been fired in this cycle and are involved in a Hierarchy relation
|
|
190
|
+
attribute(:interesting_events) { Array.new }
|
|
191
|
+
|
|
192
|
+
# The set of tasks that are currently failing
|
|
193
|
+
attribute(:failing_tasks) { ValueSet.new }
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
module Roby
|
|
198
|
+
# This exception is raised when a {hierarchy relation}[classes/Roby/TaskStructure/Hierarchy.html] fails
|
|
199
|
+
class ChildFailedError < LocalizedError
|
|
200
|
+
# The parent in the relation
|
|
201
|
+
attr_reader :parent
|
|
202
|
+
# The child in the relation
|
|
203
|
+
def child; failed_task end
|
|
204
|
+
# The relation parameters (i.e. the hash given to #realized_by)
|
|
205
|
+
attr_reader :relation
|
|
206
|
+
|
|
207
|
+
# The event which is the cause of this error. This is either the task
|
|
208
|
+
# source of a failure event, or the reason why a positive event has
|
|
209
|
+
# become unreachable (if there is one)
|
|
210
|
+
def initialize(parent, event)
|
|
211
|
+
super(event.task_sources.find { true })
|
|
212
|
+
@parent = parent
|
|
213
|
+
@relation = parent[child, TaskStructure::Hierarchy]
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def pretty_print(pp) # :nodoc:
|
|
217
|
+
super
|
|
218
|
+
pp.breakable
|
|
219
|
+
pp.breakable
|
|
220
|
+
pp.text "The failed relation is"
|
|
221
|
+
pp.breakable
|
|
222
|
+
pp.nest(2) do
|
|
223
|
+
pp.text " "
|
|
224
|
+
parent.pretty_print pp
|
|
225
|
+
pp.breakable
|
|
226
|
+
pp.text "realized_by "
|
|
227
|
+
child.pretty_print pp
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
def backtrace; [] end
|
|
231
|
+
|
|
232
|
+
# True if +obj+ is involved in this exception
|
|
233
|
+
def involved_plan_object?(obj)
|
|
234
|
+
super || obj == parent
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
Control.structure_checks << TaskStructure::Hierarchy.method(:check_structure)
|
|
238
|
+
end
|
|
239
|
+
|