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
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
|
|