roby 0.7

Sign up to get free protection for your applications and to get access to all the features.
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
+