roby 0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +29 -0
- data/History.txt +4 -0
- data/License-fr.txt +519 -0
- data/License.txt +515 -0
- data/Manifest.txt +245 -0
- data/NOTES +4 -0
- data/README.txt +163 -0
- data/Rakefile +161 -0
- data/TODO.txt +146 -0
- data/app/README.txt +24 -0
- data/app/Rakefile +8 -0
- data/app/config/ROBOT.rb +5 -0
- data/app/config/app.yml +91 -0
- data/app/config/init.rb +7 -0
- data/app/config/roby.yml +3 -0
- data/app/controllers/.gitattributes +0 -0
- data/app/controllers/ROBOT.rb +2 -0
- data/app/data/.gitattributes +0 -0
- data/app/planners/ROBOT/main.rb +6 -0
- data/app/planners/main.rb +5 -0
- data/app/scripts/distributed +3 -0
- data/app/scripts/generate/bookmarks +3 -0
- data/app/scripts/replay +3 -0
- data/app/scripts/results +3 -0
- data/app/scripts/run +3 -0
- data/app/scripts/server +3 -0
- data/app/scripts/shell +3 -0
- data/app/scripts/test +3 -0
- data/app/tasks/.gitattributes +0 -0
- data/app/tasks/ROBOT/.gitattributes +0 -0
- data/bin/roby +210 -0
- data/bin/roby-log +168 -0
- data/bin/roby-shell +25 -0
- data/doc/images/event_generalization.png +0 -0
- data/doc/images/exception_propagation_1.png +0 -0
- data/doc/images/exception_propagation_2.png +0 -0
- data/doc/images/exception_propagation_3.png +0 -0
- data/doc/images/exception_propagation_4.png +0 -0
- data/doc/images/exception_propagation_5.png +0 -0
- data/doc/images/replay_handler_error.png +0 -0
- data/doc/images/replay_handler_error_0.png +0 -0
- data/doc/images/replay_handler_error_1.png +0 -0
- data/doc/images/roby_cycle_overview.png +0 -0
- data/doc/images/roby_replay_02.png +0 -0
- data/doc/images/roby_replay_03.png +0 -0
- data/doc/images/roby_replay_04.png +0 -0
- data/doc/images/roby_replay_event_representation.png +0 -0
- data/doc/images/roby_replay_first_state.png +0 -0
- data/doc/images/roby_replay_relations.png +0 -0
- data/doc/images/roby_replay_startup.png +0 -0
- data/doc/images/task_event_generalization.png +0 -0
- data/doc/papers.rdoc +11 -0
- data/doc/styles/allison.css +314 -0
- data/doc/styles/allison.js +316 -0
- data/doc/styles/allison.rb +276 -0
- data/doc/styles/jamis.rb +593 -0
- data/doc/tutorials/01-GettingStarted.rdoc +86 -0
- data/doc/tutorials/02-GoForward.rdoc +220 -0
- data/doc/tutorials/03-PlannedPath.rdoc +268 -0
- data/doc/tutorials/04-EventPropagation.rdoc +236 -0
- data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
- data/doc/tutorials/06-Overview.rdoc +40 -0
- data/doc/videos.rdoc +69 -0
- data/ext/droby/dump.cc +175 -0
- data/ext/droby/extconf.rb +3 -0
- data/ext/graph/algorithm.cc +746 -0
- data/ext/graph/extconf.rb +7 -0
- data/ext/graph/graph.cc +529 -0
- data/ext/graph/graph.hh +183 -0
- data/ext/graph/iterator_sequence.hh +102 -0
- data/ext/graph/undirected_dfs.hh +226 -0
- data/ext/graph/undirected_graph.hh +421 -0
- data/lib/roby.rb +41 -0
- data/lib/roby/app.rb +870 -0
- data/lib/roby/app/rake.rb +56 -0
- data/lib/roby/app/run.rb +14 -0
- data/lib/roby/app/scripts/distributed.rb +13 -0
- data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
- data/lib/roby/app/scripts/replay.rb +31 -0
- data/lib/roby/app/scripts/results.rb +15 -0
- data/lib/roby/app/scripts/run.rb +26 -0
- data/lib/roby/app/scripts/server.rb +18 -0
- data/lib/roby/app/scripts/shell.rb +88 -0
- data/lib/roby/app/scripts/test.rb +40 -0
- data/lib/roby/basic_object.rb +151 -0
- data/lib/roby/config.rb +5 -0
- data/lib/roby/control.rb +747 -0
- data/lib/roby/decision_control.rb +17 -0
- data/lib/roby/distributed.rb +32 -0
- data/lib/roby/distributed/base.rb +440 -0
- data/lib/roby/distributed/communication.rb +871 -0
- data/lib/roby/distributed/connection_space.rb +592 -0
- data/lib/roby/distributed/distributed_object.rb +206 -0
- data/lib/roby/distributed/drb.rb +62 -0
- data/lib/roby/distributed/notifications.rb +539 -0
- data/lib/roby/distributed/peer.rb +550 -0
- data/lib/roby/distributed/protocol.rb +529 -0
- data/lib/roby/distributed/proxy.rb +343 -0
- data/lib/roby/distributed/subscription.rb +311 -0
- data/lib/roby/distributed/transaction.rb +498 -0
- data/lib/roby/event.rb +897 -0
- data/lib/roby/exceptions.rb +234 -0
- data/lib/roby/executives/simple.rb +30 -0
- data/lib/roby/graph.rb +166 -0
- data/lib/roby/interface.rb +390 -0
- data/lib/roby/log.rb +3 -0
- data/lib/roby/log/chronicle.rb +303 -0
- data/lib/roby/log/console.rb +72 -0
- data/lib/roby/log/data_stream.rb +197 -0
- data/lib/roby/log/dot.rb +279 -0
- data/lib/roby/log/event_stream.rb +151 -0
- data/lib/roby/log/file.rb +340 -0
- data/lib/roby/log/gui/basic_display.ui +83 -0
- data/lib/roby/log/gui/chronicle.rb +26 -0
- data/lib/roby/log/gui/chronicle_view.rb +40 -0
- data/lib/roby/log/gui/chronicle_view.ui +70 -0
- data/lib/roby/log/gui/data_displays.rb +172 -0
- data/lib/roby/log/gui/data_displays.ui +155 -0
- data/lib/roby/log/gui/notifications.rb +26 -0
- data/lib/roby/log/gui/relations.rb +248 -0
- data/lib/roby/log/gui/relations.ui +123 -0
- data/lib/roby/log/gui/relations_view.rb +185 -0
- data/lib/roby/log/gui/relations_view.ui +149 -0
- data/lib/roby/log/gui/replay.rb +327 -0
- data/lib/roby/log/gui/replay_controls.rb +200 -0
- data/lib/roby/log/gui/replay_controls.ui +259 -0
- data/lib/roby/log/gui/runtime.rb +130 -0
- data/lib/roby/log/hooks.rb +185 -0
- data/lib/roby/log/logger.rb +202 -0
- data/lib/roby/log/notifications.rb +244 -0
- data/lib/roby/log/plan_rebuilder.rb +470 -0
- data/lib/roby/log/relations.rb +1056 -0
- data/lib/roby/log/server.rb +550 -0
- data/lib/roby/log/sqlite.rb +47 -0
- data/lib/roby/log/timings.rb +164 -0
- data/lib/roby/plan-object.rb +247 -0
- data/lib/roby/plan.rb +762 -0
- data/lib/roby/planning.rb +13 -0
- data/lib/roby/planning/loops.rb +302 -0
- data/lib/roby/planning/model.rb +906 -0
- data/lib/roby/planning/task.rb +151 -0
- data/lib/roby/propagation.rb +562 -0
- data/lib/roby/query.rb +619 -0
- data/lib/roby/relations.rb +583 -0
- data/lib/roby/relations/conflicts.rb +70 -0
- data/lib/roby/relations/ensured.rb +20 -0
- data/lib/roby/relations/error_handling.rb +23 -0
- data/lib/roby/relations/events.rb +9 -0
- data/lib/roby/relations/executed_by.rb +193 -0
- data/lib/roby/relations/hierarchy.rb +239 -0
- data/lib/roby/relations/influence.rb +10 -0
- data/lib/roby/relations/planned_by.rb +63 -0
- data/lib/roby/robot.rb +7 -0
- data/lib/roby/standard_errors.rb +218 -0
- data/lib/roby/state.rb +5 -0
- data/lib/roby/state/events.rb +221 -0
- data/lib/roby/state/information.rb +55 -0
- data/lib/roby/state/pos.rb +110 -0
- data/lib/roby/state/shapes.rb +32 -0
- data/lib/roby/state/state.rb +353 -0
- data/lib/roby/support.rb +92 -0
- data/lib/roby/task-operations.rb +182 -0
- data/lib/roby/task.rb +1618 -0
- data/lib/roby/test/common.rb +399 -0
- data/lib/roby/test/distributed.rb +214 -0
- data/lib/roby/test/tasks/empty_task.rb +9 -0
- data/lib/roby/test/tasks/goto.rb +36 -0
- data/lib/roby/test/tasks/simple_task.rb +23 -0
- data/lib/roby/test/testcase.rb +519 -0
- data/lib/roby/test/tools.rb +160 -0
- data/lib/roby/thread_task.rb +87 -0
- data/lib/roby/transactions.rb +462 -0
- data/lib/roby/transactions/proxy.rb +292 -0
- data/lib/roby/transactions/updates.rb +139 -0
- data/plugins/fault_injection/History.txt +4 -0
- data/plugins/fault_injection/README.txt +37 -0
- data/plugins/fault_injection/Rakefile +18 -0
- data/plugins/fault_injection/TODO.txt +0 -0
- data/plugins/fault_injection/app.rb +52 -0
- data/plugins/fault_injection/fault_injection.rb +89 -0
- data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
- data/plugins/subsystems/README.txt +40 -0
- data/plugins/subsystems/Rakefile +18 -0
- data/plugins/subsystems/app.rb +171 -0
- data/plugins/subsystems/test/app/README +24 -0
- data/plugins/subsystems/test/app/Rakefile +8 -0
- data/plugins/subsystems/test/app/config/app.yml +71 -0
- data/plugins/subsystems/test/app/config/init.rb +9 -0
- data/plugins/subsystems/test/app/config/roby.yml +3 -0
- data/plugins/subsystems/test/app/planners/main.rb +20 -0
- data/plugins/subsystems/test/app/scripts/distributed +3 -0
- data/plugins/subsystems/test/app/scripts/replay +3 -0
- data/plugins/subsystems/test/app/scripts/results +3 -0
- data/plugins/subsystems/test/app/scripts/run +3 -0
- data/plugins/subsystems/test/app/scripts/server +3 -0
- data/plugins/subsystems/test/app/scripts/shell +3 -0
- data/plugins/subsystems/test/app/scripts/test +3 -0
- data/plugins/subsystems/test/app/tasks/services.rb +15 -0
- data/plugins/subsystems/test/test_subsystems.rb +71 -0
- data/test/distributed/test_communication.rb +178 -0
- data/test/distributed/test_connection.rb +282 -0
- data/test/distributed/test_execution.rb +373 -0
- data/test/distributed/test_mixed_plan.rb +341 -0
- data/test/distributed/test_plan_notifications.rb +238 -0
- data/test/distributed/test_protocol.rb +516 -0
- data/test/distributed/test_query.rb +102 -0
- data/test/distributed/test_remote_plan.rb +491 -0
- data/test/distributed/test_transaction.rb +463 -0
- data/test/mockups/tasks.rb +27 -0
- data/test/planning/test_loops.rb +380 -0
- data/test/planning/test_model.rb +427 -0
- data/test/planning/test_task.rb +106 -0
- data/test/relations/test_conflicts.rb +42 -0
- data/test/relations/test_ensured.rb +38 -0
- data/test/relations/test_executed_by.rb +149 -0
- data/test/relations/test_hierarchy.rb +158 -0
- data/test/relations/test_planned_by.rb +54 -0
- data/test/suite_core.rb +24 -0
- data/test/suite_distributed.rb +9 -0
- data/test/suite_planning.rb +3 -0
- data/test/suite_relations.rb +8 -0
- data/test/test_bgl.rb +508 -0
- data/test/test_control.rb +399 -0
- data/test/test_event.rb +894 -0
- data/test/test_exceptions.rb +592 -0
- data/test/test_interface.rb +37 -0
- data/test/test_log.rb +114 -0
- data/test/test_log_server.rb +132 -0
- data/test/test_plan.rb +584 -0
- data/test/test_propagation.rb +210 -0
- data/test/test_query.rb +266 -0
- data/test/test_relations.rb +180 -0
- data/test/test_state.rb +414 -0
- data/test/test_support.rb +16 -0
- data/test/test_task.rb +938 -0
- data/test/test_testcase.rb +122 -0
- data/test/test_thread_task.rb +73 -0
- data/test/test_transactions.rb +569 -0
- data/test/test_transactions_proxy.rb +198 -0
- metadata +570 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'roby/task'
|
|
2
|
+
|
|
3
|
+
module Roby::TaskStructure
|
|
4
|
+
relation :PlannedBy, :child_name => :planning_task,
|
|
5
|
+
:parent_name => :planned_task, :noinfo => true, :single_child => true do
|
|
6
|
+
|
|
7
|
+
# The set of tasks which are planned by this one
|
|
8
|
+
def planned_tasks; parent_objects(PlannedBy) end
|
|
9
|
+
# Set +task+ as the planning task of +self+
|
|
10
|
+
def planned_by(task, options = {})
|
|
11
|
+
if old = planning_task
|
|
12
|
+
if options[:replace]
|
|
13
|
+
remove_planning_task(old)
|
|
14
|
+
else
|
|
15
|
+
raise ArgumentError, "this task already has a planner"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
add_planning_task(task)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns a set of PlanningFailedError exceptions for all abstract tasks
|
|
23
|
+
# for which planning has failed
|
|
24
|
+
def PlannedBy.check_planning(plan)
|
|
25
|
+
result = []
|
|
26
|
+
Roby::TaskStructure::PlannedBy.each_edge do |planned_task, planning_task, _|
|
|
27
|
+
next unless plan == planning_task.plan && planning_task.failed?
|
|
28
|
+
next unless planned_task.pending? && !planned_task.executable? && planned_task.self_owned?
|
|
29
|
+
result << Roby::PlanningFailedError.new(planned_task, planning_task)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
result
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module Roby
|
|
37
|
+
# This exception is raised when a task is abstract, and its planner failed:
|
|
38
|
+
# the system will therefore not have a suitable executable development for
|
|
39
|
+
# this task, and this is a failure
|
|
40
|
+
class PlanningFailedError < LocalizedError
|
|
41
|
+
# The planning task
|
|
42
|
+
attr_reader :planned_task
|
|
43
|
+
|
|
44
|
+
def initialize(planned_task, planning_task)
|
|
45
|
+
@planned_task = planned_task
|
|
46
|
+
super(planning_task.terminal_event)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def message # :nodoc:
|
|
50
|
+
msg = "failed to plan #{planned_task}.planned_by(#{failed_task}): failed with #{failure_point.symbol}"
|
|
51
|
+
if failure_point.context
|
|
52
|
+
if failure_point.context.first.respond_to?(:full_message)
|
|
53
|
+
msg << "\n" << failure_point.context.first.full_message
|
|
54
|
+
else
|
|
55
|
+
msg << "(" << failure_point.context.first.to_s << ")"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
msg
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
Control.structure_checks << TaskStructure::PlannedBy.method(:check_planning)
|
|
62
|
+
end
|
|
63
|
+
|
data/lib/roby/robot.rb
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
require 'pp'
|
|
2
|
+
module Roby
|
|
3
|
+
# This kind of errors are generated during the plan execution, allowing to
|
|
4
|
+
# blame a fault on a plan object (#failure_point). The precise failure
|
|
5
|
+
# point is categorized in the #failed_event, #failed_generator and
|
|
6
|
+
# #failed_task. It is guaranteed that one of #failed_generator and
|
|
7
|
+
# #failed_task is non-nil.
|
|
8
|
+
class LocalizedError < RuntimeError
|
|
9
|
+
# The object describing the point of failure
|
|
10
|
+
attr_reader :failure_point
|
|
11
|
+
|
|
12
|
+
# The objects of the given categories which are related to #failure_point
|
|
13
|
+
attr_reader :failed_event, :failed_generator, :failed_task
|
|
14
|
+
|
|
15
|
+
# Create a LocalizedError object with the given failure point
|
|
16
|
+
def initialize(failure_point)
|
|
17
|
+
@failure_point = failure_point
|
|
18
|
+
if failure_point.kind_of?(Event)
|
|
19
|
+
@failed_event = failure_point
|
|
20
|
+
@failed_generator = failure_point.generator
|
|
21
|
+
elsif failure_point.kind_of?(EventGenerator)
|
|
22
|
+
@failed_generator = failure_point
|
|
23
|
+
elsif failure_point.kind_of?(Task)
|
|
24
|
+
@failed_task = failure_point
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if !@failed_task && @failed_generator && @failed_generator.respond_to?(:task)
|
|
28
|
+
@failed_task = failed_generator.task
|
|
29
|
+
end
|
|
30
|
+
if !@failed_task && !@failed_generator
|
|
31
|
+
raise ArgumentError, "cannot deduce a task and/or a generator from #{failure_point}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
super("")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def pretty_print(pp)
|
|
38
|
+
pp.text "#{self.class.name}"
|
|
39
|
+
if !message.empty?
|
|
40
|
+
pp.text ": #{message}"
|
|
41
|
+
end
|
|
42
|
+
pp.breakable
|
|
43
|
+
failure_point.pretty_print(pp)
|
|
44
|
+
|
|
45
|
+
if backtrace && !backtrace.empty?
|
|
46
|
+
Roby.pretty_print_backtrace(pp, backtrace)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# True if +obj+ is involved in this error
|
|
51
|
+
def involved_plan_object?(obj)
|
|
52
|
+
obj.kind_of?(PlanObject) &&
|
|
53
|
+
(obj == failed_event ||
|
|
54
|
+
obj == failed_generator ||
|
|
55
|
+
obj == failed_task)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Raised during event propagation if a task event is called or emitted,
|
|
60
|
+
# while this task is not executable.
|
|
61
|
+
class TaskNotExecutable < LocalizedError; end
|
|
62
|
+
# Raised during event propagation if an event is called or emitted,
|
|
63
|
+
# while this event is not executable.
|
|
64
|
+
class EventNotExecutable < LocalizedError; end
|
|
65
|
+
# Raised during event propagation if an event is called, while this event
|
|
66
|
+
# is not controlable.
|
|
67
|
+
class EventNotControlable < LocalizedError; end
|
|
68
|
+
|
|
69
|
+
# Raised when an operation is attempted while the ownership does not allow
|
|
70
|
+
# it.
|
|
71
|
+
class OwnershipError < RuntimeError; end
|
|
72
|
+
class RemotePeerMismatch < RuntimeError; end
|
|
73
|
+
|
|
74
|
+
# Raised when a consistency check failed in the Roby internal code
|
|
75
|
+
class InternalError < RuntimeError; end
|
|
76
|
+
# Raised when a consistency check failed in the Roby propagation code
|
|
77
|
+
class PropagationError < InternalError; end
|
|
78
|
+
|
|
79
|
+
# Some operations need to be performed in the control thread, and some
|
|
80
|
+
# other (namely blocking operations) must not. This exception is raised
|
|
81
|
+
# when this constraint is not met.
|
|
82
|
+
class ThreadMismatch < RuntimeError; end
|
|
83
|
+
|
|
84
|
+
# Raised when a user-provided code block (i.e. a code block which is
|
|
85
|
+
# outside of Roby's plan management algorithms) has raised. This includes:
|
|
86
|
+
# event commands, event handlers, task polling blocks, ...
|
|
87
|
+
class CodeError < LocalizedError
|
|
88
|
+
# The original exception object
|
|
89
|
+
attr_reader :error
|
|
90
|
+
# Create a CodeError object from the given original exception object, and
|
|
91
|
+
# with the given failure point
|
|
92
|
+
def initialize(error, *args)
|
|
93
|
+
if error && !error.kind_of?(Exception)
|
|
94
|
+
raise TypeError, "#{error} should be an exception"
|
|
95
|
+
end
|
|
96
|
+
super(*args)
|
|
97
|
+
@error = error
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def pretty_print(pp) # :nodoc:
|
|
101
|
+
if error
|
|
102
|
+
pp.text "#{self.class.name}: user code raised an exception "
|
|
103
|
+
failure_point.pretty_print(pp)
|
|
104
|
+
pp.breakable
|
|
105
|
+
pp.breakable
|
|
106
|
+
error.pretty_print(pp)
|
|
107
|
+
else
|
|
108
|
+
super
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Raised if a command block has raised an exception
|
|
114
|
+
class CommandFailed < CodeError; end
|
|
115
|
+
# Raised when the call of an event has been canceled.
|
|
116
|
+
# See EventGenerator#cancel.
|
|
117
|
+
class EventCanceled < LocalizedError; end
|
|
118
|
+
# Raised when an event is called, but one of
|
|
119
|
+
# its precondition is not met. See EventGenerator#precondition
|
|
120
|
+
class EventPreconditionFailed < LocalizedError; end
|
|
121
|
+
# Raised when the emission of an event has failed.
|
|
122
|
+
# See EventGenerator#emit_failed.
|
|
123
|
+
class EmissionFailed < CodeError; end
|
|
124
|
+
# Raised when an event handler has raised.
|
|
125
|
+
class EventHandlerError < CodeError; end
|
|
126
|
+
|
|
127
|
+
# Raised when an exception handler has raised.
|
|
128
|
+
class FailedExceptionHandler < CodeError
|
|
129
|
+
attr_reader :handled_exception
|
|
130
|
+
def initialize(error, object, handled_exception)
|
|
131
|
+
super(error, object)
|
|
132
|
+
@handled_exception = handled_exception
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Raised when an event has become unreachable while other parts of the plan
|
|
137
|
+
# where waiting for its emission.
|
|
138
|
+
class UnreachableEvent < LocalizedError
|
|
139
|
+
# The generator which has become unreachable
|
|
140
|
+
attr_reader :generator
|
|
141
|
+
# Create an UnreachableEvent error for the given +generator+. +reason+
|
|
142
|
+
# is supposed to be either nil or a plan object which is the reason why
|
|
143
|
+
# +generator+ has become unreachable.
|
|
144
|
+
def initialize(generator, reason)
|
|
145
|
+
@generator = generator
|
|
146
|
+
super(reason || generator)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def pretty_print(pp) # :nodoc:
|
|
150
|
+
pp.text "#{generator} has become unreachable"
|
|
151
|
+
if failure_point
|
|
152
|
+
pp.breakable ':'
|
|
153
|
+
failure_point.pretty_print(pp)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Exception raised when the event loop aborts because of an unhandled
|
|
159
|
+
# exception
|
|
160
|
+
class Aborting < RuntimeError
|
|
161
|
+
attr_reader :all_exceptions
|
|
162
|
+
def initialize(exceptions)
|
|
163
|
+
@all_exceptions = exceptions
|
|
164
|
+
super("")
|
|
165
|
+
end
|
|
166
|
+
def pretty_print(pp) # :nodoc:
|
|
167
|
+
pp.text "control loop aborting because of unhandled exceptions"
|
|
168
|
+
pp.seplist(",") do
|
|
169
|
+
all_exceptions.pretty_print(pp)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
def backtrace # :nodoc:
|
|
173
|
+
[]
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Raised by Plan#replace when the new task cannot replace the older one.
|
|
178
|
+
class InvalidReplace < RuntimeError
|
|
179
|
+
# The task being replaced
|
|
180
|
+
attr_reader :from
|
|
181
|
+
# The task which should have replaced #from
|
|
182
|
+
attr_reader :to
|
|
183
|
+
# A description of the replacement failure
|
|
184
|
+
attr_reader :error
|
|
185
|
+
|
|
186
|
+
# Create a new InvalidReplace object
|
|
187
|
+
def initialize(from, to, error)
|
|
188
|
+
@from, @to, @error = from, to, error
|
|
189
|
+
end
|
|
190
|
+
def pretty_print(pp) # :nodoc:
|
|
191
|
+
pp.text "invalid replacement: #{message}"
|
|
192
|
+
pp.breakable
|
|
193
|
+
pp.text "from "
|
|
194
|
+
from.pretty_print(pp)
|
|
195
|
+
pp.breakable
|
|
196
|
+
pp.text "to "
|
|
197
|
+
to.pretty_print(pp)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Exception raised when a mission has failed
|
|
202
|
+
class MissionFailedError < LocalizedError
|
|
203
|
+
# Create a new MissionFailedError for the given mission
|
|
204
|
+
def initialize(task)
|
|
205
|
+
super(task.terminal_event)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def pretty_print(pp)
|
|
209
|
+
pp.text "mission failed: "
|
|
210
|
+
super
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Exception raised in threads which are waiting for the control thread
|
|
215
|
+
# See for instance Roby.execute
|
|
216
|
+
class ControlQuitError < RuntimeError; end
|
|
217
|
+
end
|
|
218
|
+
|
data/lib/roby/state.rb
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
|
|
2
|
+
module Roby
|
|
3
|
+
class StateSpace
|
|
4
|
+
# Create an event which will be emitted everytime some state parameters
|
|
5
|
+
# vary more than the given deltas. The following state parameters are
|
|
6
|
+
# available:
|
|
7
|
+
# +t+:: time in seconds
|
|
8
|
+
# +d+:: distance in meters
|
|
9
|
+
# +yaw+:: heading in radians
|
|
10
|
+
#
|
|
11
|
+
# For instance:
|
|
12
|
+
# Roby.state.on_delta :d => 10, :t => 20
|
|
13
|
+
#
|
|
14
|
+
# will emit everytime the robot moves more than 10 meters AND more than
|
|
15
|
+
# 20 seconds have elapsed.
|
|
16
|
+
#
|
|
17
|
+
# If more than once specification is given, the resulting event is
|
|
18
|
+
# combined with the & operator. This can be changed by setting the :or
|
|
19
|
+
# option to 'true'.
|
|
20
|
+
#
|
|
21
|
+
# Roby.state.on_delta :d => 10, :t => 20, :or => true
|
|
22
|
+
#
|
|
23
|
+
# See DeltaEvent and its subclasses.
|
|
24
|
+
def on_delta(spec)
|
|
25
|
+
or_aggregate = spec.delete(:or)
|
|
26
|
+
|
|
27
|
+
events = spec.map do |name, value|
|
|
28
|
+
unless klass = DeltaEvent.event_types[name]
|
|
29
|
+
raise "unknown delta type #{name}. Known types are #{DeltaEvent.event_types.keys}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ev = klass.new
|
|
33
|
+
ev.threshold = value
|
|
34
|
+
ev
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if events.size > 1
|
|
38
|
+
result = if or_aggregate then OrGenerator.new
|
|
39
|
+
else AndGenerator.new
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
result.on { result.reset }
|
|
43
|
+
def result.or(spec); DeltaEvent.or(spec, self) end
|
|
44
|
+
events.each { |ev| result << ev }
|
|
45
|
+
result
|
|
46
|
+
else
|
|
47
|
+
events.first
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns an event which emits when the given state is reached.
|
|
52
|
+
# For now, the following state variables are available:
|
|
53
|
+
# +t+:: time as a Time object
|
|
54
|
+
#
|
|
55
|
+
# See TimePointEvent
|
|
56
|
+
def at(options)
|
|
57
|
+
options = validate_options options, :t => nil
|
|
58
|
+
if time = options[:t]
|
|
59
|
+
TimePointEvent.new(time)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Registered on Control to call the #poll method of state events
|
|
65
|
+
def self.poll_state_events # :nodoc:
|
|
66
|
+
for ev in Roby.plan.free_events
|
|
67
|
+
if ev.kind_of?(StateEvent) && ev.enabled?
|
|
68
|
+
ev.poll
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
Roby::Control.each_cycle(&Roby.method(:poll_state_events))
|
|
73
|
+
|
|
74
|
+
# A state event is an event which emits when some parameters over the state
|
|
75
|
+
# are reached. See DeltaEvent and TimePointEvent.
|
|
76
|
+
class StateEvent < EventGenerator
|
|
77
|
+
# True if this event is currently active
|
|
78
|
+
def enabled?; !@disabled end
|
|
79
|
+
# True if this event is currently disabled
|
|
80
|
+
def disabled?; @disabled end
|
|
81
|
+
# Call to reenable this event. If +reset+ is true, the event is reset
|
|
82
|
+
# at the same time.
|
|
83
|
+
def enable(reset = true)
|
|
84
|
+
@disabled = false
|
|
85
|
+
self.reset if reset
|
|
86
|
+
end
|
|
87
|
+
# Call to disable this event. When the state events are disabled, they
|
|
88
|
+
# will no more emit.
|
|
89
|
+
def disable; @disabled = true end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# This event emits itself when the specified time is reached
|
|
93
|
+
class TimePointEvent < StateEvent
|
|
94
|
+
# Time at which this event should emit himself
|
|
95
|
+
attr_reader :time
|
|
96
|
+
|
|
97
|
+
# Creates an event which will emit when +time+ is reached
|
|
98
|
+
def initialize(time)
|
|
99
|
+
@time = time
|
|
100
|
+
super
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Called at each cycle by Roby.poll_state_events
|
|
104
|
+
def poll # :nodoc:
|
|
105
|
+
if !happened? && Time.now >= time
|
|
106
|
+
emit
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Generic implementation of events which emit when a given delta is reached
|
|
112
|
+
# in the state. Subclasses must implement the following methods:
|
|
113
|
+
#
|
|
114
|
+
# [<tt>#has_sample</tt>]
|
|
115
|
+
# must return true if the state variable can be read
|
|
116
|
+
# [<tt>#delta</tt>]
|
|
117
|
+
# must return the delta between the current value and the
|
|
118
|
+
# value at the last emission (#last_value). The returned value
|
|
119
|
+
# must be comparable with #threshold.
|
|
120
|
+
# [<tt>#read</tt>]
|
|
121
|
+
# must return the current value.
|
|
122
|
+
class DeltaEvent < StateEvent
|
|
123
|
+
@@event_types = Hash.new
|
|
124
|
+
# The set of event types which
|
|
125
|
+
def self.event_types; @@event_types end
|
|
126
|
+
# Declare that the currently defined delta event has to be registered
|
|
127
|
+
# as a +name+ option for StateSpace#on_delta. For instance, the TimeDeltaEvent
|
|
128
|
+
# is registered by using
|
|
129
|
+
#
|
|
130
|
+
# class TimeDeltaEvent < DeltaEvent
|
|
131
|
+
# register_as :t
|
|
132
|
+
# end
|
|
133
|
+
#
|
|
134
|
+
# which allows to use it with
|
|
135
|
+
#
|
|
136
|
+
# Roby.state.on_delta :t => 10
|
|
137
|
+
def self.register_as(name)
|
|
138
|
+
event_types[name] = self
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# The last value for the considered state, the last time this event has
|
|
142
|
+
# been emitted
|
|
143
|
+
attr_reader :last_value
|
|
144
|
+
# A value expressing the delta in state for which the event should be
|
|
145
|
+
# emitted.
|
|
146
|
+
attr_accessor :threshold
|
|
147
|
+
|
|
148
|
+
# Reset +last_value+ to the current value of the state variable,
|
|
149
|
+
# making the event emit at current_value + threshold
|
|
150
|
+
def reset
|
|
151
|
+
@last_value = read
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.or(spec, base_event)
|
|
155
|
+
new = State.on_delta(spec)
|
|
156
|
+
result = OrGenerator.new
|
|
157
|
+
result << base_event
|
|
158
|
+
result << new
|
|
159
|
+
result.on { result.reset }
|
|
160
|
+
def result.or(spec); DeltaEvent.or(spec, self) end
|
|
161
|
+
result
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def or(spec)
|
|
165
|
+
DeltaEvent.or(spec, self)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Called at each cycle by Roby.poll_state_events
|
|
169
|
+
def poll # :nodoc:
|
|
170
|
+
if !has_sample?
|
|
171
|
+
return
|
|
172
|
+
elsif !last_value
|
|
173
|
+
@last_value = read
|
|
174
|
+
else
|
|
175
|
+
if delta.abs >= threshold
|
|
176
|
+
reset
|
|
177
|
+
emit(last_value)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# An event which emits at a given period (delta in time)
|
|
184
|
+
class TimeDeltaEvent < DeltaEvent
|
|
185
|
+
register_as :t
|
|
186
|
+
# Always true, as we can always measure time
|
|
187
|
+
def has_sample?; true end
|
|
188
|
+
# Returns how much time elapsed since the last emission
|
|
189
|
+
def delta; Time.now - last_value end
|
|
190
|
+
# Returns the current time
|
|
191
|
+
def read; Time.now end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# An event which emits everytime the robot heading moves more than a given
|
|
195
|
+
# angle (in radians)
|
|
196
|
+
class YawDeltaEvent < DeltaEvent
|
|
197
|
+
register_as :yaw
|
|
198
|
+
# True if State.pos is set
|
|
199
|
+
def has_sample?; State.pos? end
|
|
200
|
+
# Returns the variation in heading since the last emission (in radians)
|
|
201
|
+
def delta; State.pos.yaw - last_value end
|
|
202
|
+
# Returns the current heading position (in radians)
|
|
203
|
+
def read; State.pos.yaw end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# An event which emits everytime the robot moves more than a given
|
|
207
|
+
# distance.
|
|
208
|
+
class PosDeltaEvent < DeltaEvent
|
|
209
|
+
register_as :d
|
|
210
|
+
# True if State.pos is set
|
|
211
|
+
def has_sample?; State.pos? end
|
|
212
|
+
# Returns the distance this the position at the last emission
|
|
213
|
+
def delta; State.pos.distance(last_value) end
|
|
214
|
+
# Returns the current position
|
|
215
|
+
def read; State.pos.dup end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|