)
~~~
Where the priority value is a number between 1 (lowest) and 99 (highest).
@@ -319,8 +319,8 @@ a chain of method calls, and require the dots.
The second case is called a sequential activity and is declared with:
~~~ ruby
- task('TaskName', 'orogen_project::TaskClass').
- sequential
+ task('TaskName', 'orogen_project::TaskClass')
+ .sequential
~~~
**Next** this is mostly all. [The next page](runtime.html) describes
diff --git a/source/components/index.html.md b/source/components/index.html.md
index 302e3e8..4cdac9e 100644
--- a/source/components/index.html.md
+++ b/source/components/index.html.md
@@ -1,17 +1,161 @@
---
layout: documentation
-title: Introduction
+title: Writing Components
sort_info: 0
directory_title: Components
directory_sort_info: 40
---
-# Components
+# Writing Components
{:.no_toc}
- TOC
{:toc}
+Within Rock, components are _implemented_ in C++. They are also _specified_ in a
+Ruby domain-specific language that is processed by a code generation tool,
+**oroGen**. This tool ensures that the component's interface matches its
+specification. It also removes most of the crude boilerplate-writing code that
+is the declaration in C++ of the component interfaces.
+
+From a package point of view, components are defined in an orogen package. The
+orogen packages are all placed in the `/orogen/` subdirectory of one of the
+[package categories](../workspace/conventions.html). This page will not dwell
+on how to create an orogen package. [Check this page out](./orogen_packages.html)
+for more details on the subject.
+
+**Important** an oroGen package and a library can share the same basename (e.g.
+`drivers/hokuyo` and `drivers/orogen/hokuyo`). This is even a recommended
+behavior when an orogen package is mainly tied to a certain library.
+{: .note}
+
+Within oroGen packages, the components are named _Task Contexts_. This is
+historically the name chosen by the developers of the Orocos RTT toolkit, the
+underlying C++ component implementation used by Rock and orogen.
+
+A component is like a black box that you configure, start, feed data to (write
+data) to get some data out (read data). This page will give you an overview of how
+to create your own, along with the most important guidelines on how to write a
+"good" component.
+
+To start building a component, you have to _define its interface_, answering
+four questions:
+
+1. what is its name ?
+2. what data will it need as input ? (_input ports_)
+3. what data will it need as output ? (_output ports_)
+4. what data will it need as configuration ? (_properties_)
+
+Once the interface is defined, you can use orogen to generate a C++ scaffold, and
+fill the scaffold to actually implement the component.
+
+In summary, the overall development process looks like this:
+
+{: .fullwidth}
+
+1. components are implemented and unit-tested in orogen projects (design and
+ implementation)
+2. you choose how the components will actually run in your system (deployment)
+3. the component is being executed (by Syskit)
+
+## Designing the Interface
+
+The component interfaces are written in the package's `.orogen` file. This section
+will first present the role of the major parts of a component interface, to finish
+with pointers to other pages for details on how to actually write the component's interface definition.
+
+### The component name
+
+This is the one thing that is painful to change. Changing data types, adding and
+removing inputs, outputs or properties is painless. The easiest way to change a
+component's name is to copy all the component implementation from one class to
+another.
+
+**Guidelines**
+
+- suffix all component names by `Task`
+- if the oroGen package you are writing should not have more than one component,
+ simply use `Task`
+- avoid duplicating the namespace. Almost every place where you will refer to the
+ component name will require you to specify the namespace, so it makes names
+ only longer. For instance, do not name `camera_gigevision::GigEVisionCameraTask`, but
+ simply `camera_gigevision::Task`
+
+### Note about data types
+
+Provided you followed a few rules, oroGen is meant to be using the C++ types you
+are using in your libraries directly. This is by far the recommended workflow,
+as your components will follow the progress on the library implementation naturally,
+without adding to the library code a dependency on orogen-generated code.
+
+### Input and Output Ports
+
+The list of input and output ports is obviously very system-specific. While port names
+are relatively easy to change, port _types_ are a rather critical choice.
+
+It is nonetheless generally speaking a very good idea to replicate the naming
+scheme used by other components in your system. We only recommend a specific
+naming scheme for ports that [generate transformations](geometric_transformations.html).
+
+**Reusing the types is critical**. For an input to be connected to an
+output, they need to share the same type. This is the reason why "superset" types
+such as for instance Rock's base/samples/RigidBodyState exist. They are here to allow
+using a full state estimate with linear and angular position and velocities to be used
+for e.g. a simple position.
+
+Also, for this reason, changing a port's type is close to impossible in practice, since
+it requires changing the type in all the system(s) the component is being used. What
+is done instead is creating another port with the updated type and change step by step,
+which is rather effort-consuming (and therefore, avoided in practice)
+
+What has just been said obviously applies only to ports that are meant to be
+connected to another component in the system. As we will see, it is a very good
+practice to have "status" or "debug" ports in your components. These ports will
+only ever be connected to a logger, and for those we usually define a data
+structure that is specific to the component being developed.
+{: .note}
+
+**Guidelines**
+
+- **group together data that belong together**. Do not create 5 ports to get a piece of
+ data because, for instance, you could not find an existing data structure for that
+ combination. Re-combining data that belong together is difficult in an asynchronous
+ system like Rock.
+- **port names need to be unique**, an input cannot be named the same as an output
+
+### Properties
+
+The property interface has a lot less constraints than the ports, especially when
+it comes to types. It is really only a contract between Syskit and the
+component, that does not involve other components in the system. However, it is
+generally speaking good to follow the same naming scheme than other components
+of the same kind in your system, for simplicity of development. As for ports,
+it is good to group properties that really belong together in structures, to
+avoid creating massive unstructured configuration interfaces.
+
+oroGen allows you to define default parameters for simple types in the specification
+directly, and allows you to initialize the more complex types in the C++ code. Always
+do so. Always go for "safe" values as defaults, abstracting yourself from the system
+you are currently developing as much as possible.
+
+### Actually Writing and Updating the Interface Description
+
+Now is a good time to go read on:
+
+- [how to define types](./defining_types.html)
+- [how to import types](./importing_types.html)
+- [how to declare a component interface](./interface.html)
+
+Once you have created / updated a `task_context` block, run `amake` to do
+code generation and verify that everything is fine. Code generation will create
+a C++ class for you to modify in `tasks/`, matching exactly the task context name.
+
+**Implementation Detail**: That class depends on a `Base` class that is managed
+*by orogen within the `.orogen/tasks/` folder. This is how orogen manages to update
+the task interface after the first generation. There are very few operations that
+require a manual change in the `tasks/` folder. They are detailed in the corresponding
+documentation.
+
## Component/Library Separation
We **strongly recommend** that you develop most of your system's functionality
@@ -41,99 +185,29 @@ Developing libraries is covered in the [libraries](../libraries) section.
This section deals with using library-integrated functionality and using them
to build data-processing components.
-## Introduction to Components
+## Implementation
-Within Rock, components are _implemented_ in C++. They are also _specified_ in a
-Ruby domain-specific language that is processed by a code generation tool,
-**oroGen**. This tool ensures that the component's interface matches its
-specification. It also removes most of the crude boilerplate-writing code that
-is the declaration in C++ of the component interfaces.
+Once the component C++ class has been generated, you need to actually implement it.
+Familiarize yourself with the Rock component's
-From a package point of view, components are defined in an orogen package. The
-orogen packages are all placed in the `/orogen/` subdirectory of one of the
-[package categories](../workspace/conventions.html)
+- [state machine](./state_machine.html)
+- [how to interact with the interface elements from C++](./writing_the_hooks.html)
-**Important** an oroGen package and a library can share the same basename (e.g.
-`drivers/hokuyo` and `drivers/orogen/hokuyo`). This is even a recommended
-behavior when an orogen package is mainly tied to a certain library.
-{: .note}
+And, additionally, re-read the part about [initializing properties](./interface.html#properties)
-From this page on, the rest of this section will deal with the integration of
-the functionality from C++ libraries into Rock components by means of orogen.
-But let's first talk about how to create an orogen package.
-
-## Type System
-
-One of the first thing that a system designer has to think about is defining
-the data structures that will be used to exchange data between the system's
-parts (in our case, between components, and between the components and Syskit).
-
-These types are used for a few different things
-
-* in the communication between components, and between components and Syskit
- (ports)
-* in the configuration of the component (properties)
-* to assess the component's state, i.e. diagnotics and monitoring (ports)
-* to provide information about the component's internal state, i.e. debugging (ports)
-
-In Rock, the types are defined in C++, ideally within the libraries but
-sometimes within the component packages. They are then exported into Rock's
-type system to allow for their **transport** (communication between
-processes), but also for their manipulation in Syskit.
-
-This section will detail [how types are defined](defining_types.html), how
-they can [be used within oroGen packages](importing_types.html), and how
-they are [mapped into the Ruby layers](types_in_ruby.html) to ease their use
-on the tooling side.
-
-## Development Workflow
-
-Developing a component involves doing mainly three things:
-
-- [defining and importing data types](importing_types.html) for usage on
- its interface. The recommended course of action for functionality developed
- targetting Rock is to make sure that the library package defines C++ types
- [compatible with Rock's type system](../components/defining_types.html), and
- import these types directly. Alternatively, you may define types within the
- oroGen package and do the conversion between the library and the Rock
- type system, either manually or automatically using [opaque types](../components/importing_types.html#opaques)
-- defining the component(s) interface(s) in the orogen file
-- implementing the processing parts of the component in C++
-
-**Let's remember** we strongly recommend that you develop the bulk of your
-component's functionality in **libraries**, instead of doing in the components
-themselves.
-{: .important}
-
-Each time data types or the orogen specification are modified, one must run
-orogen to re-generate code. After code generation, the package behaves like
-a CMake package.
-
-The best way to build an oroGen package is to use
-[`amake`](../basics/day_to_day.html). It takes care of code generation and
-building the generated CMake package.
-
-## Runtime Workflow
-
-"Developing" a component in C++ within Rock is to write a C++ class that
-interacts with its inputs/outputs. This class does not specify when the
-processing is going to be called, and under which OS resource (threads,
-processes). It is said that the _component implementation_ is separated
-from the _system deployment_. The first one is really writing the C++ code that
-interacts with the component's interface. The second one is part of system
-integration.
-
-What it means in practice is that a component implement is nothing more than a standalone
-C++ class. This C++ class can be instantiated multiple times in a single
-system, using different periods or triggering mechanisms, different threading
-policies, …
-
-When you define components in oroGen, you create a _task library_, which is a
-shared library in which the task context classes are defined. Then, you need to
-put these libraries in _deployments_ (which is also done by oroGen). Finally,
-you can start these deployments, connect the tasks together, and monitor them
-using Syskit.
+We recommend that you unit-test your components. Rock has a built-in support using
+Syskit as a backend. See [this page](./testing.html).
-{: .fullwidth}
+This documentation also provides pages on specific topics important to robotics:
+
+- [Timestamping of data](./timestamping.html)
+- [Handling geometric transformations](./geometric_transformations.html)
+
+## Deployment
+
+In Rock, the _deployment_ is the process to how all these components will be
+split in threads, processes and machines. The oroGen layer deals with the first
+two, Syskit allows you to handle the third.
-**Next** Let's get an overview of [type definitions](defining_types.html)
+In general, with your first components, you will want to keep with [the default
+deployment](./deployment.html#default).
diff --git a/source/components/interface.html.md b/source/components/interface.html.md
index 5bc9efb..22b3f13 100644
--- a/source/components/interface.html.md
+++ b/source/components/interface.html.md
@@ -48,6 +48,7 @@ Documentation for an interface element should be written as a comment directly
on top of the declaration.
### Ports
+
Ports are defined with
~~~ ruby
diff --git a/source/components/create_and_update.html.md b/source/components/orogen_packages.html.md
similarity index 91%
rename from source/components/create_and_update.html.md
rename to source/components/orogen_packages.html.md
index e49187f..767825e 100644
--- a/source/components/create_and_update.html.md
+++ b/source/components/orogen_packages.html.md
@@ -1,10 +1,10 @@
---
layout: documentation
-title: Creating & Updating an oroGen Package
+title: oroGen Packages
sort_info: 3
---
-# Components
+# Managing an oroGen package
{:.no_toc}
- TOC
@@ -91,13 +91,7 @@ another oroGen package.
## Dependencies for type definitions
-It is mandatory that this type of dependency defines a pkg-config file. All Rock
-packages do, but 3rd party libraries may not. If they do not, you will have to
-follow [this step-by-step](../libraries/cpp_libraries.html#unconventional_dependencies) to
-work around these.
-
-Once the new pkg-config file is installed, you can refer to it with
-`using_library` as described [here](./importing_types.html)
+This is covered in the ["Importing types" page](importing_types.html)
## Dependencies to libraries that are used in the public interface
diff --git a/source/components/state_machine.html.md b/source/components/state_machine.html.md
index 589ac7b..fe31236 100644
--- a/source/components/state_machine.html.md
+++ b/source/components/state_machine.html.md
@@ -16,6 +16,11 @@ lifecycle state machine defines how a component can be controlled at runtime.
All Rock components share the same state machine, which is what allows [the
generic Syskit integration](../runtime_overview/event_loop.html).
+Every transition in this state machine is given a **hook**, that is a C++ method
+that will be called when the component should be performing the transition. In the
+diagrams below, the transition is in _italic_ and the corresponding hook name in plain
+(for instance the hook for configure is _configureHook_).
+
## The nominal RTT state machine
What follows is the **nominal** state machine. On each state
@@ -25,11 +30,10 @@ something, i.e. the ones that you -- the component developer -- must implement
if something is needed for a particular transition.
The `configureHook()` and `startHook()` methods may return false, in which case
-the transition is refused.
+the transition is refused. Throwing an exception will have the same effect.
{: .fullwidth}
-
### Configure and Start
As its name implies, the transition between PreOperational and Stopped is meant
@@ -38,9 +42,22 @@ trying to open and configure a device (which can take very long). To give you
another example, in hard realtime contexts, it is expected that `startHook()` is
hard realtime while `configureHook()` does not need to be.
-Additionally, because of [assumptions within Syskit](../basics/recap.html), the
-`configureHook()` is the only place where dynamic ports can be created (and
-`cleanupHook()` the place where they must be destroyed).
+There are two additional hard constraints:
+1. because of [assumptions within Syskit](../basics/recap.html), the
+ `configureHook()` is the only place where dynamic ports can be created (and
+ `cleanupHook()` the place where they must be destroyed).
+2. a component that becomes unused will only be stopped by Syskit, not cleaned up
+ (that is, "de-configured"). For this reason, any operation that needs to be done
+ in a stop/start cycle must be done within the `stopHook` and `startHook`
+
+**In summary:**
+
+- place in `configureHook` as much setup as possible that leaves the component in
+ an inactive state (essentially, not using CPU resources).
+- do not put anything that would require to be redone after a stop
+- place the rest of the initialization code in `startHook`
+- undo everything that `startHook` does in `stopHook`
+- undo everything that `configureHook` does in `cleanupHook`
**Note** the `needs_configuration` statement within the file generated by
`rock-create-orogen` allowed to control whether the component's requires a
@@ -51,7 +68,7 @@ components should have it.
## Error representation
-
+
{: .pull-left}
Errors are represented in the way depicted on the left. The exception state is
@@ -70,11 +87,12 @@ of the method it is in. Make sure you return after the exception:
~~~
void Task::updateHook()
{
- if (something_went_wrong)
- return exception();
+ if (something_went_wrong) {
+ return exception();
+ }
- // Without the 'return', the execution would continue as if everything was
- // alright
+ // Without the 'return', the execution would continue as if everything was
+ // alright
}
~~~