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
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Roby
|
|
2
|
+
# TaskIndex objects are used to maintain a set of tasks as classified sets,
|
|
3
|
+
# speeding up query operations. See Plan#task_index.
|
|
4
|
+
class TaskIndex
|
|
5
|
+
# A model => ValueSet map of the tasks for each model
|
|
6
|
+
attr_reader :by_model
|
|
7
|
+
# A state => ValueSet map of tasks given their state. The state is
|
|
8
|
+
# a symbol in [:pending, :starting, :running, :finishing,
|
|
9
|
+
# :finished]
|
|
10
|
+
attr_reader :by_state
|
|
11
|
+
# A peer => ValueSet map of tasks given their owner.
|
|
12
|
+
attr_reader :by_owner
|
|
13
|
+
# The set of tasks which have an event which is being repaired
|
|
14
|
+
attr_reader :repaired_tasks
|
|
15
|
+
|
|
16
|
+
STATE_PREDICATES = [:pending?, :running?, :finished?, :success?, :failed?].to_value_set
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@by_model = Hash.new { |h, k| h[k] = ValueSet.new }
|
|
20
|
+
@by_state = Hash.new
|
|
21
|
+
STATE_PREDICATES.each do |state_name|
|
|
22
|
+
by_state[state_name] = ValueSet.new
|
|
23
|
+
end
|
|
24
|
+
@by_owner = Hash.new
|
|
25
|
+
@task_state = Hash.new
|
|
26
|
+
@repaired_tasks = ValueSet.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Add a new task to this index
|
|
30
|
+
def add(task)
|
|
31
|
+
for klass in task.model.ancestors
|
|
32
|
+
by_model[klass] << task
|
|
33
|
+
end
|
|
34
|
+
by_state[:pending?] << task
|
|
35
|
+
for owner in task.owners
|
|
36
|
+
add_owner(task, owner)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Updates the index to reflect that +new_owner+ now owns +task+
|
|
41
|
+
def add_owner(task, new_owner)
|
|
42
|
+
(by_owner[new_owner] ||= ValueSet.new) << task
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Updates the index to reflect that +peer+ no more owns +task+
|
|
46
|
+
def remove_owner(task, peer)
|
|
47
|
+
if set = by_owner[peer]
|
|
48
|
+
set.delete(task)
|
|
49
|
+
if set.empty?
|
|
50
|
+
by_owner.delete(peer)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Updates the index to reflect a change of state for +task+
|
|
56
|
+
def set_state(task, new_state)
|
|
57
|
+
for state_set in by_state
|
|
58
|
+
state_set.last.delete(task)
|
|
59
|
+
end
|
|
60
|
+
by_state[new_state] << task
|
|
61
|
+
if new_state == :success? || new_state == :failed?
|
|
62
|
+
by_state[:finished?] << task
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Remove all references of +task+ from the index.
|
|
67
|
+
def remove(task)
|
|
68
|
+
for klass in task.model.ancestors
|
|
69
|
+
by_model[klass].delete(task)
|
|
70
|
+
end
|
|
71
|
+
for state_set in by_state
|
|
72
|
+
state_set.last.delete(task)
|
|
73
|
+
end
|
|
74
|
+
for owner in task.owners
|
|
75
|
+
remove_owner(task, owner)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
data/lib/roby/test/common.rb
CHANGED
|
@@ -22,14 +22,24 @@ module Roby
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# The plan used by the tests
|
|
25
|
-
|
|
25
|
+
attr_reader :plan
|
|
26
|
+
# The decision control component used by the tests
|
|
27
|
+
attr_reader :control
|
|
28
|
+
def engine; plan.engine end
|
|
26
29
|
|
|
27
30
|
# Clear the plan and return it
|
|
28
31
|
def new_plan
|
|
29
|
-
|
|
32
|
+
plan.clear
|
|
30
33
|
plan
|
|
31
34
|
end
|
|
32
35
|
|
|
36
|
+
def deprecated_feature
|
|
37
|
+
Roby.enable_deprecation_warnings = false
|
|
38
|
+
yield
|
|
39
|
+
ensure
|
|
40
|
+
Roby.enable_deprecation_warnings = true
|
|
41
|
+
end
|
|
42
|
+
|
|
33
43
|
# a [collection, collection_backup] array of the collections saved
|
|
34
44
|
# by #original_collections
|
|
35
45
|
attr_reader :original_collections
|
|
@@ -54,18 +64,25 @@ module Roby
|
|
|
54
64
|
end
|
|
55
65
|
|
|
56
66
|
def setup
|
|
67
|
+
super if defined? super
|
|
68
|
+
|
|
57
69
|
@console_logger ||= false
|
|
70
|
+
@event_logger ||= false
|
|
58
71
|
if !defined? Roby::State
|
|
59
|
-
|
|
72
|
+
Roby.const_set(:State, StateSpace.new)
|
|
73
|
+
else
|
|
74
|
+
Roby::State.clear
|
|
60
75
|
end
|
|
61
76
|
|
|
62
77
|
@original_roby_logger_level = Roby.logger.level
|
|
63
78
|
@timings = { :start => Time.now }
|
|
64
79
|
|
|
65
80
|
@original_collections = []
|
|
66
|
-
Thread.abort_on_exception =
|
|
81
|
+
Thread.abort_on_exception = false
|
|
67
82
|
@remote_processes = []
|
|
68
83
|
|
|
84
|
+
Roby.app.setup_loggers
|
|
85
|
+
|
|
69
86
|
if Test.check_allocation_count
|
|
70
87
|
GC.start
|
|
71
88
|
GC.disable
|
|
@@ -79,34 +96,48 @@ module Roby
|
|
|
79
96
|
Roby::Planning::Planner.last_id = 0
|
|
80
97
|
end
|
|
81
98
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Roby.control.abort_on_application_exception = true
|
|
89
|
-
Roby.control.abort_on_framework_exception = true
|
|
99
|
+
@plan ||= Plan.new
|
|
100
|
+
@control ||= DecisionControl.new
|
|
101
|
+
if !plan.engine
|
|
102
|
+
ExecutionEngine.new(@plan, @control)
|
|
103
|
+
end
|
|
104
|
+
plan.engine.gc_warning = false
|
|
90
105
|
|
|
91
|
-
|
|
92
|
-
save_collection
|
|
106
|
+
# Save and restore some arrays
|
|
107
|
+
save_collection engine.propagation_handlers
|
|
108
|
+
save_collection Roby::ExecutionEngine.propagation_handlers
|
|
109
|
+
save_collection plan.structure_checks
|
|
110
|
+
save_collection Roby::Plan.structure_checks
|
|
111
|
+
save_collection engine.at_cycle_end_handlers
|
|
112
|
+
save_collection Roby::EventGenerator.event_gathering
|
|
113
|
+
Roby.app.abort_on_exception = true
|
|
114
|
+
Roby.app.abort_on_application_exception = true
|
|
93
115
|
|
|
94
|
-
save_collection
|
|
116
|
+
save_collection engine.event_ordering
|
|
117
|
+
save_collection engine.delayed_events
|
|
118
|
+
save_collection plan.exception_handlers
|
|
95
119
|
timings[:setup] = Time.now
|
|
120
|
+
|
|
121
|
+
engine.at_cycle_end(&Test.method(:check_event_assertions))
|
|
122
|
+
engine.finalizers << Test.method(:finalize_event_assertions)
|
|
96
123
|
end
|
|
97
124
|
|
|
125
|
+
|
|
98
126
|
def teardown_plan
|
|
127
|
+
engine.at_cycle_end_handlers.delete(Test.method(:check_event_assertions))
|
|
128
|
+
engine.finalizers.delete(Test.method(:finalize_event_assertions))
|
|
129
|
+
|
|
99
130
|
old_gc_roby_logger_level = Roby.logger.level
|
|
100
131
|
if debug_gc?
|
|
101
132
|
Roby.logger.level = Logger::DEBUG
|
|
102
133
|
end
|
|
103
134
|
|
|
104
|
-
if !
|
|
105
|
-
|
|
135
|
+
if !engine.running?
|
|
136
|
+
engine.run
|
|
106
137
|
end
|
|
107
138
|
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
engine.quit
|
|
140
|
+
engine.join
|
|
110
141
|
plan.clear
|
|
111
142
|
|
|
112
143
|
ensure
|
|
@@ -142,10 +173,9 @@ module Roby
|
|
|
142
173
|
end
|
|
143
174
|
|
|
144
175
|
Roby::TaskStructure::Hierarchy.interesting_events.clear
|
|
145
|
-
if defined? Roby::
|
|
146
|
-
Roby.
|
|
147
|
-
Roby.
|
|
148
|
-
Roby.control.abort_on_framework_exception = false
|
|
176
|
+
if defined? Roby::Application
|
|
177
|
+
Roby.app.abort_on_exception = false
|
|
178
|
+
Roby.app.abort_on_application_exception = false
|
|
149
179
|
end
|
|
150
180
|
|
|
151
181
|
if defined? Roby::Log
|
|
@@ -169,24 +199,28 @@ module Roby
|
|
|
169
199
|
end
|
|
170
200
|
end
|
|
171
201
|
|
|
202
|
+
super if defined? super
|
|
203
|
+
|
|
172
204
|
rescue Exception => e
|
|
173
205
|
STDERR.puts "failed teardown: #{e.full_message}"
|
|
174
206
|
|
|
175
207
|
ensure
|
|
176
|
-
while
|
|
177
|
-
|
|
178
|
-
|
|
208
|
+
while engine.running?
|
|
209
|
+
engine.quit
|
|
210
|
+
engine.join rescue nil
|
|
179
211
|
end
|
|
180
|
-
|
|
212
|
+
plan.clear
|
|
213
|
+
@plan = nil
|
|
181
214
|
|
|
182
215
|
Roby.logger.level = @original_roby_logger_level
|
|
183
216
|
self.console_logger = false
|
|
217
|
+
self.event_logger = false
|
|
184
218
|
end
|
|
185
219
|
|
|
186
220
|
# Process pending events
|
|
187
221
|
def process_events
|
|
188
|
-
Roby
|
|
189
|
-
|
|
222
|
+
Roby.synchronize do
|
|
223
|
+
engine.process_events
|
|
190
224
|
end
|
|
191
225
|
end
|
|
192
226
|
|
|
@@ -215,29 +249,29 @@ module Roby
|
|
|
215
249
|
#
|
|
216
250
|
def prepare_plan(options)
|
|
217
251
|
options = validate_options options,
|
|
218
|
-
:missions => 0, :discover => 0, :tasks => 0,
|
|
252
|
+
:missions => 0, :add => 0, :discover => 0, :tasks => 0,
|
|
219
253
|
:permanent => 0,
|
|
220
254
|
:model => Roby::Task, :plan => plan
|
|
221
255
|
|
|
222
|
-
missions, permanent,
|
|
256
|
+
missions, permanent, added, tasks = [], [], [], []
|
|
223
257
|
(1..options[:missions]).each do |i|
|
|
224
|
-
options[:plan].
|
|
258
|
+
options[:plan].add_mission(t = options[:model].new(:id => "mission-#{i}"))
|
|
225
259
|
missions << t
|
|
226
260
|
end
|
|
227
261
|
(1..options[:permanent]).each do |i|
|
|
228
|
-
options[:plan].
|
|
262
|
+
options[:plan].add_permanent(t = options[:model].new(:id => "perm-#{i}"))
|
|
229
263
|
permanent << t
|
|
230
264
|
end
|
|
231
|
-
(1..options[:discover]).each do |i|
|
|
232
|
-
options[:plan].
|
|
233
|
-
|
|
265
|
+
(1..(options[:discover] + options[:add])).each do |i|
|
|
266
|
+
options[:plan].add(t = options[:model].new(:id => "discover-#{i}"))
|
|
267
|
+
added << t
|
|
234
268
|
end
|
|
235
269
|
(1..options[:tasks]).each do |i|
|
|
236
270
|
tasks << options[:model].new(:id => "task-#{i}")
|
|
237
271
|
end
|
|
238
272
|
|
|
239
273
|
result = []
|
|
240
|
-
[missions, permanent,
|
|
274
|
+
[missions, permanent, added, tasks].each do |set|
|
|
241
275
|
unless set.empty?
|
|
242
276
|
set = *set
|
|
243
277
|
result << set
|
|
@@ -255,19 +289,24 @@ module Roby
|
|
|
255
289
|
start_r, start_w= IO.pipe
|
|
256
290
|
quit_r, quit_w = IO.pipe
|
|
257
291
|
remote_pid = fork do
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
292
|
+
begin
|
|
293
|
+
start_r.close
|
|
294
|
+
yield
|
|
295
|
+
rescue Exception => e
|
|
296
|
+
puts e.full_message
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
start_w.write('OK')
|
|
300
|
+
quit_r.read(2)
|
|
262
301
|
end
|
|
263
302
|
start_w.close
|
|
264
|
-
start_r.read(2)
|
|
303
|
+
result = start_r.read(2)
|
|
265
304
|
|
|
266
305
|
remote_processes << [remote_pid, quit_w]
|
|
267
306
|
remote_pid
|
|
268
307
|
|
|
269
308
|
ensure
|
|
270
|
-
start_r.close
|
|
309
|
+
# start_r.close
|
|
271
310
|
end
|
|
272
311
|
|
|
273
312
|
# Stop all the remote processes that have been started using #remote_process
|
|
@@ -295,7 +334,8 @@ module Roby
|
|
|
295
334
|
assert_nothing_raised do
|
|
296
335
|
begin
|
|
297
336
|
yield
|
|
298
|
-
rescue
|
|
337
|
+
rescue Exception => e
|
|
338
|
+
assert_kind_of(localized_error_type, e)
|
|
299
339
|
assert_respond_to(e, :error)
|
|
300
340
|
assert_kind_of(klass, e.error)
|
|
301
341
|
end
|
|
@@ -372,6 +412,21 @@ module Roby
|
|
|
372
412
|
end
|
|
373
413
|
end
|
|
374
414
|
|
|
415
|
+
attr_reader :event_logger
|
|
416
|
+
def event_logger=(value)
|
|
417
|
+
if value && !@event_logger
|
|
418
|
+
require 'roby/log/file'
|
|
419
|
+
logfile = @method_name + ".log"
|
|
420
|
+
logger = Roby::Log::FileLogger.new(logfile)
|
|
421
|
+
logger.stats_mode = false
|
|
422
|
+
Roby::Log.add_logger logger
|
|
423
|
+
@event_logger = logger
|
|
424
|
+
elsif !value && @event_logger
|
|
425
|
+
Roby::Log.remove_logger @event_logger
|
|
426
|
+
@event_logger = nil
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
375
430
|
def wait_thread_stopped(thread)
|
|
376
431
|
while !thread.stop?
|
|
377
432
|
sleep(0.1)
|
|
@@ -394,6 +449,190 @@ module Roby
|
|
|
394
449
|
|
|
395
450
|
result
|
|
396
451
|
end
|
|
452
|
+
|
|
453
|
+
@event_assertions = []
|
|
454
|
+
@waiting_threads = []
|
|
455
|
+
|
|
456
|
+
ASSERT_ANY_EVENTS_TLS = :assert_any_events
|
|
457
|
+
|
|
458
|
+
class << self
|
|
459
|
+
# A [thread, cv, positive, negative] list of event assertions
|
|
460
|
+
attr_reader :event_assertions
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# Tests for events in +positive+ and +negative+ and returns
|
|
464
|
+
# the set of failing events if the assertion has finished.
|
|
465
|
+
# If the set is empty, it means that the assertion finished
|
|
466
|
+
# successfully
|
|
467
|
+
def self.assert_any_event_result(positive, negative)
|
|
468
|
+
if positive_ev = positive.find { |ev| ev.happened? }
|
|
469
|
+
return false, "#{positive_ev} happened"
|
|
470
|
+
end
|
|
471
|
+
failure = negative.find_all { |ev| ev.happened? }
|
|
472
|
+
unless failure.empty?
|
|
473
|
+
return true, "#{failure} happened"
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
if positive.all? { |ev| ev.unreachable? }
|
|
477
|
+
positive.each do |ev|
|
|
478
|
+
Robot.info "#{ev} is unreachable because of the following emission:"
|
|
479
|
+
Roby.log_exception(ev.unreachability_reason, Robot.logger, :info)
|
|
480
|
+
end
|
|
481
|
+
return true, "all positive events are unreachable"
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
nil
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# This method is inserted in the control thread to implement
|
|
488
|
+
# Assertions#assert_events
|
|
489
|
+
def self.check_event_assertions
|
|
490
|
+
event_assertions.delete_if do |thread, cv, positive, negative|
|
|
491
|
+
error, result = Test.assert_any_event_result(positive, negative)
|
|
492
|
+
if !error.nil?
|
|
493
|
+
thread[ASSERT_ANY_EVENTS_TLS] = [error, result]
|
|
494
|
+
cv.broadcast
|
|
495
|
+
true
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def self.finalize_event_assertions
|
|
501
|
+
check_event_assertions
|
|
502
|
+
event_assertions.dup.each do |thread, *_|
|
|
503
|
+
thread.raise ControlQuitError
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
module Assertions
|
|
508
|
+
# Wait for any event in +positive+ to happen. If +negative+ is
|
|
509
|
+
# non-empty, any event happening in this set will make the
|
|
510
|
+
# assertion fail. If events in +positive+ are task events, the
|
|
511
|
+
# :stop events of the corresponding tasks are added to negative
|
|
512
|
+
# automatically.
|
|
513
|
+
#
|
|
514
|
+
# If a block is given, it is called from within the control thread
|
|
515
|
+
# after the checks are in place
|
|
516
|
+
#
|
|
517
|
+
# So, to check that a task fails, do
|
|
518
|
+
#
|
|
519
|
+
# assert_events(task.event(:fail)) do
|
|
520
|
+
# task.start!
|
|
521
|
+
# end
|
|
522
|
+
#
|
|
523
|
+
def assert_any_event(positive, negative = [], msg = nil, &block)
|
|
524
|
+
control_priority do
|
|
525
|
+
engine.waiting_threads << Thread.current
|
|
526
|
+
Roby.condition_variable(false) do |cv|
|
|
527
|
+
positive = Array[*positive].to_value_set
|
|
528
|
+
negative = Array[*negative].to_value_set
|
|
529
|
+
|
|
530
|
+
unreachability_reason = ValueSet.new
|
|
531
|
+
Roby.synchronize do
|
|
532
|
+
positive.each do |ev|
|
|
533
|
+
ev.if_unreachable(true) do |reason|
|
|
534
|
+
unreachability_reason << reason if reason
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
error, result = Test.assert_any_event_result(positive, negative)
|
|
539
|
+
if error.nil?
|
|
540
|
+
this_thread = Thread.current
|
|
541
|
+
|
|
542
|
+
Test.event_assertions << [this_thread, cv, positive, negative]
|
|
543
|
+
engine.once(&block) if block_given?
|
|
544
|
+
begin
|
|
545
|
+
cv.wait(Roby.global_lock)
|
|
546
|
+
ensure
|
|
547
|
+
Test.event_assertions.delete_if { |thread, _| thread == this_thread }
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
error, result = this_thread[ASSERT_ANY_EVENTS_TLS]
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
if error
|
|
554
|
+
if !unreachability_reason.empty?
|
|
555
|
+
msg = unreachability_reason.map do |reason|
|
|
556
|
+
if reason.respond_to?(:context)
|
|
557
|
+
context = (reason.context || []).map do |obj|
|
|
558
|
+
if obj.kind_of?(Exception)
|
|
559
|
+
obj.full_message
|
|
560
|
+
else
|
|
561
|
+
obj.to_s
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
reason.to_s + context.join("\n ")
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
msg.join("\n ")
|
|
568
|
+
|
|
569
|
+
flunk("#{msg} all positive events are unreachable for the following reason:\n #{msg}")
|
|
570
|
+
elsif msg
|
|
571
|
+
flunk("#{msg} failed: #{result}")
|
|
572
|
+
else
|
|
573
|
+
flunk(result)
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
ensure
|
|
580
|
+
engine.waiting_threads.delete(Thread.current)
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
# Starts +task+ and checks it succeeds
|
|
584
|
+
def assert_succeeds(task, *args)
|
|
585
|
+
control_priority do
|
|
586
|
+
if !task.kind_of?(Roby::Task)
|
|
587
|
+
engine.execute do
|
|
588
|
+
plan.add_mission(task = planner.send(task, *args))
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
assert_any_event([task.event(:success)], [], nil) do
|
|
593
|
+
plan.add_permanent(task)
|
|
594
|
+
task.start! if task.pending?
|
|
595
|
+
yield if block_given?
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def control_priority
|
|
601
|
+
if !engine.thread
|
|
602
|
+
return yield
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
old_priority = Thread.current.priority
|
|
606
|
+
Thread.current.priority = engine.thread.priority + 1
|
|
607
|
+
|
|
608
|
+
yield
|
|
609
|
+
ensure
|
|
610
|
+
Thread.current.priority = old_priority if old_priority
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
# This assertion fails if the relative error between +found+ and
|
|
614
|
+
# +expected+is more than +error+
|
|
615
|
+
def assert_relative_error(expected, found, error, msg = "")
|
|
616
|
+
if expected == 0
|
|
617
|
+
assert_in_delta(0, found, error, "comparing #{found} to #{expected} in #{msg}")
|
|
618
|
+
else
|
|
619
|
+
assert_in_delta(0, (found - expected) / expected, error, "comparing #{found} to #{expected} in #{msg}")
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
# This assertion fails if +found+ and +expected+ are more than +dl+
|
|
624
|
+
# meters apart in the x, y and z coordinates, or +dt+ radians apart
|
|
625
|
+
# in angles
|
|
626
|
+
def assert_same_position(expected, found, dl = 0.01, dt = 0.01, msg = "")
|
|
627
|
+
assert_relative_error(expected.x, found.x, dl, msg)
|
|
628
|
+
assert_relative_error(expected.y, found.y, dl, msg)
|
|
629
|
+
assert_relative_error(expected.z, found.z, dl, msg)
|
|
630
|
+
assert_relative_error(expected.yaw, found.yaw, dt, msg)
|
|
631
|
+
assert_relative_error(expected.pitch, found.pitch, dt, msg)
|
|
632
|
+
assert_relative_error(expected.roll, found.roll, dt, msg)
|
|
633
|
+
end
|
|
634
|
+
end
|
|
635
|
+
|
|
397
636
|
end
|
|
398
637
|
end
|
|
399
638
|
|