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,236 @@
1
+ {Previous tutorial}[link:files/doc/tutorials/03-PlannedPath_rdoc.html]
2
+ {Next tutorial}[link:files/doc/tutorials/05-ErrorHandling_rdoc.html]
3
+ = Understanding event propagation
4
+
5
+ This tutorial will show you how to trace plan execution, and understand how the
6
+ plans you build are actually executed by the system. It will not go in great
7
+ details, but it should help you understand more advanced uses of Roby. For that,
8
+ we will base ourselves on the execution trace of the controller we built in the
9
+ previous tutorial.
10
+
11
+ Roby plan execution is based on a fixed-duration execution cycle which includes
12
+ three steps:
13
+
14
+ link:../../images/roby_cycle_overview.png
15
+
16
+ This tutorial focusses on the first process. It will show how to use the log and
17
+ display tools that are shipped with Roby, and how event propagation works. The
18
+ last section will also give you a glimpse of the purpose of the garbage
19
+ collection algorithm.
20
+
21
+
22
+ == Getting a log file
23
+
24
+ We will first get a log file of the plan execution of the PathPlan robot
25
+ (previous tutorial). Plan execution logs are expensive from a CPU point of
26
+ view, so they are disabled by default. Enable them back by editing
27
+ <tt>config/app.yml</tt> and uncomment <tt>events: true</tt> around line 23.
28
+
29
+ Now, run again the controller
30
+
31
+ # scripts/run PathPlan
32
+
33
+ and in the shell, do
34
+
35
+ >> move_to! :x => 10, :y => 10
36
+ => MoveTo{goal => Vector3D(x=10.000000,y=10.000000,z=0.000000)}:0x4840c8d8[]
37
+ >>
38
+ !task MoveTo{goal => Vector3D(x=10.000000,y=10.000000,z=0.000000)}:0x4840c8d8[] finished successfully
39
+
40
+ Now, let's save the log files for further analysis (otherwise, one could
41
+ destroy them by restarting the controller).
42
+
43
+ # scripts/result tut04
44
+ moving /home/doudou/dev/roby-tutorials/log to /home/doudou/dev/roby-tutorials/results/20080502-tut04
45
+
46
+ <tt>scripts/result</tt> copies all files in <tt>log/</tt> into a subdirectory
47
+ of the result dir (by default APP_DIR/results, but can be changed in
48
+ <tt>config/app.yml</tt>). The target directory name is generated following a pattern of
49
+ <tt><current date>-<name provided on command line></tt>.
50
+
51
+ == Displaying the log file
52
+ Now, let's go in the directory where the results are (see <tt>scripts/results</tt> output). If you look into it,
53
+ two PathPlan files are present: <tt>PathPlan-events.log</tt> and
54
+ <tt>PathPlan-index.log</tt>. The first one includes a trace of everything that
55
+ happens in the Roby controller which has been traced. The second one can
56
+ actually be generated from data in the first one. It is simply used to speed up
57
+ operations.
58
+
59
+ The data in the event log can be used to display the plan operations in a GUI.
60
+ For that, you need to have installed {Ruby/Qt4}[http://korundum.rubyforge.org], as
61
+ the GUI is written using Qt and Ruby.
62
+
63
+ To start it, simply do the following in the directory of the log files:
64
+
65
+ # roby-log replay PathPlan
66
+
67
+ The following window should appear:
68
+
69
+ link:../../images/roby_replay_startup.png
70
+
71
+ This window is separated in three:
72
+ * the toplevel part is the list of data sources defined for this project. It is
73
+ for instance possible to have a synchronized display of the logs of two
74
+ different Roby controllers -- for multi-robot setup.
75
+ * the second part is the set of displays defined. More about that later.
76
+ * the third part is the replay controls: play, fast forward, position, ...
77
+
78
+ Note that you can either print the display, or export it as a SVG file for
79
+ further editing. See the +View+ menu.
80
+
81
+ Right now, we will be looking at the plan structure and execution trace. The
82
+ +Relations+ display is designed for that. Let's add one by clicking on the
83
+ +Add+ button just next to the display type combo. The configuration options
84
+ appear (including the data source associated with the display), and a new
85
+ window:
86
+
87
+ link:../../images/roby_replay_relations.png
88
+
89
+ This display will show two things: the task structure (i.e. how tasks are
90
+ related to each other) and the event propagation (i.e. how events call and/or
91
+ emit each other). The set of task relations to display has to be selected on
92
+ the configuration part of the relation display, including the colors for each
93
+ displayed relation. For our purposes, we only need the +Hierarchy+ (it is the
94
+ actual name of the +realized_by+ relation) and the +PlannedBy+ relations.
95
+
96
+ <b>Very important note</b> your own display may not look exactly like the ones
97
+ displayed here. Some of the features showed here (like threaded planning) are
98
+ asynchronous and as such the exact displays depend on the execution timing. Note
99
+ that, even though it is the case, the robot _behaviour_ remains unchanged.
100
+
101
+ == Startup of the <tt>move_to!</tt> action
102
+
103
+ Let's get to the first task-related events. Click on the 'Step' button until
104
+ something appears on the display. It should look like the next image:
105
+
106
+ link:../../images/roby_replay_first_state.png
107
+
108
+ The displays shows two plans (black boxes). The left one is the plan as it is
109
+ being executed. The right one is called a _transaction_ and allows to build a
110
+ new plan without interfering with the execution. Transactions are presented in
111
+ the sixth tutorial. The task description includes the task model and the task
112
+ owners (which is only useful in multi-robot setup). The <tt>Task labels</tt>
113
+ menu allows to customize that.
114
+
115
+ The left part is a representation of the plan built when the <tt>move_to!</tt>
116
+ command is entered in the shell. It consists of a generic task (Roby::Task)
117
+ which is +planned_by+ a Roby::PlanningTask. This is how Roby handles action
118
+ requests from the shell: (i) it searches a planner defined for that robot with
119
+ the specific action and (ii) generates the plan representing the planning
120
+ process.
121
+
122
+ Once that initial plan has been built, the Roby::PlanningTask task has been
123
+ started. The various cases of event propagation are represented in different
124
+ ways, based on wether or not the event is controlable or contingent, or if it is
125
+ called and/or emitted.
126
+
127
+ link:../../images/roby_replay_event_representation.png
128
+
129
+ A note about propagation representation: it would be useless to represent all
130
+ the event propagation from the beginning of the execution to the current point.
131
+ The display therefore represents only the propagations that have taken place
132
+ <i>since the last display point</i>. It means that, if you go forward 10
133
+ seconds, it will display 10 seconds worth of propagation. In our case, we
134
+ displayed only the first execution cycle and we see that, in this cycle, the
135
+ planning task +start+ event has been called and emitted.
136
+
137
+ == The MoveTo plan
138
+
139
+ Advance again using 'Step' until the display looks like this:
140
+
141
+ link:../../images/roby_replay_02.png
142
+
143
+ The MoveTo action has been planned and the executed plan is modified to reflect
144
+ that. The MoveTo action itself is then started, and that is propagated to the
145
+ ComputePath 'start' event through the signalling relation our plan method has
146
+ defined betweem both.
147
+
148
+ Next execution step gives us the following:
149
+
150
+ link:../../images/roby_replay_03.png
151
+
152
+ PlannedPath emitted its +success+ event. We see here that the emission of the
153
+ +success+ event of that task does not mean 'the plan modification has just took
154
+ place' but instead that 'it has taken place some time earlier'.
155
+
156
+ The ComputePath task has also finished generating the path. That has two
157
+ consequences: the internal data of MoveTo is changed (hence the 'internal_data'
158
+ emission) and TrackPath is started. Here, the dotted lines between the events
159
+ represent a forwarding relation between the two events, while the plain lines
160
+ represent signal relations.
161
+
162
+ If we go back to the code, we see that nowhere a forwarding relation has been
163
+ set up between the +success+ event of ComputePath and the +internal_data+ of
164
+ MoveTo. +internal_data+ is actually automatically emitted by Roby::Task#data=,
165
+ which is called in ComputePath's event handler of +success+ we installed.
166
+
167
+ on :success do |ev|
168
+ parents.find { true }.data = result
169
+ end
170
+
171
+ The interpretation of Roby in that the causality chain is so that the emission
172
+ of +success+ is the cause of the emission of +success+, and as such counts for
173
+ a forward. The same would have happened if the event handler would have called
174
+ an event command.
175
+
176
+ Finally, light grey here represents tasks that have finished with the +success+
177
+ event. Tasks whose +failed+ event has been emitted are represented in red.
178
+
179
+ == To finish: the garbage collection process
180
+
181
+ Now, uncheck the box <tt>View/Hide finalized</tt> in the menu. If you go a few
182
+ cycles further, you should then get the following:
183
+
184
+ link:../../images/roby_replay_04.png
185
+
186
+ Here, TrackPath has finished its execution with success and MoveTo is therefore
187
+ finished as well -- per the forwarding relation between those two events. Note
188
+ that the tasks are now in a dark grey instead than a light one.
189
+
190
+ The mission of the robot, MoveTo, is therefore finished. From the plan
191
+ management point of view, it makes keeping a reference to it useless. In the
192
+ same way, the tasks that were in the plan for the purpose of fullfilling that
193
+ mission are rendered useless as well and can also be removed. The process which
194
+ removes those tasks is called the <i>garbage collection process</i> and runs at
195
+ the end of the execution cycle (Roby::Plan#garbage_collect).
196
+
197
+ The general idea is to kill and remove from the plan the tasks that are not
198
+ useful for the achievement of the robot's missions. The "important" tasks for
199
+ the robot are defined by two sets:
200
+ 1. a set of missions (Roby::Plan#missions)
201
+ 2. a set of "permanent" tasks that should not be considered by Roby's GC
202
+ mechanism (Roby::Plan#keepalive).
203
+
204
+ Then, the task relations are used to determine what are the tasks, in the plan,
205
+ which are actually useful for those "important" tasks
206
+ (Roby::Plan#useful_tasks). This is based on the convention that if a
207
+ <tt>a=>b</tt> relation exists, then +b+ is useful for +a+.
208
+
209
+ The remaining tasks, i.e. the tasks that are not useful, are killed (if needed)
210
+ and removed from the plan. When it is done, the task is said to be finalized
211
+ (hence the need to not hide this kind of task in the View menu).
212
+
213
+ == Some insight in the internal event propagation process
214
+
215
+ The whole propagation process tries to make life as easy as possible in cases
216
+ where multiple events are propagated towards the same source. To make that happen,
217
+ event propagation is done in a bunch of gathering/propagation pairs:
218
+ * the most suitable propagation step (forward and/or signal) is selected
219
+ * This step is performed. The associated user code is called (i.e. event
220
+ command in case of a signal and event handler in case of a forward). Any call
221
+ to command and/or emission which is performed in this user code is delayed in
222
+ the set of pending propagation steps, to be considered for propagation in the
223
+ global propagation loop.
224
+
225
+ See my PhD thesis for more details (links are available in README.txt).
226
+
227
+ = Next tutorial
228
+
229
+ Now that we have seen what happens in the nominal case, the {next
230
+ tutorial}[link:files/doc/tutorials/05-ErrorHandling_rdoc.html] will introduce
231
+ the second part of the execution cycle: the error representation and handling,
232
+ which is -- admittedly -- the most important part of this kind of plan-based
233
+ system.
234
+ ---
235
+ vim: tw=80 et
236
+
@@ -0,0 +1,319 @@
1
+ {Previous tutorial}[link:files/doc/tutorials/04-EventPropagation_rdoc.html]
2
+ {Next tutorial}[link:files/doc/tutorials/06-Overview_rdoc.html]
3
+ = Representing and handling errors
4
+
5
+ One thing about robotics, and in particular plan execution, is that Murphy's
6
+ rule applies quite well. This is due to a few things. Among them, the first is
7
+ that the models planning uses (and therefore the plans it builds) are (i) too
8
+ simple to completely reflect the reality, (ii) badly parametrized and (iii)
9
+ represent dynamic agents, which can themselves be able to take decisions. So, in
10
+ essence, the rule of thumb is that a plan will fail during its execution.
11
+
12
+ Because Roby represents and executes all the activities of a given system, the
13
+ representation of errors becomes a very powerful thing: it is quite easy, when
14
+ an error appears somewhere to actually determine what are its consequences.
15
+
16
+ What this tutorial will show is:
17
+ * how parts of the error conditions are encoded in the task structure.
18
+ * how exceptions that come from the code itself (like NoMethodError ...) are
19
+ handled.
20
+
21
+ == Where do errors come from ?
22
+ === Task structure as a constraint representation
23
+
24
+ Some (not all) task relations also define a set of constraints on the plan
25
+ execution. For instance, the +realized_by+ relation defines a set of
26
+ _desirable_ and a set of _forbidden_ events (the +success+ and +failure+
27
+ options of TaskStructure#realized_by). If none of the desirable events are
28
+ reachable (i.e. none will be emitted +ever+, see
29
+ Roby::EventGenerator#unreachable?), or if one of the forbidden events is
30
+ emitted, a ChildFailedError error is generated.
31
+
32
+ For instance, if we look at the first tutorial, we had an error provoked because
33
+ the +failed+ event of ComputePath has been emitted, while ComputePath was a
34
+ child of MoveTo:
35
+ $ scripts/shell
36
+ >> move_to! :x => 10, :y => 10
37
+ => MoveTo{goal => Vector3D(x=10.000000,y=10.000000,z=0.000000)}:0x48350370[]
38
+ >>
39
+ !Roby::ChildFailedError
40
+ !at [336040:01:45.419/186] in the failed event of ComputePath:0x483502e0
41
+ !block not supplied (ArgumentError)
42
+ ! /home/doudou/dev/roby/lib/roby/thread_task.rb:51:in `instance_eval',
43
+ ! /home/doudou/dev/roby/lib/roby/thread_task.rb:61:in `value',
44
+ ! /home/doudou/dev/roby/lib/roby/thread_task.rb:61:in the polling handler,
45
+ ! /home/doudou/system/powerpc-linux/ruby-1.8.6/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require',
46
+ ! /home/doudou/system/powerpc-linux/ruby-1.8.6/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require',
47
+ ! scripts/run:3
48
+ !
49
+ !The failed relation is
50
+ ! MoveTo:0x48350370
51
+ ! owners: Roby::Distributed
52
+ ! arguments: {:goal=>Vector3D(x=10.000000,y=10.000000,z=0.000000)}
53
+ ! realized_by ComputePath:0x483502e0
54
+ ! owners: Roby::Distributed
55
+ ! arguments: {:max_speed=>1.0,
56
+ ! :goal=>Vector3D(x=10.000000,y=10.000000,z=0.000000)}
57
+ !The following tasks have been killed:
58
+ ! ComputePath:0x483502e0
59
+ ! MoveTo:0x48350370
60
+
61
+ In the case of the PlannedBy relation that we saw in the previous tutorial, the
62
+ error is that no plan can be found. A PlanningFailedError is generated in that
63
+ case.
64
+
65
+ Those two types of error have in common that it is possible to associate the
66
+ error with one of the plan objects (event or task). They are <i>localized
67
+ errors</i> and are subclasses of Roby::LocalizedError. The nice aspect of that
68
+ is that it is possible to assess what is their impact on the plan execution. It
69
+ is therefore possible to handle the error at the plan level and continue
70
+ executing what can be executed.
71
+
72
+ === Errors generated by the code itself
73
+
74
+ In that case, the problem is not to have plan-specific errors anymore. It is to
75
+ handle errors that appear because of bugs in the code itself. Roby is
76
+ implemented in a way where the code is split into two parts:
77
+
78
+ The <i>framework code</i> is the really problematic one. It means that there is
79
+ really a bug in the execution engine itself. In that case, Roby tries to hang up
80
+ as cleanly as possible by killing all tasks that are being executed.
81
+
82
+ The <i>user code</i> is the part of the code which is tied to events and tasks:
83
+ event commands, event handlers, polling blocks. For those, it is actually
84
+ possible to generate a Roby::LocalizedError as in the previous case and to
85
+ handle the error at the plan level. Failed command tasks generate a
86
+ Roby::CommandFailedError, failed event handlers a Roby::EventHandlerError.
87
+ Polling blocks actually emit +failed+ with the poller exception as context (see
88
+ the above error message).
89
+
90
+ Let's try. Add the following event handler in the definition of MoveTo
91
+ (<tt>tasks/move_to.rb</tt>).
92
+
93
+ on :start do
94
+ raise
95
+ end
96
+
97
+ Start (or restart) the controller and launch a <tt>move_to!</tt> action in the
98
+ shell. The following should happen:
99
+
100
+ !Roby::EventHandlerError: user code raised an exception at [336641:28:04.607/23] in the start event of MoveTo:0x2b4330b4fae8
101
+ !
102
+ !
103
+ ! (RuntimeError)
104
+ !./tasks/move_to.rb:10:in event handler for 'start',
105
+ ! /home/joyeux/system/rubygems/lib/rubygems/custom_require.rb:27:in `gem_original_require',
106
+ ! /home/joyeux/system/rubygems/lib/rubygems/custom_require.rb:27:in `require',
107
+ ! scripts/run:3
108
+ !The following tasks have been killed:
109
+ ! MoveTo:0x2b4330b4fae8
110
+
111
+ An equivalent thing would happen with a task-level event handler (i.e. one
112
+ defined on the task object instead of the task model). Remove the model-level
113
+ handler we just added and try adding the following to the planning method in
114
+ <tt>planners/PathPlan/main.rb</tt>. Execute, and see the result !
115
+
116
+ move.on :start do
117
+ raise
118
+ end
119
+
120
+ Now, what happens during execution: how Roby does react to that error ? What we
121
+ can see in the relation display is the following two successive steps (don't
122
+ forget to uncheck <tt>View/Hide finalized</tt>).
123
+
124
+ link:../../images/replay_handler_error_0.png
125
+ link:../../images/replay_handler_error_1.png
126
+
127
+ From Roby point of view, the event has already happened when the event handlers
128
+ are called. Therefore, the event propagation should go on (the temporal
129
+ structure is well-formed). However, an error occured and has not been handled,
130
+ so the MoveTo task cannot be kept running. That is the job of the garbage
131
+ collection process, which queues the 'stop' event, to be executed during the
132
+ next cycle. The MoveTo task is therefore stopped at the next cycle, and the
133
+ tasks that are now useless are also stopped.
134
+
135
+ For event commands, all depends on where the exception actually appears. If
136
+ 'emit' has already been called, then the event will be emitted and propagated.
137
+ Otherwise, it counts as a cancelling of the event command.
138
+
139
+ == Handling errors
140
+
141
+ Now that we have seen how errors are detected and represented, we can tackle
142
+ the problem of handling them. There are three ways to do that in Roby, that we
143
+ will present right away.
144
+
145
+ As we saw in the fifth tutorial, the forwarding relation represents an event
146
+ generalization (the target represents a superset of the situations represented
147
+ by the source), allowing to represent <i>fault modes</i>, i.e. specific fault
148
+ situations that are classified through the forwarding relation (see figure
149
+ below). The target of the forwarding relations being, of course, the +failed+
150
+ event. This is used during error handling to generalize the event handlers: an
151
+ event handler which applies to a given erroneous situation also applies to all
152
+ the situations that are subsets of it.
153
+
154
+ link:../../images/task_event_generalization.png
155
+
156
+ *Example*: the +blocked+ event is a particular fault mode during the movement.
157
+ More complex forwarding network would allow to represent the relationships
158
+ between the different type of faults recognized by the system.
159
+
160
+ === Repairing during events propagation
161
+
162
+ If a child fails, for instance because of a spurious problem, it would have been
163
+ possible to actually restart the failing child directly in the event handler of
164
+ 'failed' and replace the failed task through this new one. This is as simple as:
165
+
166
+ on(:failed) do
167
+ plan.respawn(self)
168
+ end
169
+
170
+ Let's try it. Add the following to the definition of +TrackPath+ to simulate an
171
+ error:
172
+ attr_accessor :should_pass
173
+ event :start do
174
+ if !should_pass
175
+ forward :start, self, :failed, :delay => 0.2
176
+ end
177
+ emit :start
178
+ end
179
+
180
+ What the event command does is schedule a delayed forwarding of 0.2 seconds if
181
+ #should_pass is false (the default). +failed+ will therefore be emitted 0.2
182
+ seconds after the path tracking has been started, if +should_pass+ is false.
183
+
184
+ Then, the error handler itself:
185
+
186
+ on :failed do
187
+ if !should_pass
188
+ Robot.info "respawning ..."
189
+ new_task = plan.respawn(self)
190
+ new_task.should_pass = true
191
+ end
192
+ end
193
+
194
+ This handler replaces the failed TrackPath with a copy of itself and schedules
195
+ it for starting. Then, we set @should_pass to true to avoid having further
196
+ errors. Look at the relation display to see how it worked. Note that doing such
197
+ a thing on the +failed+ event is a bad idea, as +failed+ is emitted when the
198
+ task gets interrupted.
199
+
200
+ The next figure is an example of how it works on a real robot. As a workaround
201
+ of a spurious error in the +TrackSpeedStart+ task, known to be harmless, an
202
+ event handler is defined on this task model, which restarts the task online.
203
+
204
+ link:../../images/repair_event_propagation.png
205
+
206
+ === Asynchronous repairs
207
+
208
+ Sometime, repairing the plan needs a few actions. While those actions are
209
+ performed, we do not actually know yet if the plan *can* be repaired or not,
210
+ only that necessary measures are taken to assess it and/or repair it.
211
+
212
+ In Roby's plans, asynchronous repairs are represented as <i>plan repairs</i>
213
+ (Roby::Plan#add_repair). Plan repairs are tasks which are associated with a
214
+ task's event. While the plan repair is running, errors whose failure point is
215
+ the associated event are simply ignored by the system. Once the task finished,
216
+ normal error detection and handling resumes.
217
+
218
+ To automate the process of installing plan repairs, a ErrorHandling relation
219
+ exists, which defines the set of possible plan repairs for a given task and
220
+ event. Roby::TaskEventGenerator#handle_with allows to easily add a new plan
221
+ repair by associated the receiving task event with the (pending) task.
222
+
223
+ Here is a simple example: http://roby.rubyforge.org/videos/rflex_repaired.avi.
224
+ In this video, the microcontroller which drives the robot's motors can give us
225
+ spurious <tt>BRAKES_ON</tt> messages. Our problem is that the Roby controller
226
+ must determine if the message is spurious, or if brakes are actually set by the
227
+ means of an emergency switch for instance. To do that, an error handling is set
228
+ up, which wait for a few seconds and tests the <tt>BRAKES_ON</tt> state of the
229
+ robot. If the brakes are reported as off, then the robot can start moving again.
230
+ Otherwise, the error was a rightful one and should be handled by other means.
231
+
232
+ Let's simulate the same kind of problem in the PathPlan controller. What we will
233
+ do is the following:
234
+ * add a 'blocked' fault event to the model of TrackPath, and make the 'poll'
235
+ event of TrackPath emit 'blocked' randomly.
236
+ * have a 'repair' task wait 2 seconds and either (randomly) respawn the path
237
+ tracking after those two seconds, or emit +failed+.
238
+
239
+ The first point is done by adding the following to the definition of TrackPath:
240
+ event :blocked
241
+ forward :blocked => :failed
242
+
243
+ and then those three lines to the polling block:
244
+ if rand < 0.05
245
+ emit :blocked
246
+ end
247
+
248
+ A new RepairTask model has to be added. Open <tt>tasks/repair_task.rb</tt> and
249
+ add the following
250
+ class RepairTask < Roby::Task
251
+ terminates
252
+
253
+ event :start do
254
+ Robot.info "repair will succeed in 2 seconds"
255
+ forward :start, self, :success, :delay => 2
256
+ emit :start
257
+ end
258
+
259
+ on :success do
260
+ plan.respawn(failed_task)
261
+ end
262
+ end
263
+
264
+ Finally, the repair handler must be defined added to the plan. Edit the +move_to+ method in <tt>planners/PathPlan/main.rb</tt> and add the following line before the last line of the method:
265
+ track.event(:blocked).handle_with(RepairTask.new)
266
+
267
+ Run as usual and see what happens ...
268
+
269
+ Another, more complex example is the "P3d repaired" video presented
270
+ {here}[files/doc/videos_rdoc.html]
271
+
272
+ === Exception propagation
273
+
274
+ This is the third error handling paradigm available in Roby. It is akin to
275
+ classical exception propagation:
276
+ * task models can define per-type exception handlers using Roby::Task#on_exception
277
+ * when an error occurs and is not handled by a plan repair, the error is
278
+ propagated up in the +realized_by+ relation, searching for a matching
279
+ exception handler.
280
+ * if an exception handler is found, it is called with the error. If the
281
+ exception handlers raises, or if it calls #pass_exception, the propagation is
282
+ resumed. Otherwise, the system stops propagating the exception. In addition
283
+ to following the +realized_by+ relation, the +planned_by+ relation is used
284
+ to check if planning activities can repair the error as well (see example below).
285
+ * if no handler accepted the error, it is passed to a global error handler defined
286
+ by Roby.on_exception.
287
+
288
+ link:../../images/exception_propagation_5.png
289
+
290
+ == Unhandled errors
291
+
292
+ Once the exception propagation phase is finished, the plan analysis (i.e.
293
+ constraint verification) is re-ran once to verify that exception handlers do
294
+ have repaired the errors. If errors are still found, they cannot be handled
295
+ anymore.
296
+
297
+ This set of errors, and the errors that have not been handled before, determine
298
+ a set of tasks that can be dangerous for the whole system. The garbage
299
+ collection kicks in and will take the necessary actions to remove these tasks
300
+ from the plan. Once necessity is to kill all tasks which were actually
301
+ depending on the faulty activities: all tasks that are parents of the faulty
302
+ tasks in any relation are forcefully garbage collected. In the exception
303
+ propagation example above, all tasks which have a number will be killed and
304
+ remove from the plan.
305
+
306
+ = Next tutorial
307
+
308
+ This tutorial presented you with one of the two most singular features of Roby:
309
+ an extensive way to represent and handle errors. Among them, the error handling
310
+ relation is the most powerful, as it allows to represent error handling
311
+ directly in the plan and would for instance work in multi-robot context, even
312
+ without communication between the two robots.
313
+
314
+ {The next tutorial} is not really a tutorial. It is an overview of important
315
+ Roby features that these tutorials did not cover. Again, my PhD thesis should
316
+ still be considered one of the most central design document which allow to
317
+ understand the system.
318
+
319
+