roby 0.7.3 → 0.8.0
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/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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
module Roby
|
|
3
2
|
module TaskStructure
|
|
4
3
|
Roby::Task.inherited_enumerable(:conflicting_model, :conflicting_models) { ValueSet.new }
|
|
@@ -10,7 +9,7 @@ module Roby
|
|
|
10
9
|
|
|
11
10
|
def conflicts_with?(model)
|
|
12
11
|
each_conflicting_model do |m|
|
|
13
|
-
return true if
|
|
12
|
+
return true if model <= m
|
|
14
13
|
end
|
|
15
14
|
false
|
|
16
15
|
end
|
|
@@ -19,7 +18,7 @@ module Roby
|
|
|
19
18
|
|
|
20
19
|
relation :Conflicts, :noinfo => true do
|
|
21
20
|
def conflicts_with(task)
|
|
22
|
-
task.event(:stop).add_precedence event(:start)
|
|
21
|
+
# task.event(:stop).add_precedence event(:start)
|
|
23
22
|
add_conflicts(task)
|
|
24
23
|
end
|
|
25
24
|
|
|
@@ -43,16 +42,14 @@ module Roby
|
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
if result
|
|
46
|
-
|
|
45
|
+
plan.control.conflict(task, result)
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
# Add the needed conflict relations
|
|
50
49
|
models = task.class.conflicting_models
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
t.conflicts_with task if t.pending?
|
|
55
|
-
end
|
|
50
|
+
for model in models
|
|
51
|
+
for t in plan.find_tasks(model)
|
|
52
|
+
t.conflicts_with task if t.pending? && t != task
|
|
56
53
|
end
|
|
57
54
|
end
|
|
58
55
|
end
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
module Roby::TaskStructure
|
|
2
|
+
relation :Dependency, :child_name => :child, :parent_name => :parent_task do
|
|
3
|
+
##
|
|
4
|
+
# :method: add_child(v, info)
|
|
5
|
+
# Adds a new child to +v+. You should use #realized_by instead.
|
|
6
|
+
|
|
7
|
+
def realizes?(obj)
|
|
8
|
+
Roby.warn_deprecated "#realizes? is deprecated. Use #depended_upon_by? instead"
|
|
9
|
+
depended_upon_by?(obj)
|
|
10
|
+
end
|
|
11
|
+
def realized_by?(obj)
|
|
12
|
+
Roby.warn_deprecated "#realized_by? is deprecated. Use #depends_on?(obj, false) instead"
|
|
13
|
+
depends_on?(obj, false)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# True if +obj+ is a parent of this object in the hierarchy relation
|
|
17
|
+
# (+obj+ is realized by +self+)
|
|
18
|
+
def depended_upon_by?(obj); parent_object?(obj, Dependency) end
|
|
19
|
+
|
|
20
|
+
# True if +obj+ is a child of this object in the hierarchy relation.
|
|
21
|
+
# If +recursive+ is true, take into account the whole subgraph.
|
|
22
|
+
# Otherwise, only direct children are checked.
|
|
23
|
+
def depends_on?(obj, recursive = true)
|
|
24
|
+
if recursive
|
|
25
|
+
generated_subgraph(Dependency).include?(obj)
|
|
26
|
+
else
|
|
27
|
+
child_object?(obj, Dependency)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
# The set of parent objects in the Dependency relation
|
|
31
|
+
def parents; parent_objects(Dependency) end
|
|
32
|
+
# The set of child objects in the Dependency relation
|
|
33
|
+
def children; child_objects(Dependency) end
|
|
34
|
+
|
|
35
|
+
def roles_of(child)
|
|
36
|
+
info = self[child, Dependency]
|
|
37
|
+
info[:roles]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def realized_by(task, options = {})
|
|
41
|
+
Roby.warn_deprecated "#realized_by is deprecated. Use #depends_on instead"
|
|
42
|
+
depends_on(task, options)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Adds +task+ as a child of +self+ in the Dependency relation. The
|
|
46
|
+
# following options are allowed:
|
|
47
|
+
#
|
|
48
|
+
# success:: the list of success events. The default is [:success]
|
|
49
|
+
# failure:: the list of failing events. The default is [:failed]
|
|
50
|
+
# model::
|
|
51
|
+
# a <tt>[task_model, arguments]</tt> pair which defines the task
|
|
52
|
+
# model the parent is expecting. The default value is to get these
|
|
53
|
+
# parameters from +task+
|
|
54
|
+
#
|
|
55
|
+
# The +success+ set describes the events of the child task that are
|
|
56
|
+
# _required_ by the parent task. More specifically, the child task
|
|
57
|
+
# remains useful for the parent task as long as none of these events are
|
|
58
|
+
# emitted. By default, it is the +success+ event. Of course, an error
|
|
59
|
+
# condition is encountered when all events of +success+ become
|
|
60
|
+
# unreachable. In addition, the relation is removed if the
|
|
61
|
+
# +remove_when_done+ flag is set to true (false by default).
|
|
62
|
+
#
|
|
63
|
+
# The +failure+ set describes the events of the child task which are an
|
|
64
|
+
# error condition from the parent task point of view.
|
|
65
|
+
#
|
|
66
|
+
# In both error cases, a +ChildFailedError+ exception is raised.
|
|
67
|
+
def depends_on(task, options = {})
|
|
68
|
+
options = validate_options options,
|
|
69
|
+
:model => [task.model, task.meaningful_arguments],
|
|
70
|
+
:success => [:success],
|
|
71
|
+
:failure => [],
|
|
72
|
+
:remove_when_done => true,
|
|
73
|
+
:roles => nil,
|
|
74
|
+
:role => nil
|
|
75
|
+
|
|
76
|
+
roles = options[:roles] || ValueSet.new
|
|
77
|
+
if role = options.delete(:role)
|
|
78
|
+
roles << role.to_str
|
|
79
|
+
end
|
|
80
|
+
roles = roles.map { |r| r.to_str }
|
|
81
|
+
options[:roles] = roles.to_set
|
|
82
|
+
|
|
83
|
+
options[:success] = Array[*options[:success]]
|
|
84
|
+
options[:failure] = Array[*options[:failure]]
|
|
85
|
+
options[:success] -= options[:failure]
|
|
86
|
+
|
|
87
|
+
# Validate failure and success event names
|
|
88
|
+
options[:success].each { |ev| task.event(ev) }
|
|
89
|
+
options[:failure].each { |ev| task.event(ev) }
|
|
90
|
+
|
|
91
|
+
options[:model] = [options[:model], {}] unless Array === options[:model]
|
|
92
|
+
required_model, required_args = *options[:model]
|
|
93
|
+
if !required_args.respond_to?(:to_hash)
|
|
94
|
+
raise ArgumentError, "argument specification must be a hash, got #{required_args} (#{required_args.class})"
|
|
95
|
+
elsif !task.fullfills?(required_model, required_args)
|
|
96
|
+
raise ArgumentError, "task #{task} does not fullfill the provided model #{options[:model]}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Check if there is already a dependency link. If it is the case,
|
|
100
|
+
# merge the options. Otherwise, just add.
|
|
101
|
+
add_child(task, options)
|
|
102
|
+
self
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Set up the event gathering needed by Dependency.check_structure
|
|
106
|
+
def added_child_object(child, relations, info) # :nodoc:
|
|
107
|
+
super if defined? super
|
|
108
|
+
if relations.include?(Dependency) && !respond_to?(:__getobj__) && !child.respond_to?(:__getobj__)
|
|
109
|
+
events = info[:success].map do |ev|
|
|
110
|
+
ev = child.event(ev)
|
|
111
|
+
ev.if_unreachable { Dependency.interesting_events << ev }
|
|
112
|
+
ev
|
|
113
|
+
end
|
|
114
|
+
events.concat info[:failure].map { |ev| child.event(ev) }
|
|
115
|
+
Roby::EventGenerator.gather_events(Dependency.interesting_events, events)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Return the set of this task children for which the :start event has
|
|
120
|
+
# no parent in CausalLinks
|
|
121
|
+
def first_children
|
|
122
|
+
result = ValueSet.new
|
|
123
|
+
|
|
124
|
+
generated_subgraph(Dependency).each do |task|
|
|
125
|
+
next if task == self
|
|
126
|
+
if task.event(:start).root?(Roby::EventStructure::CausalLink)
|
|
127
|
+
result << task
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
result
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The set of events that are needed by the parent tasks
|
|
134
|
+
def fullfilled_events
|
|
135
|
+
needed = ValueSet.new
|
|
136
|
+
merged_relations(:each_parent_task, false) do |myself, parent|
|
|
137
|
+
needed.merge(parent[myself, Dependency][:success])
|
|
138
|
+
end
|
|
139
|
+
needed
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Return [tags, arguments] where +tags+ is a list of task models which
|
|
143
|
+
# are required by the parent tasks of this task, and arguments the
|
|
144
|
+
# required arguments
|
|
145
|
+
#
|
|
146
|
+
# If there is a task class in the required models, it is always the
|
|
147
|
+
# first element of +tags+
|
|
148
|
+
def fullfilled_model
|
|
149
|
+
model, tags, arguments = Roby::Task, [], {}
|
|
150
|
+
|
|
151
|
+
has_parent = false
|
|
152
|
+
merged_relations(:each_parent_task, false) do |myself, parent|
|
|
153
|
+
has_parent = true
|
|
154
|
+
|
|
155
|
+
required_models, required_arguments = parent[myself, Dependency][:model]
|
|
156
|
+
required_models = [required_models] if !required_models.respond_to?(:to_ary)
|
|
157
|
+
|
|
158
|
+
for m in required_models
|
|
159
|
+
if m.kind_of?(Roby::TaskModelTag)
|
|
160
|
+
tags << m
|
|
161
|
+
elsif m.has_ancestor?(model)
|
|
162
|
+
model = m
|
|
163
|
+
elsif !model.has_ancestor?(m)
|
|
164
|
+
raise Roby::ModelViolation, "inconsistency in fullfilled models: #{model} and #{m} are incompatible"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
arguments.merge!(required_arguments) do |name, old, new|
|
|
169
|
+
if old != new
|
|
170
|
+
raise Roby::ModelViolation, "inconsistency in fullfilled models: #{old} and #{new}"
|
|
171
|
+
end
|
|
172
|
+
old
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
if !has_parent
|
|
177
|
+
[[self.model], self.arguments]
|
|
178
|
+
else
|
|
179
|
+
tags.unshift(model)
|
|
180
|
+
[tags, arguments]
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Remove all children that have successfully finished
|
|
185
|
+
def remove_finished_children
|
|
186
|
+
# We call #to_a to get a copy of children, since we will remove
|
|
187
|
+
# children in the block. Note that we can't use #delete_if here
|
|
188
|
+
# since #children is a relation enumerator (not the relation list
|
|
189
|
+
# itself)
|
|
190
|
+
children.to_a.each do |child|
|
|
191
|
+
success_events = self[child, Dependency][:success]
|
|
192
|
+
if success_events.any? { |ev| child.event(ev).happened? }
|
|
193
|
+
remove_child(child)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
Hierarchy = Dependency
|
|
199
|
+
|
|
200
|
+
def Dependency.merge_info(parent, child, opt1, opt2)
|
|
201
|
+
if opt1[:remove_when_done] != opt2[:remove_when_done]
|
|
202
|
+
raise Roby::ModelViolation, "incompatible dependency specification: trying to change the value of +remove_when_done+"
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
result = { :remove_when_done => opt1[:remove_when_done] }
|
|
206
|
+
|
|
207
|
+
# Remove from :success the events listed in :failure. We can't remove
|
|
208
|
+
# the events in opt1[:success] that would lead to having opt2[:success]
|
|
209
|
+
# unreachable though ...
|
|
210
|
+
result[:success] = (opt1[:success] - opt2[:failure]) | (opt2[:success] - opt1[:failure])
|
|
211
|
+
result[:failure] = opt1[:failure] | opt2[:failure]
|
|
212
|
+
if result[:success].empty? && (!opt1[:success].empty? || !opt2[:success].empty?)
|
|
213
|
+
raise Roby::ModelViolation, "incompatibility between the :success and :failure sets of #{opt1} and #{opt2}"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Check model compatibility
|
|
217
|
+
model1, arguments1 = opt1[:model]
|
|
218
|
+
model2, arguments2 = opt2[:model]
|
|
219
|
+
if model1 <= model2
|
|
220
|
+
result[:model] = [model1, {}]
|
|
221
|
+
elsif model2 < model1
|
|
222
|
+
result[:model] = [model2, {}]
|
|
223
|
+
else
|
|
224
|
+
# Find the most generic model that +task+ fullfills and that
|
|
225
|
+
# includes both +model1+ and +model2+
|
|
226
|
+
klass = child.model
|
|
227
|
+
while klass != Roby::Task && (klass <= model1 && klass <= model2)
|
|
228
|
+
candidate = klass
|
|
229
|
+
klass = klass.superclass
|
|
230
|
+
end
|
|
231
|
+
# We should always have a solution, as +task+ fullfills both model1 and model2
|
|
232
|
+
result[:model] = [candidate, []]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Merge arguments
|
|
236
|
+
result[:model][1] = arguments1.merge(arguments2) do |key, old_value, new_value|
|
|
237
|
+
if old_value != new_value
|
|
238
|
+
raise Roby::ModelViolation, "incompatible argument constraint #{old_value} and #{new_value} for #{key}"
|
|
239
|
+
end
|
|
240
|
+
old_value
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Finally, merge the roles (the easy part ;-))
|
|
244
|
+
result[:roles] = opt1[:roles] | opt2[:roles]
|
|
245
|
+
|
|
246
|
+
result
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Checks the structure of +plan+ w.r.t. the constraints of the hierarchy
|
|
250
|
+
# relations. It returns an array of ChildFailedError for all failed
|
|
251
|
+
# hierarchy relations
|
|
252
|
+
def Dependency.check_structure(plan)
|
|
253
|
+
result = []
|
|
254
|
+
|
|
255
|
+
events = Hierarchy.interesting_events
|
|
256
|
+
return result if events.empty? && failing_tasks.empty?
|
|
257
|
+
|
|
258
|
+
# Get the set of tasks for which a possible failure has been
|
|
259
|
+
# registered The tasks that are failing the hierarchy requirements
|
|
260
|
+
# are registered in Hierarchy.failing_tasks. The interesting_events
|
|
261
|
+
# set is cleared at cycle end (see below)
|
|
262
|
+
tasks = events.inject(failing_tasks) do |set, event|
|
|
263
|
+
if event.respond_to?(:generator)
|
|
264
|
+
set << event.generator.task
|
|
265
|
+
else
|
|
266
|
+
set << event.task
|
|
267
|
+
end
|
|
268
|
+
set
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
@failing_tasks = ValueSet.new
|
|
272
|
+
tasks.each do |child|
|
|
273
|
+
# Check if the task has been removed from the plan
|
|
274
|
+
next unless child.plan
|
|
275
|
+
|
|
276
|
+
has_error = false
|
|
277
|
+
child.each_parent_task do |parent|
|
|
278
|
+
next unless parent.self_owned?
|
|
279
|
+
next if parent.finished? || parent.finishing?
|
|
280
|
+
|
|
281
|
+
options = parent[child, Hierarchy]
|
|
282
|
+
success = options[:success]
|
|
283
|
+
failure = options[:failure]
|
|
284
|
+
|
|
285
|
+
if success.any? { |e| child.event(e).happened? }
|
|
286
|
+
if options[:remove_when_done]
|
|
287
|
+
parent.remove_child child
|
|
288
|
+
end
|
|
289
|
+
elsif failing_event = failure.find { |e| child.event(e).happened? }
|
|
290
|
+
result << Roby::ChildFailedError.new(parent, child.event(failing_event).last)
|
|
291
|
+
failing_tasks << child
|
|
292
|
+
elsif success.all? { |e| child.event(e).unreachable? }
|
|
293
|
+
failing_event = success.find { |e| child.event(e).unreachability_reason }
|
|
294
|
+
failing_event = child.event(failing_event).unreachability_reason
|
|
295
|
+
if !failing_event
|
|
296
|
+
failing_event = child.event(success.find { |e| child.event(e) })
|
|
297
|
+
end
|
|
298
|
+
result << Roby::ChildFailedError.new(parent, failing_event)
|
|
299
|
+
failing_tasks << child
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
events.clear
|
|
305
|
+
result
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
class << Dependency
|
|
309
|
+
# The set of events that have been fired in this cycle and are involved in a Hierarchy relation
|
|
310
|
+
attribute(:interesting_events) { Array.new }
|
|
311
|
+
|
|
312
|
+
# The set of tasks that are currently failing
|
|
313
|
+
attribute(:failing_tasks) { ValueSet.new }
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
module Roby
|
|
318
|
+
# This exception is raised when a {hierarchy relation}[classes/Roby/TaskStructure/Hierarchy.html] fails
|
|
319
|
+
class ChildFailedError < LocalizedError
|
|
320
|
+
# The parent in the relation
|
|
321
|
+
attr_reader :parent
|
|
322
|
+
# The child in the relation
|
|
323
|
+
def child; failed_task end
|
|
324
|
+
# The relation parameters (i.e. the hash given to #depends_on)
|
|
325
|
+
attr_reader :relation
|
|
326
|
+
|
|
327
|
+
# The event which is the cause of this error. This is either the task
|
|
328
|
+
# source of a failure event, or the reason why a positive event has
|
|
329
|
+
# become unreachable (if there is one)
|
|
330
|
+
def initialize(parent, event)
|
|
331
|
+
super(event)
|
|
332
|
+
@parent = parent
|
|
333
|
+
@relation = parent[child, TaskStructure::Dependency]
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def pretty_print(pp) # :nodoc:
|
|
337
|
+
super
|
|
338
|
+
pp.breakable
|
|
339
|
+
pp.breakable
|
|
340
|
+
pp.text "The failed relation is"
|
|
341
|
+
pp.breakable
|
|
342
|
+
pp.nest(2) do
|
|
343
|
+
pp.text " "
|
|
344
|
+
parent.pretty_print pp
|
|
345
|
+
pp.breakable
|
|
346
|
+
pp.text "depends_on "
|
|
347
|
+
child.pretty_print pp
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
def backtrace; [] end
|
|
351
|
+
|
|
352
|
+
# True if +obj+ is involved in this exception
|
|
353
|
+
def involved_plan_object?(obj)
|
|
354
|
+
super || obj == parent
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'roby/task'
|
|
2
|
-
|
|
3
1
|
module Roby::TaskStructure
|
|
4
2
|
# This module defines model-level definition of execution agent, for
|
|
5
3
|
# instance to Roby::Task
|
|
@@ -27,8 +25,8 @@ module Roby::TaskStructure
|
|
|
27
25
|
#
|
|
28
26
|
# for all instances of TaskModel. The actual job is done in the
|
|
29
27
|
# ExecutionAgentSpawn module
|
|
30
|
-
def executed_by(
|
|
31
|
-
@execution_agent =
|
|
28
|
+
def executed_by(agent_model, arguments = Hash.new)
|
|
29
|
+
@execution_agent = [agent_model, arguments]
|
|
32
30
|
end
|
|
33
31
|
end
|
|
34
32
|
|
|
@@ -67,7 +65,7 @@ module Roby::TaskStructure
|
|
|
67
65
|
# are already set up
|
|
68
66
|
if running?
|
|
69
67
|
Roby::Distributed.update(self) do
|
|
70
|
-
agent.
|
|
68
|
+
agent.forward_to(:stop, self, :aborted)
|
|
71
69
|
end
|
|
72
70
|
else
|
|
73
71
|
on(:start) do
|
|
@@ -76,7 +74,7 @@ module Roby::TaskStructure
|
|
|
76
74
|
# actually an execution agent
|
|
77
75
|
if execution_agent
|
|
78
76
|
Roby::Distributed.update(self) do
|
|
79
|
-
execution_agent.
|
|
77
|
+
execution_agent.forward_to(:stop, self, :aborted)
|
|
80
78
|
end
|
|
81
79
|
end
|
|
82
80
|
end
|
|
@@ -109,9 +107,10 @@ module Roby::TaskStructure
|
|
|
109
107
|
# agent model (see ModelLevelExecutionAgent), either by reusing one
|
|
110
108
|
# that is already in the plan, or by creating a new one.
|
|
111
109
|
def ExecutionAgent.spawn(task)
|
|
112
|
-
agent_model = task.model.execution_agent
|
|
110
|
+
agent_model, arguments = task.model.execution_agent
|
|
113
111
|
candidates = task.plan.find_tasks.
|
|
114
112
|
with_model(agent_model).
|
|
113
|
+
with_arguments(arguments).
|
|
115
114
|
self_owned.
|
|
116
115
|
not_finished
|
|
117
116
|
|
|
@@ -119,7 +118,7 @@ module Roby::TaskStructure
|
|
|
119
118
|
|
|
120
119
|
if candidates.empty?
|
|
121
120
|
begin
|
|
122
|
-
agent = agent_model.new
|
|
121
|
+
agent = agent_model.new(arguments)
|
|
123
122
|
agent.on(:stop) do
|
|
124
123
|
agent.each_executed_task do |task|
|
|
125
124
|
if task.running?
|
|
@@ -131,7 +130,7 @@ module Roby::TaskStructure
|
|
|
131
130
|
end
|
|
132
131
|
end
|
|
133
132
|
rescue Exception => e
|
|
134
|
-
|
|
133
|
+
task.plan.engine.add_error(ExecutionAgentSpawningFailed.new(task, agent_model, e))
|
|
135
134
|
end
|
|
136
135
|
else
|
|
137
136
|
running, pending = candidates.partition { |t| t.running? }
|
|
@@ -150,7 +149,23 @@ module Roby::TaskStructure
|
|
|
150
149
|
def calling(context)
|
|
151
150
|
super if defined? super
|
|
152
151
|
return unless symbol == :start
|
|
153
|
-
|
|
152
|
+
|
|
153
|
+
agent = task.execution_agent
|
|
154
|
+
if !agent
|
|
155
|
+
if task.model.execution_agent
|
|
156
|
+
raise CommandFailed.new(nil, self), "the model of #{task} requires an execution agent, but the task has none"
|
|
157
|
+
else
|
|
158
|
+
return
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Check that the agent matches the model
|
|
163
|
+
agent_model, arguments = task.model.execution_agent
|
|
164
|
+
if agent_model
|
|
165
|
+
if !agent.fullfills?(agent_model, arguments)
|
|
166
|
+
raise CommandFailed.new(nil, self), "the execution agent #{agent} does not match the required model #{agent_model}, #{arguments}"
|
|
167
|
+
end
|
|
168
|
+
end
|
|
154
169
|
|
|
155
170
|
if agent.finished? || agent.finishing?
|
|
156
171
|
raise CommandFailed.new(nil, self), "task #{task} has an execution agent but it is dead"
|
|
@@ -173,7 +188,7 @@ module Roby::TaskStructure
|
|
|
173
188
|
module ExecutionAgentSpawn
|
|
174
189
|
# Hook into plan discovery to add execution agents to new tasks.
|
|
175
190
|
# See ExecutionAgentSpawn.spawn
|
|
176
|
-
def
|
|
191
|
+
def added_tasks(tasks)
|
|
177
192
|
# For now, settle on adding the execution agents only in the
|
|
178
193
|
# main plan. Otherwise, it is possible that two transactions
|
|
179
194
|
# will try to add two different agents
|