{Previous tutorial}[link:files/doc/tutorials/03-PlannedPath_rdoc.html] {Next tutorial}[link:files/doc/tutorials/05-ErrorHandling_rdoc.html] = Understanding event propagation This tutorial will show you how to trace plan execution, and understand how the plans you build are actually executed by the system. It will not go in great details, but it should help you understand more advanced uses of Roby. For that, we will base ourselves on the execution trace of the controller we built in the previous tutorial. Roby plan execution is based on a fixed-duration execution cycle which includes three steps: link:../../images/roby_cycle_overview.png This tutorial focusses on the first process. It will show how to use the log and display tools that are shipped with Roby, and how event propagation works. The last section will also give you a glimpse of the purpose of the garbage collection algorithm. == Getting a log file We will first get a log file of the plan execution of the PathPlan robot (previous tutorial). Plan execution logs are expensive from a CPU point of view, so they are disabled by default. Enable them back by editing config/app.yml and uncomment events: true around line 23. Now, run again the controller # scripts/run PathPlan and in the shell, do >> move_to! :x => 10, :y => 10 => MoveTo{goal => Vector3D(x=10.000000,y=10.000000,z=0.000000)}:0x4840c8d8[] >> !task MoveTo{goal => Vector3D(x=10.000000,y=10.000000,z=0.000000)}:0x4840c8d8[] finished successfully Now, let's save the log files for further analysis (otherwise, one could destroy them by restarting the controller). # scripts/result tut04 moving /home/doudou/dev/roby-tutorials/log to /home/doudou/dev/roby-tutorials/results/20080502-tut04 scripts/result copies all files in log/ into a subdirectory of the result dir (by default APP_DIR/results, but can be changed in config/app.yml). The target directory name is generated following a pattern of -. == Displaying the log file Now, let's go in the directory where the results are (see scripts/results output). If you look into it, two PathPlan files are present: PathPlan-events.log and PathPlan-index.log. The first one includes a trace of everything that happens in the Roby controller which has been traced. The second one can actually be generated from data in the first one. It is simply used to speed up operations. The data in the event log can be used to display the plan operations in a GUI. For that, you need to have installed {Ruby/Qt4}[http://korundum.rubyforge.org], as the GUI is written using Qt and Ruby. To start it, simply do the following in the directory of the log files: # roby-log replay PathPlan The following window should appear: link:../../images/roby_replay_startup.png This window is separated in three: * the toplevel part is the list of data sources defined for this project. It is for instance possible to have a synchronized display of the logs of two different Roby controllers -- for multi-robot setup. * the second part is the set of displays defined. More about that later. * the third part is the replay controls: play, fast forward, position, ... Note that you can either print the display, or export it as a SVG file for further editing. See the +View+ menu. Right now, we will be looking at the plan structure and execution trace. The +Relations+ display is designed for that. Let's add one by clicking on the +Add+ button just next to the display type combo. The configuration options appear (including the data source associated with the display), and a new window: link:../../images/roby_replay_relations.png This display will show two things: the task structure (i.e. how tasks are related to each other) and the event propagation (i.e. how events call and/or emit each other). The set of task relations to display has to be selected on the configuration part of the relation display, including the colors for each displayed relation. For our purposes, we only need the +Hierarchy+ (it is the actual name of the +realized_by+ relation) and the +PlannedBy+ relations. Very important note your own display may not look exactly like the ones displayed here. Some of the features showed here (like threaded planning) are asynchronous and as such the exact displays depend on the execution timing. Note that, even though it is the case, the robot _behaviour_ remains unchanged. == Startup of the move_to! action Let's get to the first task-related events. Click on the 'Step' button until something appears on the display. It should look like the next image: link:../../images/roby_replay_first_state.png The displays shows two plans (black boxes). The left one is the plan as it is being executed. The right one is called a _transaction_ and allows to build a new plan without interfering with the execution. Transactions are presented in the sixth tutorial. The task description includes the task model and the task owners (which is only useful in multi-robot setup). The Task labels menu allows to customize that. The left part is a representation of the plan built when the move_to! command is entered in the shell. It consists of a generic task (Roby::Task) which is +planned_by+ a Roby::PlanningTask. This is how Roby handles action requests from the shell: (i) it searches a planner defined for that robot with the specific action and (ii) generates the plan representing the planning process. Once that initial plan has been built, the Roby::PlanningTask task has been started. The various cases of event propagation are represented in different ways, based on wether or not the event is controlable or contingent, or if it is called and/or emitted. link:../../images/roby_replay_event_representation.png A note about propagation representation: it would be useless to represent all the event propagation from the beginning of the execution to the current point. The display therefore represents only the propagations that have taken place since the last display point. It means that, if you go forward 10 seconds, it will display 10 seconds worth of propagation. In our case, we displayed only the first execution cycle and we see that, in this cycle, the planning task +start+ event has been called and emitted. == The MoveTo plan Advance again using 'Step' until the display looks like this: link:../../images/roby_replay_02.png The MoveTo action has been planned and the executed plan is modified to reflect that. The MoveTo action itself is then started, and that is propagated to the ComputePath 'start' event through the signalling relation our plan method has defined betweem both. Next execution step gives us the following: link:../../images/roby_replay_03.png PlannedPath emitted its +success+ event. We see here that the emission of the +success+ event of that task does not mean 'the plan modification has just took place' but instead that 'it has taken place some time earlier'. The ComputePath task has also finished generating the path. That has two consequences: the internal data of MoveTo is changed (hence the 'internal_data' emission) and TrackPath is started. Here, the dotted lines between the events represent a forwarding relation between the two events, while the plain lines represent signal relations. If we go back to the code, we see that nowhere a forwarding relation has been set up between the +success+ event of ComputePath and the +internal_data+ of MoveTo. +internal_data+ is actually automatically emitted by Roby::Task#data=, which is called in ComputePath's event handler of +success+ we installed. on :success do |ev| parents.find { true }.data = result end The interpretation of Roby in that the causality chain is so that the emission of +success+ is the cause of the emission of +success+, and as such counts for a forward. The same would have happened if the event handler would have called an event command. Finally, light grey here represents tasks that have finished with the +success+ event. Tasks whose +failed+ event has been emitted are represented in red. == To finish: the garbage collection process Now, uncheck the box View/Hide finalized in the menu. If you go a few cycles further, you should then get the following: link:../../images/roby_replay_04.png Here, TrackPath has finished its execution with success and MoveTo is therefore finished as well -- per the forwarding relation between those two events. Note that the tasks are now in a dark grey instead than a light one. The mission of the robot, MoveTo, is therefore finished. From the plan management point of view, it makes keeping a reference to it useless. In the same way, the tasks that were in the plan for the purpose of fullfilling that mission are rendered useless as well and can also be removed. The process which removes those tasks is called the garbage collection process and runs at the end of the execution cycle (Roby::Plan#garbage_collect). The general idea is to kill and remove from the plan the tasks that are not useful for the achievement of the robot's missions. The "important" tasks for the robot are defined by two sets: 1. a set of missions (Roby::Plan#missions) 2. a set of "permanent" tasks that should not be considered by Roby's GC mechanism (Roby::Plan#keepalive). Then, the task relations are used to determine what are the tasks, in the plan, which are actually useful for those "important" tasks (Roby::Plan#useful_tasks). This is based on the convention that if a a=>b relation exists, then +b+ is useful for +a+. The remaining tasks, i.e. the tasks that are not useful, are killed (if needed) and removed from the plan. When it is done, the task is said to be finalized (hence the need to not hide this kind of task in the View menu). == Some insight in the internal event propagation process The whole propagation process tries to make life as easy as possible in cases where multiple events are propagated towards the same source. To make that happen, event propagation is done in a bunch of gathering/propagation pairs: * the most suitable propagation step (forward and/or signal) is selected * This step is performed. The associated user code is called (i.e. event command in case of a signal and event handler in case of a forward). Any call to command and/or emission which is performed in this user code is delayed in the set of pending propagation steps, to be considered for propagation in the global propagation loop. See my PhD thesis for more details (links are available in README.txt). = Next tutorial Now that we have seen what happens in the nominal case, the {next tutorial}[link:files/doc/tutorials/05-ErrorHandling_rdoc.html] will introduce the second part of the execution cycle: the error representation and handling, which is -- admittedly -- the most important part of this kind of plan-based system. --- vim: tw=80 et