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/plan.rb
CHANGED
@@ -1,33 +1,46 @@
|
|
1
|
-
require 'roby/event'
|
2
|
-
require 'roby/task'
|
3
|
-
require 'roby/relations'
|
4
|
-
require 'roby/basic_object'
|
5
|
-
|
6
1
|
module Roby
|
7
|
-
# A plan object
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
2
|
+
# A plan object manages a collection of tasks and events.
|
3
|
+
#
|
4
|
+
# == Adding and removing objects from plans
|
5
|
+
# The #add, #add_mission and #add_permanent calls allow to add objects in
|
6
|
+
# plans. The #remove_object removes the same objects from the plan. Note
|
7
|
+
# that you should never remove objects yourself: a GC mechanism will do
|
8
|
+
# that properly for you, taking into account the consequences of the object
|
9
|
+
# removal.
|
13
10
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
11
|
+
# To reduce the complexity of object management, a garbage collection
|
12
|
+
# mechanism is in place during the plan execution, stopping and removing
|
13
|
+
# tasks that are not useful anymore for the system's goals. This garbage
|
14
|
+
# collection mechanism runs at the end of the execution cycle. Once an
|
15
|
+
# object is not active (i.e. for a task, once it is stopped), the object is
|
16
|
+
# /finalized/ and either the #finalized_task or the #finalized_event hook is
|
17
|
+
# called.
|
18
|
+
#
|
19
|
+
# Two special kinds of objects exist in plans:
|
20
|
+
# * the +missions+ (#missions, #mission?, #add_mission and #unmark_mission) are the
|
21
|
+
# final goals of the system. A task is +useful+ if it helps into the
|
22
|
+
# Realization of a mission (it is the child of a mission through one of the
|
23
|
+
# task relations).
|
24
|
+
# * the +permanent+ objects (#add_permanent, #unmark_permanent, #permanent?, #permanent_tasks and
|
25
|
+
# #permanent_events) are plan objects that are not affected by the plan's
|
26
|
+
# garbage collection mechanism. As for missions, task that are useful to
|
27
|
+
# permanent tasks are also
|
25
28
|
#
|
26
29
|
class Plan < BasicObject
|
27
30
|
extend Logger::Hierarchy
|
28
31
|
extend Logger::Forward
|
29
32
|
|
30
|
-
|
33
|
+
# The ExecutionEngine object which handles this plan. The role of this
|
34
|
+
# object is to provide the event propagation, error propagation and
|
35
|
+
# garbage collection mechanisms for the execution.
|
36
|
+
attr_accessor :engine
|
37
|
+
# The DecisionControl object which is associated with this plan. This
|
38
|
+
# object's role is to handle the conflicts that can occur during event
|
39
|
+
# propagation.
|
40
|
+
def control; engine.control end
|
41
|
+
|
42
|
+
# The task index for this plan. This is a TaskIndex object which allows
|
43
|
+
# efficient resolving of queries.
|
31
44
|
attr_reader :task_index
|
32
45
|
|
33
46
|
# The list of tasks that are included in this plan
|
@@ -35,13 +48,16 @@ module Roby
|
|
35
48
|
# The set of events that are defined by #known_tasks
|
36
49
|
attr_reader :task_events
|
37
50
|
# The list of the robot's missions. Do not change that set directly, use
|
38
|
-
# #
|
51
|
+
# #add_mission and #remove_mission instead.
|
39
52
|
attr_reader :missions
|
53
|
+
# The list of tasks that are kept outside GC. Do not change that set
|
54
|
+
# directly, use #permanent and #auto instead.
|
55
|
+
attr_reader :permanent_tasks
|
40
56
|
# The list of events that are not included in a task
|
41
57
|
attr_reader :free_events
|
42
|
-
# The list of
|
58
|
+
# The list of events that are kept outside GC. Do not change that set
|
43
59
|
# directly, use #permanent and #auto instead.
|
44
|
-
attr_reader :
|
60
|
+
attr_reader :permanent_events
|
45
61
|
|
46
62
|
# A map of event => task repairs. Whenever an exception is found,
|
47
63
|
# exception propagation checks that no repair is defined for that
|
@@ -70,9 +86,17 @@ module Roby
|
|
70
86
|
end
|
71
87
|
end
|
72
88
|
|
89
|
+
# The set of relations available for this plan
|
90
|
+
attr_reader :relations
|
91
|
+
|
92
|
+
# The propagation engine for this object. It is either nil (if no
|
93
|
+
# propagation engine is available) or self.
|
94
|
+
attr_reader :propagation_engine
|
95
|
+
|
73
96
|
def initialize
|
74
97
|
@missions = ValueSet.new
|
75
|
-
@
|
98
|
+
@permanent_tasks = ValueSet.new
|
99
|
+
@permanent_events = ValueSet.new
|
76
100
|
@known_tasks = ValueSet.new
|
77
101
|
@free_events = ValueSet.new
|
78
102
|
@task_events = ValueSet.new
|
@@ -80,16 +104,34 @@ module Roby
|
|
80
104
|
@gc_quarantine = ValueSet.new
|
81
105
|
@transactions = ValueSet.new
|
82
106
|
@repairs = Hash.new
|
107
|
+
@exception_handlers = Array.new
|
108
|
+
|
109
|
+
@relations = TaskStructure.relations + EventStructure.relations
|
110
|
+
@structure_checks = relations.
|
111
|
+
map { |r| r.method(:check_structure) if r.respond_to?(:check_structure) }.
|
112
|
+
compact
|
83
113
|
|
84
114
|
@task_index = Roby::TaskIndex.new
|
85
115
|
|
86
116
|
super() if defined? super
|
87
117
|
end
|
88
118
|
|
89
|
-
def inspect
|
119
|
+
def inspect # :nodoc:
|
90
120
|
"#<#{to_s}: missions=#{missions.to_s} tasks=#{known_tasks.to_s} events=#{free_events.to_s} transactions=#{transactions.to_s}>"
|
91
121
|
end
|
92
122
|
|
123
|
+
# Calls the given block in the execution thread of this plan's engine.
|
124
|
+
# If there is no engine attached to this plan, yields immediately
|
125
|
+
#
|
126
|
+
# See ExecutionEngine#execute
|
127
|
+
def execute(&block)
|
128
|
+
if engine
|
129
|
+
engine.execute(&block)
|
130
|
+
else
|
131
|
+
yield
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
93
135
|
# call-seq:
|
94
136
|
# plan.partition_event_task(objects) => events, tasks
|
95
137
|
#
|
@@ -119,51 +161,138 @@ module Roby
|
|
119
161
|
ret
|
120
162
|
end
|
121
163
|
|
122
|
-
|
123
|
-
|
124
|
-
|
164
|
+
# Returns the set of stacked transaction, starting at +self+
|
165
|
+
def transaction_stack
|
166
|
+
plan_chain = [self]
|
167
|
+
while plan_chain.last.respond_to?(:plan)
|
168
|
+
plan_chain << plan_chain.last.plan
|
169
|
+
end
|
170
|
+
plan_chain
|
171
|
+
end
|
172
|
+
|
173
|
+
# Inserts a new mission in the plan.
|
174
|
+
#
|
175
|
+
# In the plan manager, missions are the tasks which constitute the
|
176
|
+
# robot's goal. This is the base for two things:
|
177
|
+
# * if a mission fails, the MissionFailedError is raised
|
178
|
+
# * the mission and all the tasks and events which are useful for it,
|
179
|
+
# are not removed automatically by the garbage collection mechanism.
|
180
|
+
# A task or event is <b>useful</b> if it is part of the child subgraph
|
181
|
+
# of the mission, i.e. if there is a path in the relation graphs where
|
182
|
+
# the mission is the source and the task is the target.
|
183
|
+
def add_mission(task)
|
125
184
|
return if @missions.include?(task)
|
126
185
|
|
127
186
|
@missions << task
|
128
|
-
|
187
|
+
add(task)
|
129
188
|
task.mission = true if task.self_owned?
|
130
|
-
|
189
|
+
added_mission(task)
|
131
190
|
self
|
132
191
|
end
|
192
|
+
def insert(task) # :nodoc:
|
193
|
+
Roby.warn_deprecated "#insert has been replaced by #add_mission"
|
194
|
+
add_mission(task)
|
195
|
+
end
|
133
196
|
# Hook called when +tasks+ have been inserted in this plan
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
def auto(task); @keepalive.delete(task) end
|
144
|
-
|
145
|
-
def edit
|
146
|
-
if block_given?
|
147
|
-
Roby::Control.synchronize do
|
148
|
-
yield
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
197
|
+
def added_mission(tasks)
|
198
|
+
super if defined? super
|
199
|
+
if respond_to?(:inserted)
|
200
|
+
Roby.warn_deprecated "the #inserted hook has been replaced by #added_mission"
|
201
|
+
inserted(tasks)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
# Checks if +task+ is a mission of this plan
|
205
|
+
def mission?(task); @missions.include?(task) end
|
152
206
|
|
153
|
-
|
207
|
+
def remove_mission(task) # :nodoc:
|
208
|
+
Roby.warn_deprecated "#remove_mission renamed #unmark_mission"
|
209
|
+
unmark_mission(task)
|
210
|
+
end
|
154
211
|
|
155
212
|
# Removes the task in +tasks+ from the list of missions
|
156
|
-
def
|
213
|
+
def unmark_mission(task)
|
157
214
|
@missions.delete(task)
|
158
|
-
|
215
|
+
add(task)
|
159
216
|
task.mission = false if task.self_owned?
|
160
217
|
|
161
|
-
|
218
|
+
unmarked_mission(task)
|
162
219
|
self
|
163
220
|
end
|
164
221
|
# Hook called when +tasks+ have been discarded from this plan
|
165
|
-
def
|
222
|
+
def unmarked_mission(task)
|
223
|
+
super if defined? super
|
224
|
+
if respond_to?(:removed_mission)
|
225
|
+
Roby.warn_deprecated "the #removed_mission hook has been replaced by #unmarked_mission"
|
226
|
+
removed_mission(task)
|
227
|
+
end
|
228
|
+
if respond_to?(:discarded)
|
229
|
+
Roby.warn_deprecated "the #discarded hook has been replaced by #unmarked_mission"
|
230
|
+
discarded(task)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
def discard(task) # :nodoc:
|
234
|
+
Roby.warn_deprecated "#discard has been replaced by #unmark_mission"
|
235
|
+
unmark_mission(task)
|
236
|
+
end
|
237
|
+
|
238
|
+
# Adds +object+ in the list of permanent tasks. Permanent tasks are
|
239
|
+
# tasks that are not to be subject to the plan's garbage collection
|
240
|
+
# mechanism (i.e. they will not be removed even though they are not
|
241
|
+
# directly linked to a mission).
|
242
|
+
#
|
243
|
+
# #object is at the same time added in the plan, meaning that all the
|
244
|
+
# tasks and events related to it are added in the plan as well. See
|
245
|
+
# #add.
|
246
|
+
#
|
247
|
+
# Unlike missions, the failure of a permanent task does not constitute
|
248
|
+
# an error.
|
249
|
+
#
|
250
|
+
# See also #unmark_permanent and #permanent?
|
251
|
+
def add_permanent(object)
|
252
|
+
if object.kind_of?(Task)
|
253
|
+
@permanent_tasks << object
|
254
|
+
else
|
255
|
+
@permanent_events << object
|
256
|
+
end
|
257
|
+
add(object)
|
258
|
+
self
|
259
|
+
end
|
260
|
+
|
261
|
+
def permanent(object) # :nodoc:
|
262
|
+
Roby.warn_deprecated "#permanent has been replaced by #add_permanent"
|
263
|
+
add_permanent(object)
|
264
|
+
end
|
265
|
+
|
266
|
+
# Removes +object+ from the list of permanent objects. Permanent objects
|
267
|
+
# are protected from the plan's garbage collection. This does not remove
|
268
|
+
# the task/event itself from the plan.
|
269
|
+
#
|
270
|
+
# See also #add_permanent and #permanent?
|
271
|
+
def unmark_permanent(object)
|
272
|
+
@permanent_tasks.delete(object)
|
273
|
+
@permanent_events.delete(object)
|
274
|
+
end
|
275
|
+
|
276
|
+
def auto(obj) # :nodoc:
|
277
|
+
Roby.warn_deprecated "#auto has been replaced by #unmark_permanent"
|
278
|
+
unmark_permanent(obj)
|
279
|
+
end
|
280
|
+
|
281
|
+
# True if +obj+ is neither a permanent task nor a permanent object.
|
282
|
+
#
|
283
|
+
# See also #add_permanent and #unmark_permanent
|
284
|
+
def permanent?(obj); @permanent_tasks.include?(obj) || @permanent_events.include?(obj) end
|
166
285
|
|
286
|
+
def edit
|
287
|
+
if block_given?
|
288
|
+
Roby.synchronize do
|
289
|
+
yield
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# True if this plan owns the given object, i.e. if all the owners of the
|
295
|
+
# object are also owners of the plan.
|
167
296
|
def owns?(object)
|
168
297
|
(object.owners - owners).empty?
|
169
298
|
end
|
@@ -175,43 +304,55 @@ module Roby
|
|
175
304
|
@free_events.each { |e| e.clear_relations }
|
176
305
|
@free_events.clear
|
177
306
|
@missions.clear
|
178
|
-
@
|
307
|
+
@permanent_tasks.clear
|
308
|
+
@permanent_events.clear
|
179
309
|
end
|
180
310
|
|
181
|
-
def handle_replace(from, to)
|
311
|
+
def handle_replace(from, to) # :nodoc:
|
182
312
|
return if from == to
|
183
313
|
|
184
314
|
# Check that +to+ is valid in all hierarchy relations where +from+ is a child
|
185
315
|
if !to.fullfills?(*from.fullfilled_model)
|
186
|
-
raise InvalidReplace.new(from, to, "to does not
|
316
|
+
raise InvalidReplace.new(from, to), "task #{to} does not fullfill #{from.fullfilled_model}"
|
187
317
|
end
|
188
318
|
|
189
319
|
# Check that +to+ is in the same execution state than +from+
|
190
320
|
if !to.compatible_state?(from)
|
191
|
-
raise InvalidReplace.new(from, to, "state
|
321
|
+
raise InvalidReplace.new(from, to), "cannot replace #{from} by #{to} as their state is incompatible: from is #{from.running?} and to is #{to.running?}"
|
192
322
|
end
|
193
323
|
|
194
324
|
# Swap the subplans of +from+ and +to+
|
195
325
|
yield(from, to)
|
196
326
|
|
197
|
-
replaced(from, to)
|
198
327
|
if mission?(from)
|
199
|
-
|
200
|
-
|
328
|
+
unmark_mission(from)
|
329
|
+
add_mission(to)
|
201
330
|
elsif permanent?(from)
|
202
|
-
|
203
|
-
|
331
|
+
unmark_permanent(from)
|
332
|
+
add_permanent(to)
|
204
333
|
else
|
205
|
-
|
334
|
+
add(to)
|
206
335
|
end
|
336
|
+
replaced(from, to)
|
207
337
|
end
|
208
338
|
|
339
|
+
# Replace the task +from+ by +to+ in all relations +from+ is part of
|
340
|
+
# (including events).
|
341
|
+
#
|
342
|
+
# See also #replace
|
209
343
|
def replace_task(from, to)
|
210
344
|
handle_replace(from, to) do
|
211
345
|
from.replace_by(to)
|
212
346
|
end
|
213
347
|
end
|
214
348
|
|
349
|
+
# Replace +from+ by +to+ in the plan, in all relations in which +from+
|
350
|
+
# and its events are /children/. It therefore replaces the subplan
|
351
|
+
# generated by +from+ (i.e. +from+ and all the tasks/events that can be
|
352
|
+
# reached by following the task and event relations) by the subplan
|
353
|
+
# generated by +to+.
|
354
|
+
#
|
355
|
+
# See also #replace_task
|
215
356
|
def replace(from, to)
|
216
357
|
handle_replace(from, to) do
|
217
358
|
from.replace_subplan_by(to)
|
@@ -225,12 +366,24 @@ module Roby
|
|
225
366
|
# plain Plan objects and false for transcations
|
226
367
|
def executable?; true end
|
227
368
|
|
369
|
+
def discover(objects) # :nodoc:
|
370
|
+
Roby.warn_deprecated "#discover has been replaced by #add"
|
371
|
+
add(objects)
|
372
|
+
end
|
373
|
+
|
228
374
|
# call-seq:
|
229
|
-
|
375
|
+
# plan.add(task) => plan
|
376
|
+
# plan.add(event) => plan
|
377
|
+
# plan.add([task, event, task2, ...]) => plan
|
378
|
+
# plan.add([t1, t2, ...]) => plan
|
230
379
|
#
|
231
|
-
|
232
|
-
|
233
|
-
|
380
|
+
# Adds the subplan of the given tasks and events into the plan.
|
381
|
+
#
|
382
|
+
# That means that it adds the listed tasks/events and the task/events
|
383
|
+
# that are reachable through any relations). The #added_events and
|
384
|
+
# #added_tasks hooks are called for the objects that were not in
|
385
|
+
# the plan.
|
386
|
+
def add(objects)
|
234
387
|
event_seeds, tasks = partition_event_task(objects)
|
235
388
|
event_seeds = (event_seeds || ValueSet.new).to_value_set
|
236
389
|
|
@@ -239,7 +392,7 @@ module Roby
|
|
239
392
|
new_tasks = useful_task_component(nil, tasks, tasks)
|
240
393
|
unless new_tasks.empty?
|
241
394
|
old_task_events = task_events.dup
|
242
|
-
new_tasks =
|
395
|
+
new_tasks = add_task_set(new_tasks)
|
243
396
|
event_seeds.merge(task_events - old_task_events)
|
244
397
|
end
|
245
398
|
end
|
@@ -257,17 +410,17 @@ module Roby
|
|
257
410
|
end
|
258
411
|
end
|
259
412
|
|
260
|
-
|
413
|
+
add_event_set(events - task_events - free_events)
|
261
414
|
end
|
262
415
|
|
263
416
|
self
|
264
417
|
end
|
265
418
|
|
266
|
-
# Add +events+ to the set of known events and call
|
419
|
+
# Add +events+ to the set of known events and call added_events
|
267
420
|
# for the new events
|
268
421
|
#
|
269
|
-
# This is for internal use, use #
|
270
|
-
def
|
422
|
+
# This is for internal use, use #add instead
|
423
|
+
def add_event_set(events)
|
271
424
|
events = events.difference(free_events)
|
272
425
|
events.delete_if do |e|
|
273
426
|
if !e.root_object?
|
@@ -280,17 +433,17 @@ module Roby
|
|
280
433
|
|
281
434
|
unless events.empty?
|
282
435
|
free_events.merge(events)
|
283
|
-
|
436
|
+
added_events(events)
|
284
437
|
end
|
285
438
|
|
286
439
|
events
|
287
440
|
end
|
288
441
|
|
289
|
-
# Add +tasks+ to the set of known tasks and call
|
442
|
+
# Add +tasks+ to the set of known tasks and call added_tasks for
|
290
443
|
# the new tasks
|
291
444
|
#
|
292
|
-
# This is for internal use, use #
|
293
|
-
def
|
445
|
+
# This is for internal use, use #add instead
|
446
|
+
def add_task_set(tasks)
|
294
447
|
tasks = tasks.difference(known_tasks)
|
295
448
|
for t in tasks
|
296
449
|
t.plan = self
|
@@ -298,7 +451,7 @@ module Roby
|
|
298
451
|
task_index.add t
|
299
452
|
end
|
300
453
|
known_tasks.merge tasks
|
301
|
-
|
454
|
+
added_tasks(tasks)
|
302
455
|
|
303
456
|
for t in tasks
|
304
457
|
t.instantiate_model_event_relations
|
@@ -306,18 +459,45 @@ module Roby
|
|
306
459
|
tasks
|
307
460
|
end
|
308
461
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
462
|
+
def added_tasks(tasks)
|
463
|
+
if respond_to?(:discovered)
|
464
|
+
Roby.warn_deprecated "the #discovered hook is deprecated, use #added_tasks instead"
|
465
|
+
discovered(tasks)
|
466
|
+
end
|
467
|
+
if respond_to?(:discovered_tasks)
|
468
|
+
Roby.warn_deprecated "the #discovered_tasks hook is deprecated, use #added_tasks instead"
|
469
|
+
discovered_tasks(tasks)
|
470
|
+
end
|
471
|
+
|
472
|
+
if engine
|
473
|
+
engine.event_ordering.clear
|
474
|
+
end
|
475
|
+
super if defined? super
|
476
|
+
end
|
477
|
+
|
316
478
|
# Hook called when new events have been discovered in this plan
|
317
|
-
def
|
479
|
+
def added_events(events)
|
480
|
+
if engine
|
481
|
+
engine.event_ordering.clear
|
482
|
+
end
|
483
|
+
|
484
|
+
if respond_to?(:discovered_events)
|
485
|
+
Roby.warn_deprecated "the #discovered_events hook has been replaced by #added_events"
|
486
|
+
discovered_events(events)
|
487
|
+
end
|
318
488
|
super if defined? super
|
319
489
|
end
|
320
490
|
|
491
|
+
# Creates a new transaction and yields it. Ensures that the transaction
|
492
|
+
# is discarded if the block returns without having committed it.
|
493
|
+
def in_transaction
|
494
|
+
yield(trsc = Transaction.new(self))
|
495
|
+
|
496
|
+
ensure
|
497
|
+
if trsc && !trsc.finalized?
|
498
|
+
trsc.discard_transaction
|
499
|
+
end
|
500
|
+
end
|
321
501
|
# Hook called when a new transaction has been built on top of this plan
|
322
502
|
def added_transaction(trsc); super if defined? super end
|
323
503
|
# Removes the transaction +trsc+ from the list of known transactions
|
@@ -334,7 +514,7 @@ module Roby
|
|
334
514
|
def useful_task_component(complete_set, useful_set, seeds)
|
335
515
|
old_useful_set = useful_set.dup
|
336
516
|
for rel in TaskStructure.relations
|
337
|
-
next
|
517
|
+
next if !rel.root_relation?
|
338
518
|
for subgraph in rel.generated_subgraphs(seeds, false)
|
339
519
|
useful_set.merge(subgraph)
|
340
520
|
end
|
@@ -356,17 +536,17 @@ module Roby
|
|
356
536
|
# Remove all missions that are finished
|
357
537
|
for finished_mission in (@missions & task_index.by_state[:finished?])
|
358
538
|
if !task_index.repaired_tasks.include?(finished_mission)
|
359
|
-
|
539
|
+
unmark_mission(finished_mission)
|
360
540
|
end
|
361
541
|
end
|
362
|
-
for finished_permanent in (@
|
542
|
+
for finished_permanent in (@permanent_tasks & task_index.by_state[:finished?])
|
363
543
|
if !task_index.repaired_tasks.include?(finished_permanent)
|
364
|
-
|
544
|
+
unmark_permanent(finished_permanent)
|
365
545
|
end
|
366
546
|
end
|
367
547
|
|
368
548
|
# Create the set of tasks which must be kept as-is
|
369
|
-
seeds = @missions | @
|
549
|
+
seeds = @missions | @permanent_tasks
|
370
550
|
for trsc in transactions
|
371
551
|
seeds.merge trsc.proxy_objects.keys.to_value_set
|
372
552
|
end
|
@@ -446,7 +626,7 @@ module Roby
|
|
446
626
|
# 'useful' when they are chained to a task.
|
447
627
|
def useful_events
|
448
628
|
return ValueSet.new if free_events.empty?
|
449
|
-
(free_events & useful_event_component(
|
629
|
+
(free_events & useful_event_component(permanent_events.dup))
|
450
630
|
end
|
451
631
|
|
452
632
|
# The set of events that can be removed from the plan
|
@@ -462,38 +642,36 @@ module Roby
|
|
462
642
|
|
463
643
|
# Checks if +task+ is included in this plan
|
464
644
|
def include?(object); @known_tasks.include?(object) || @free_events.include?(object) end
|
465
|
-
# Checks if +task+ is a mission of this plan
|
466
|
-
def mission?(task); @missions.include?(task) end
|
467
645
|
# Count of tasks in this plan
|
468
646
|
def size; @known_tasks.size end
|
469
647
|
# Returns true if there is no task in this plan
|
470
648
|
def empty?; @known_tasks.empty? end
|
471
649
|
# Iterates on all tasks
|
472
650
|
def each_task; @known_tasks.each { |t| yield(t) } end
|
473
|
-
|
474
|
-
# Install a plan repair for +failure_point+ with +task+.
|
475
|
-
#
|
651
|
+
|
652
|
+
# Install a plan repair for +failure_point+ with +task+. A plan repair
|
653
|
+
# is a task which, during its lifetime, is supposed to fix the problem
|
654
|
+
# encountered at +failure_point+
|
655
|
+
#
|
656
|
+
# +failure_point+ is an Event object which represents the event causing
|
657
|
+
# the problem.
|
476
658
|
#
|
477
659
|
# See also #repairs and #remove_repair
|
478
660
|
def add_repair(failure_point, task)
|
479
661
|
if !failure_point.kind_of?(Event)
|
480
662
|
raise TypeError, "failure point #{failure_point} should be an event"
|
481
663
|
elsif task.plan && task.plan != self
|
482
|
-
raise ArgumentError, "wrong plan: #{task} is in #{task.plan}, not #{
|
664
|
+
raise ArgumentError, "wrong plan: #{task} is in #{task.plan}, not #{plan}"
|
483
665
|
elsif repairs.has_key?(failure_point)
|
484
666
|
raise ArgumentError, "there is already a plan repair defined for #{failure_point}: #{repairs[failure_point]}"
|
485
667
|
elsif !task.plan
|
486
|
-
|
668
|
+
add(task)
|
487
669
|
end
|
488
670
|
|
489
671
|
repairs[failure_point] = task
|
490
672
|
if failure_point.generator.respond_to?(:task)
|
491
673
|
task_index.repaired_tasks << failure_point.generator.task
|
492
674
|
end
|
493
|
-
|
494
|
-
if task.pending?
|
495
|
-
Roby.once { task.start! }
|
496
|
-
end
|
497
675
|
end
|
498
676
|
|
499
677
|
# Removes +task+ from the set of active plan repairs.
|
@@ -544,11 +722,13 @@ module Roby
|
|
544
722
|
# Otherwise, raises ArgumentError.
|
545
723
|
#
|
546
724
|
# This method is provided for consistency with Transaction#[]
|
547
|
-
def [](object)
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
725
|
+
def [](object, create = true)
|
726
|
+
if !object.plan && !object.finalized?
|
727
|
+
add(object)
|
728
|
+
elsif object.finalized? && create
|
729
|
+
raise ArgumentError, "#{object} is has been finalized, and can't be reused"
|
730
|
+
elsif object.plan != self
|
731
|
+
raise ArgumentError, "#{object} is not from #{self}"
|
552
732
|
end
|
553
733
|
object
|
554
734
|
end
|
@@ -561,109 +741,13 @@ module Roby
|
|
561
741
|
end
|
562
742
|
end
|
563
743
|
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
force_gc.merge(force_on.to_value_set)
|
568
|
-
end
|
569
|
-
|
570
|
-
# The set of tasks for which we queued stop! at this cycle
|
571
|
-
# #finishing? is false until the next event propagation cycle
|
572
|
-
finishing = ValueSet.new
|
573
|
-
did_something = true
|
574
|
-
while did_something
|
575
|
-
did_something = false
|
576
|
-
|
577
|
-
tasks = unneeded_tasks | force_gc
|
578
|
-
local_tasks = self.local_tasks & tasks
|
579
|
-
remote_tasks = tasks - local_tasks
|
580
|
-
|
581
|
-
# Remote tasks are simply removed, regardless of other concerns
|
582
|
-
for t in remote_tasks
|
583
|
-
Plan.debug { "GC: removing the remote task #{t}" }
|
584
|
-
remove_object(t)
|
585
|
-
end
|
586
|
-
|
587
|
-
break if local_tasks.empty?
|
588
|
-
|
589
|
-
if local_tasks.all? { |t| t.pending? || t.finished? }
|
590
|
-
local_tasks.each do |t|
|
591
|
-
Plan.debug { "GC: #{t} is not running, removed" }
|
592
|
-
garbage(t)
|
593
|
-
remove_object(t)
|
594
|
-
end
|
595
|
-
break
|
596
|
-
end
|
597
|
-
|
598
|
-
# Mark all root local_tasks as garbage
|
599
|
-
roots = nil
|
600
|
-
2.times do |i|
|
601
|
-
roots = local_tasks.find_all do |t|
|
602
|
-
if t.root?
|
603
|
-
garbage(t)
|
604
|
-
true
|
605
|
-
else
|
606
|
-
Plan.debug { "GC: ignoring #{t}, it is not root" }
|
607
|
-
false
|
608
|
-
end
|
609
|
-
end
|
610
|
-
|
611
|
-
break if i == 1 || !roots.empty?
|
612
|
-
|
613
|
-
# There is a cycle somewhere. Try to break it by removing
|
614
|
-
# weak relations within elements of local_tasks
|
615
|
-
Plan.debug "cycle found, removing weak relations"
|
616
|
-
|
617
|
-
local_tasks.each do |t|
|
618
|
-
next if t.root?
|
619
|
-
t.each_graph do |rel|
|
620
|
-
rel.remove(t) if rel.weak?
|
621
|
-
end
|
622
|
-
end
|
623
|
-
end
|
624
|
-
|
625
|
-
(roots.to_value_set - finishing - gc_quarantine).each do |local_task|
|
626
|
-
if local_task.pending?
|
627
|
-
Plan.info "GC: removing pending task #{local_task}"
|
628
|
-
remove_object(local_task)
|
629
|
-
did_something = true
|
630
|
-
elsif local_task.starting?
|
631
|
-
# wait for task to be started before killing it
|
632
|
-
Plan.debug { "GC: #{local_task} is starting" }
|
633
|
-
elsif local_task.finished?
|
634
|
-
Plan.debug { "GC: #{local_task} is not running, removed" }
|
635
|
-
remove_object(local_task)
|
636
|
-
did_something = true
|
637
|
-
elsif !local_task.finishing?
|
638
|
-
if local_task.event(:stop).controlable?
|
639
|
-
Plan.debug { "GC: queueing #{local_task}/stop" }
|
640
|
-
if !local_task.respond_to?(:stop!)
|
641
|
-
Plan.fatal "something fishy: #{local_task}/stop is controlable but there is no #stop! method"
|
642
|
-
gc_quarantine << local_task
|
643
|
-
else
|
644
|
-
finishing << local_task
|
645
|
-
Roby::Control.once do
|
646
|
-
Plan.debug { "GC: stopping #{local_task}" }
|
647
|
-
local_task.stop!(nil)
|
648
|
-
end
|
649
|
-
end
|
650
|
-
else
|
651
|
-
Plan.warn "GC: ignored #{local_task}, it cannot be stopped"
|
652
|
-
gc_quarantine << local_task
|
653
|
-
end
|
654
|
-
elsif local_task.finishing?
|
655
|
-
Plan.debug { "GC: waiting for #{local_task} to finish" }
|
656
|
-
else
|
657
|
-
Plan.warn "GC: ignored #{local_task}"
|
658
|
-
end
|
659
|
-
end
|
660
|
-
end
|
661
|
-
|
662
|
-
unneeded_events.each do |event|
|
663
|
-
remove_object(event)
|
664
|
-
end
|
665
|
-
end
|
744
|
+
def discard_modifications(object)
|
745
|
+
remove_object(object)
|
746
|
+
end
|
666
747
|
|
748
|
+
# Remove +object+ from this plan. You usually don't have to do that
|
749
|
+
# manually. Object removal is handled by the plan's garbage collection
|
750
|
+
# mechanism.
|
667
751
|
def remove_object(object)
|
668
752
|
if !object.root_object?
|
669
753
|
raise ArgumentError, "cannot remove #{object} which is a non-root object"
|
@@ -687,8 +771,12 @@ module Roby
|
|
687
771
|
|
688
772
|
@free_events.delete(object)
|
689
773
|
@missions.delete(object)
|
774
|
+
if object.respond_to? :mission=
|
775
|
+
object.mission = false
|
776
|
+
end
|
690
777
|
@known_tasks.delete(object)
|
691
|
-
@
|
778
|
+
@permanent_tasks.delete(object)
|
779
|
+
@permanent_events.delete(object)
|
692
780
|
force_gc.delete(object)
|
693
781
|
|
694
782
|
object.plan = nil
|
@@ -714,11 +802,6 @@ module Roby
|
|
714
802
|
self
|
715
803
|
end
|
716
804
|
|
717
|
-
# Backward compatibility
|
718
|
-
def remove_task(t) # :nodoc:
|
719
|
-
remove_object(t)
|
720
|
-
end
|
721
|
-
|
722
805
|
# Hook called when +task+ is marked as garbage. It will be garbage
|
723
806
|
# collected as soon as possible
|
724
807
|
def garbage(task)
|
@@ -741,22 +824,123 @@ module Roby
|
|
741
824
|
def finalized(task) # :nodoc:
|
742
825
|
super if defined? super
|
743
826
|
end
|
827
|
+
|
744
828
|
# Hook called when +task+ has been removed from this plan
|
745
829
|
def finalized_task(task)
|
830
|
+
finalized_transaction_object(task) { |trsc, proxy| trsc.finalized_plan_task(proxy) }
|
746
831
|
super if defined? super
|
747
832
|
finalized(task)
|
748
833
|
end
|
834
|
+
|
749
835
|
# Hook called when +event+ has been removed from this plan
|
750
|
-
def finalized_event(event)
|
836
|
+
def finalized_event(event)
|
837
|
+
if engine && executable?
|
838
|
+
engine.finalized_event(event)
|
839
|
+
end
|
840
|
+
finalized_transaction_object(event) { |trsc, proxy| trsc.finalized_plan_event(proxy) }
|
841
|
+
super if defined? super
|
842
|
+
end
|
843
|
+
|
844
|
+
# Generic filter which checks if +object+ is included in one of the
|
845
|
+
# transactions of this plan. If it is the case, it yields the
|
846
|
+
# transaction and the associated proxy
|
847
|
+
def finalized_transaction_object(object)
|
848
|
+
return unless object.root_object?
|
849
|
+
for trsc in transactions
|
850
|
+
next unless trsc.proxying?
|
851
|
+
|
852
|
+
if proxy = trsc.wrap(object, false)
|
853
|
+
yield(trsc, proxy)
|
854
|
+
end
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
858
|
+
# Replace +task+ with a fresh copy of itself.
|
859
|
+
#
|
860
|
+
# The new task takes the place of the old one in the plan: any relation
|
861
|
+
# that was going to/from +task+ or one of its events is removed, and the
|
862
|
+
# corresponding one is created, but this time involving the newly
|
863
|
+
# created task.
|
864
|
+
def recreate(task)
|
865
|
+
new_task = task.create_fresh_copy
|
866
|
+
replace_task(task, new_task)
|
867
|
+
new_task
|
868
|
+
end
|
751
869
|
|
752
|
-
# Replace +task+ with a fresh copy of itself
|
870
|
+
# Replace +task+ with a fresh copy of itself and start it.
|
871
|
+
#
|
872
|
+
# See #recreate for details about the new task.
|
753
873
|
def respawn(task)
|
754
|
-
|
874
|
+
new = recreate(task)
|
875
|
+
engine.once { new.start!(nil) }
|
876
|
+
new
|
877
|
+
end
|
878
|
+
|
879
|
+
# The set of blocks that should be called to check the structure of the
|
880
|
+
# plan. See also Plan.structure_checks.
|
881
|
+
attr_reader :structure_checks
|
882
|
+
|
883
|
+
@structure_checks = Array.new
|
884
|
+
class << self
|
885
|
+
# A set of structure checking procedures that must be performed on all plans
|
886
|
+
attr_reader :structure_checks
|
887
|
+
end
|
888
|
+
|
889
|
+
# Get all missions that have failed
|
890
|
+
def self.check_failed_missions(plan)
|
891
|
+
result = []
|
892
|
+
for task in plan.missions
|
893
|
+
result << MissionFailedError.new(task) if task.failed?
|
894
|
+
end
|
895
|
+
result
|
896
|
+
end
|
897
|
+
structure_checks << method(:check_failed_missions)
|
898
|
+
|
899
|
+
# Perform the structure checking step by calling the procs registered
|
900
|
+
# in #structure_checks and Plan.structure_checks. These procs are
|
901
|
+
# supposed to return a collection of exception objects, or nil if no
|
902
|
+
# error has been found
|
903
|
+
def check_structure
|
904
|
+
# Do structure checking and gather the raised exceptions
|
905
|
+
exceptions = {}
|
906
|
+
for prc in (Plan.structure_checks + structure_checks)
|
907
|
+
begin
|
908
|
+
new_exceptions = prc.call(self)
|
909
|
+
rescue Exception => e
|
910
|
+
if engine
|
911
|
+
engine.add_framework_error(e, 'structure checking')
|
912
|
+
else
|
913
|
+
raise
|
914
|
+
end
|
915
|
+
end
|
916
|
+
next unless new_exceptions
|
755
917
|
|
756
|
-
|
757
|
-
|
758
|
-
|
918
|
+
[*new_exceptions].each do |e, tasks|
|
919
|
+
e = ExecutionEngine.to_execution_exception(e)
|
920
|
+
exceptions[e] = tasks
|
921
|
+
end
|
922
|
+
end
|
923
|
+
exceptions
|
759
924
|
end
|
925
|
+
|
926
|
+
|
927
|
+
include Roby::ExceptionHandlingObject
|
928
|
+
|
929
|
+
attr_reader :exception_handlers
|
930
|
+
def each_exception_handler(&iterator); exception_handlers.each(&iterator) end
|
931
|
+
def on_exception(*matchers, &handler)
|
932
|
+
check_arity(handler, 2)
|
933
|
+
exception_handlers.unshift [matchers, handler]
|
934
|
+
end
|
935
|
+
end
|
936
|
+
|
937
|
+
class << self
|
938
|
+
# Returns the main plan
|
939
|
+
attr_reader :plan
|
760
940
|
end
|
941
|
+
|
942
|
+
# Defines a global exception handler on the main plan.
|
943
|
+
# See also Plan#on_exception
|
944
|
+
def self.on_exception(*matchers, &handler); Roby.plan.on_exception(*matchers, &handler) end
|
761
945
|
end
|
762
946
|
|