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.
Files changed (240) hide show
  1. data/.gitignore +29 -0
  2. data/History.txt +4 -0
  3. data/License-fr.txt +519 -0
  4. data/License.txt +515 -0
  5. data/Manifest.txt +245 -0
  6. data/NOTES +4 -0
  7. data/README.txt +163 -0
  8. data/Rakefile +161 -0
  9. data/TODO.txt +146 -0
  10. data/app/README.txt +24 -0
  11. data/app/Rakefile +8 -0
  12. data/app/config/ROBOT.rb +5 -0
  13. data/app/config/app.yml +91 -0
  14. data/app/config/init.rb +7 -0
  15. data/app/config/roby.yml +3 -0
  16. data/app/controllers/.gitattributes +0 -0
  17. data/app/controllers/ROBOT.rb +2 -0
  18. data/app/data/.gitattributes +0 -0
  19. data/app/planners/ROBOT/main.rb +6 -0
  20. data/app/planners/main.rb +5 -0
  21. data/app/scripts/distributed +3 -0
  22. data/app/scripts/generate/bookmarks +3 -0
  23. data/app/scripts/replay +3 -0
  24. data/app/scripts/results +3 -0
  25. data/app/scripts/run +3 -0
  26. data/app/scripts/server +3 -0
  27. data/app/scripts/shell +3 -0
  28. data/app/scripts/test +3 -0
  29. data/app/tasks/.gitattributes +0 -0
  30. data/app/tasks/ROBOT/.gitattributes +0 -0
  31. data/bin/roby +210 -0
  32. data/bin/roby-log +168 -0
  33. data/bin/roby-shell +25 -0
  34. data/doc/images/event_generalization.png +0 -0
  35. data/doc/images/exception_propagation_1.png +0 -0
  36. data/doc/images/exception_propagation_2.png +0 -0
  37. data/doc/images/exception_propagation_3.png +0 -0
  38. data/doc/images/exception_propagation_4.png +0 -0
  39. data/doc/images/exception_propagation_5.png +0 -0
  40. data/doc/images/replay_handler_error.png +0 -0
  41. data/doc/images/replay_handler_error_0.png +0 -0
  42. data/doc/images/replay_handler_error_1.png +0 -0
  43. data/doc/images/roby_cycle_overview.png +0 -0
  44. data/doc/images/roby_replay_02.png +0 -0
  45. data/doc/images/roby_replay_03.png +0 -0
  46. data/doc/images/roby_replay_04.png +0 -0
  47. data/doc/images/roby_replay_event_representation.png +0 -0
  48. data/doc/images/roby_replay_first_state.png +0 -0
  49. data/doc/images/roby_replay_relations.png +0 -0
  50. data/doc/images/roby_replay_startup.png +0 -0
  51. data/doc/images/task_event_generalization.png +0 -0
  52. data/doc/papers.rdoc +11 -0
  53. data/doc/styles/allison.css +314 -0
  54. data/doc/styles/allison.js +316 -0
  55. data/doc/styles/allison.rb +276 -0
  56. data/doc/styles/jamis.rb +593 -0
  57. data/doc/tutorials/01-GettingStarted.rdoc +86 -0
  58. data/doc/tutorials/02-GoForward.rdoc +220 -0
  59. data/doc/tutorials/03-PlannedPath.rdoc +268 -0
  60. data/doc/tutorials/04-EventPropagation.rdoc +236 -0
  61. data/doc/tutorials/05-ErrorHandling.rdoc +319 -0
  62. data/doc/tutorials/06-Overview.rdoc +40 -0
  63. data/doc/videos.rdoc +69 -0
  64. data/ext/droby/dump.cc +175 -0
  65. data/ext/droby/extconf.rb +3 -0
  66. data/ext/graph/algorithm.cc +746 -0
  67. data/ext/graph/extconf.rb +7 -0
  68. data/ext/graph/graph.cc +529 -0
  69. data/ext/graph/graph.hh +183 -0
  70. data/ext/graph/iterator_sequence.hh +102 -0
  71. data/ext/graph/undirected_dfs.hh +226 -0
  72. data/ext/graph/undirected_graph.hh +421 -0
  73. data/lib/roby.rb +41 -0
  74. data/lib/roby/app.rb +870 -0
  75. data/lib/roby/app/rake.rb +56 -0
  76. data/lib/roby/app/run.rb +14 -0
  77. data/lib/roby/app/scripts/distributed.rb +13 -0
  78. data/lib/roby/app/scripts/generate/bookmarks.rb +162 -0
  79. data/lib/roby/app/scripts/replay.rb +31 -0
  80. data/lib/roby/app/scripts/results.rb +15 -0
  81. data/lib/roby/app/scripts/run.rb +26 -0
  82. data/lib/roby/app/scripts/server.rb +18 -0
  83. data/lib/roby/app/scripts/shell.rb +88 -0
  84. data/lib/roby/app/scripts/test.rb +40 -0
  85. data/lib/roby/basic_object.rb +151 -0
  86. data/lib/roby/config.rb +5 -0
  87. data/lib/roby/control.rb +747 -0
  88. data/lib/roby/decision_control.rb +17 -0
  89. data/lib/roby/distributed.rb +32 -0
  90. data/lib/roby/distributed/base.rb +440 -0
  91. data/lib/roby/distributed/communication.rb +871 -0
  92. data/lib/roby/distributed/connection_space.rb +592 -0
  93. data/lib/roby/distributed/distributed_object.rb +206 -0
  94. data/lib/roby/distributed/drb.rb +62 -0
  95. data/lib/roby/distributed/notifications.rb +539 -0
  96. data/lib/roby/distributed/peer.rb +550 -0
  97. data/lib/roby/distributed/protocol.rb +529 -0
  98. data/lib/roby/distributed/proxy.rb +343 -0
  99. data/lib/roby/distributed/subscription.rb +311 -0
  100. data/lib/roby/distributed/transaction.rb +498 -0
  101. data/lib/roby/event.rb +897 -0
  102. data/lib/roby/exceptions.rb +234 -0
  103. data/lib/roby/executives/simple.rb +30 -0
  104. data/lib/roby/graph.rb +166 -0
  105. data/lib/roby/interface.rb +390 -0
  106. data/lib/roby/log.rb +3 -0
  107. data/lib/roby/log/chronicle.rb +303 -0
  108. data/lib/roby/log/console.rb +72 -0
  109. data/lib/roby/log/data_stream.rb +197 -0
  110. data/lib/roby/log/dot.rb +279 -0
  111. data/lib/roby/log/event_stream.rb +151 -0
  112. data/lib/roby/log/file.rb +340 -0
  113. data/lib/roby/log/gui/basic_display.ui +83 -0
  114. data/lib/roby/log/gui/chronicle.rb +26 -0
  115. data/lib/roby/log/gui/chronicle_view.rb +40 -0
  116. data/lib/roby/log/gui/chronicle_view.ui +70 -0
  117. data/lib/roby/log/gui/data_displays.rb +172 -0
  118. data/lib/roby/log/gui/data_displays.ui +155 -0
  119. data/lib/roby/log/gui/notifications.rb +26 -0
  120. data/lib/roby/log/gui/relations.rb +248 -0
  121. data/lib/roby/log/gui/relations.ui +123 -0
  122. data/lib/roby/log/gui/relations_view.rb +185 -0
  123. data/lib/roby/log/gui/relations_view.ui +149 -0
  124. data/lib/roby/log/gui/replay.rb +327 -0
  125. data/lib/roby/log/gui/replay_controls.rb +200 -0
  126. data/lib/roby/log/gui/replay_controls.ui +259 -0
  127. data/lib/roby/log/gui/runtime.rb +130 -0
  128. data/lib/roby/log/hooks.rb +185 -0
  129. data/lib/roby/log/logger.rb +202 -0
  130. data/lib/roby/log/notifications.rb +244 -0
  131. data/lib/roby/log/plan_rebuilder.rb +470 -0
  132. data/lib/roby/log/relations.rb +1056 -0
  133. data/lib/roby/log/server.rb +550 -0
  134. data/lib/roby/log/sqlite.rb +47 -0
  135. data/lib/roby/log/timings.rb +164 -0
  136. data/lib/roby/plan-object.rb +247 -0
  137. data/lib/roby/plan.rb +762 -0
  138. data/lib/roby/planning.rb +13 -0
  139. data/lib/roby/planning/loops.rb +302 -0
  140. data/lib/roby/planning/model.rb +906 -0
  141. data/lib/roby/planning/task.rb +151 -0
  142. data/lib/roby/propagation.rb +562 -0
  143. data/lib/roby/query.rb +619 -0
  144. data/lib/roby/relations.rb +583 -0
  145. data/lib/roby/relations/conflicts.rb +70 -0
  146. data/lib/roby/relations/ensured.rb +20 -0
  147. data/lib/roby/relations/error_handling.rb +23 -0
  148. data/lib/roby/relations/events.rb +9 -0
  149. data/lib/roby/relations/executed_by.rb +193 -0
  150. data/lib/roby/relations/hierarchy.rb +239 -0
  151. data/lib/roby/relations/influence.rb +10 -0
  152. data/lib/roby/relations/planned_by.rb +63 -0
  153. data/lib/roby/robot.rb +7 -0
  154. data/lib/roby/standard_errors.rb +218 -0
  155. data/lib/roby/state.rb +5 -0
  156. data/lib/roby/state/events.rb +221 -0
  157. data/lib/roby/state/information.rb +55 -0
  158. data/lib/roby/state/pos.rb +110 -0
  159. data/lib/roby/state/shapes.rb +32 -0
  160. data/lib/roby/state/state.rb +353 -0
  161. data/lib/roby/support.rb +92 -0
  162. data/lib/roby/task-operations.rb +182 -0
  163. data/lib/roby/task.rb +1618 -0
  164. data/lib/roby/test/common.rb +399 -0
  165. data/lib/roby/test/distributed.rb +214 -0
  166. data/lib/roby/test/tasks/empty_task.rb +9 -0
  167. data/lib/roby/test/tasks/goto.rb +36 -0
  168. data/lib/roby/test/tasks/simple_task.rb +23 -0
  169. data/lib/roby/test/testcase.rb +519 -0
  170. data/lib/roby/test/tools.rb +160 -0
  171. data/lib/roby/thread_task.rb +87 -0
  172. data/lib/roby/transactions.rb +462 -0
  173. data/lib/roby/transactions/proxy.rb +292 -0
  174. data/lib/roby/transactions/updates.rb +139 -0
  175. data/plugins/fault_injection/History.txt +4 -0
  176. data/plugins/fault_injection/README.txt +37 -0
  177. data/plugins/fault_injection/Rakefile +18 -0
  178. data/plugins/fault_injection/TODO.txt +0 -0
  179. data/plugins/fault_injection/app.rb +52 -0
  180. data/plugins/fault_injection/fault_injection.rb +89 -0
  181. data/plugins/fault_injection/test/test_fault_injection.rb +84 -0
  182. data/plugins/subsystems/README.txt +40 -0
  183. data/plugins/subsystems/Rakefile +18 -0
  184. data/plugins/subsystems/app.rb +171 -0
  185. data/plugins/subsystems/test/app/README +24 -0
  186. data/plugins/subsystems/test/app/Rakefile +8 -0
  187. data/plugins/subsystems/test/app/config/app.yml +71 -0
  188. data/plugins/subsystems/test/app/config/init.rb +9 -0
  189. data/plugins/subsystems/test/app/config/roby.yml +3 -0
  190. data/plugins/subsystems/test/app/planners/main.rb +20 -0
  191. data/plugins/subsystems/test/app/scripts/distributed +3 -0
  192. data/plugins/subsystems/test/app/scripts/replay +3 -0
  193. data/plugins/subsystems/test/app/scripts/results +3 -0
  194. data/plugins/subsystems/test/app/scripts/run +3 -0
  195. data/plugins/subsystems/test/app/scripts/server +3 -0
  196. data/plugins/subsystems/test/app/scripts/shell +3 -0
  197. data/plugins/subsystems/test/app/scripts/test +3 -0
  198. data/plugins/subsystems/test/app/tasks/services.rb +15 -0
  199. data/plugins/subsystems/test/test_subsystems.rb +71 -0
  200. data/test/distributed/test_communication.rb +178 -0
  201. data/test/distributed/test_connection.rb +282 -0
  202. data/test/distributed/test_execution.rb +373 -0
  203. data/test/distributed/test_mixed_plan.rb +341 -0
  204. data/test/distributed/test_plan_notifications.rb +238 -0
  205. data/test/distributed/test_protocol.rb +516 -0
  206. data/test/distributed/test_query.rb +102 -0
  207. data/test/distributed/test_remote_plan.rb +491 -0
  208. data/test/distributed/test_transaction.rb +463 -0
  209. data/test/mockups/tasks.rb +27 -0
  210. data/test/planning/test_loops.rb +380 -0
  211. data/test/planning/test_model.rb +427 -0
  212. data/test/planning/test_task.rb +106 -0
  213. data/test/relations/test_conflicts.rb +42 -0
  214. data/test/relations/test_ensured.rb +38 -0
  215. data/test/relations/test_executed_by.rb +149 -0
  216. data/test/relations/test_hierarchy.rb +158 -0
  217. data/test/relations/test_planned_by.rb +54 -0
  218. data/test/suite_core.rb +24 -0
  219. data/test/suite_distributed.rb +9 -0
  220. data/test/suite_planning.rb +3 -0
  221. data/test/suite_relations.rb +8 -0
  222. data/test/test_bgl.rb +508 -0
  223. data/test/test_control.rb +399 -0
  224. data/test/test_event.rb +894 -0
  225. data/test/test_exceptions.rb +592 -0
  226. data/test/test_interface.rb +37 -0
  227. data/test/test_log.rb +114 -0
  228. data/test/test_log_server.rb +132 -0
  229. data/test/test_plan.rb +584 -0
  230. data/test/test_propagation.rb +210 -0
  231. data/test/test_query.rb +266 -0
  232. data/test/test_relations.rb +180 -0
  233. data/test/test_state.rb +414 -0
  234. data/test/test_support.rb +16 -0
  235. data/test/test_task.rb +938 -0
  236. data/test/test_testcase.rb +122 -0
  237. data/test/test_thread_task.rb +73 -0
  238. data/test/test_transactions.rb +569 -0
  239. data/test/test_transactions_proxy.rb +198 -0
  240. metadata +570 -0
@@ -0,0 +1,10 @@
1
+ module Roby
2
+ module TaskStructure
3
+ relation :Influence, :child_name => 'influenced_task', :noinfo => true, :weak => true do
4
+ def influenced_by(task)
5
+ add_influenced_task(task)
6
+ end
7
+ end
8
+ end
9
+ end
10
+
@@ -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,7 @@
1
+ module Robot
2
+ class << self
3
+ attr_accessor :logger
4
+ end
5
+ extend Logger::Forward
6
+ end
7
+
@@ -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,5 @@
1
+ require 'roby/state/state'
2
+ require 'roby/state/events'
3
+ require 'roby/state/pos'
4
+ require 'roby/state/shapes'
5
+
@@ -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
+