MIRA
Writing a Configuration File


This documentation will guide you through the process of creating and understanding framework config files that can be used with miracenter, mira and miragui.

For a detailed reference to all tags please see the Configuration File Reference.

Configuration files are used to configure a framework. They are written in XML and each file needs a root node.

<root>
</root>

Units

A configuration file is parsed and all contained units will be instantiated by a special unit loader using the <unit> tag. So a basic configuration file will probably contain one or multiple units.

<root>
<unit id="MyUnitsName" class="mira::MyUnit" />
</root>

In the above example an instance of the class mira::MyUnit is created and checked in at the framework using the root namespace and the name "MyUnitsName" (the following authority is created: "/MyUnitsName").

Parameters

Each unit is instantiated by deserialization of the <unit> tag. This means that also parameters (members and properties) of the unit will be loaded from the config file. The following example will additionally set the parameter "MyParam" to 10 and restore the Pose parameter from the given X,Y,Phi members.

<root>
<unit id="MyUnitsName" class="mira::MyUnit">
<MyParam>10</MyParam>
<Pose>
<X>5</X>
<Y>0</Y>
<Phi>0</Phi>
</Pose>
</unit>
</root>

Using namespaces

Whenever multiple units are configured using a single config file, it may be necessary to group units into namespaces using the <namespace> tag. Namespaces can be used to avoid conflicts of published channels or to structure your application.

The following configuration creates two instances of the same unit in separate namespaces.

<root>
<namespace name="A">
<unit id="MyUnitsName" class="mira::MyUnit" />
</namespace>
<namespace name="B">
<unit id="MyUnitsName" class="mira::MyUnit" />
</namespace>
</root>

Two authorities will be checked in at the framework after loading the above configuration:

and their published and subscribed channels will be part of their respective namespace.

Using channels from other namespaces

Units must be placed in namespaces to avoid conflicts of channels. However, if units want to access channels that are not published in - and therefore are not part of their - namespace, we need to import these channels into their namespace. In the following, "MyPublisher" publishes channel "MyChannel". The channel will have the full qualified name "/A/MyChannel". The unit "MySubscriber" subscribes to "MyChannel", but the full qualified name of that channel would be "/B/MyChannel". These are two different channels! For "MySubscriber" to access the same channel "MyPublisher" publishes, we need to import the channel name using the <using> tag:

<root>
<namespace name="A">
<unit id="MyPublisher" class="mira::MyPublisherUnit" />
</namespace>
<namespace name="B">
<using name="/A/MyChannel" />
<unit id="MySubscriber" class="mira::MySubscriberUnit" />
</namespace>
</root>

In the above example the channel "MyChannel" is imported from namespace "A" into namespace "B". Its fully qualified name is now "/B/MyChannel".

Assuming that unit "MySubscriber" subscribes to a channel of the same type but under a different name e.g. "Channel" instead of "MyChannel", one can use the optional 'as' attribute of the <using> tag as follows:

<root>
<namespace name="A">
<unit id="MyPublisher" class="mira::MyPublisherUnit" />
</namespace>
<namespace name="B">
<using name="/A/MyChannel" as="Channel" />
<unit id="MySubscriber" class="mira::MySubscriberUnit" />
</namespace>
</root>

Now the channel "MyChannel" is imported from namespace "A" into namespace "B" and is mapped to "Channel". Its fully qualified name is now "/B/Channel".

In the case that one needs to import names from the parent namespace, a special syntax is provided:

<root>
<namespace name="A">
<unit id="MyPublisher" class="mira::MyPublisherUnit" />
<namespace name="B">
<using name="../MyChannel" />
<unit id="MySubscriber" class="mira::MySubscriberUnit" />
</namespace>
</namespace>
</root>

In the above example, "MyChannel" is imported into namespace "/A/B" from the parent namespace by using the filesystem-like syntax "../". It is allowed to go up several levels at once (e.g. "../../../MyChannel").

Organizing configuration files - using includes

If a configuration file contains many units, it might start to be confusing. Maybe some parts of it can be reused in other configuration files. Therefore, modularization of config files is supported: A configuration file can be split up into multiple files. They can be combined to a single file using the <include> tag.

Assume we have a XML file "BasicComponents.xml" that contains a configuration that stays the same for multiple applications. Now we have a new file that uses these basic components and adds another unit.

<root>
<include file="BasicComponents.xml" />
<unit id="MyUnitsName" class="mira::MyUnit" />
</root>

The "BasicComponents.xml" file is inserted in our own configuration file upon loading. All the units contained in it will be created. Normally, the absolute path to that file is not known. We can use the find placeholder. Assuming that "BasicComponents.xml" resides in a sub directory "configs" in one of our MIRA_PATHs, the following code will find and include it.

<root>
<include file="${find configs/BasicComponents.xml}" />
<unit id="MyUnitsName" class="mira::MyUnit" />
</root>

Conditional configurations - choose what you use

Sometimes a configuration has fixed components but some parts depend on conditions (e.g. Operating system, presence of plugins,...)

These conditional parts can not be solved using includes. We need conditions. Assume you have a unit that operates on a camera image and you want to build a configuration that includes the camera driver unit as well as your image processing unit. But there are multiple camera drivers available. You can start writing many different configs or one config using conditionals.

<root>
<var camera="USB" />
<if camera="USB">
<include file="${find configs/MyUSBCamera.xml}" />
</if>
<if camera="FireWire">
<include file="${find configs/MyFWCamera.xml}" />
</if>
<unit id="MyImageProcessor" class="mira::MyIP" />
</root>

This example shows the use of the <var> tag to define a certain variable "camera" and assign a value "USB" to it. The <if> tag is used to include the correct driver configuration based on the content of the "camera" variable.

To avoid editing of the configuration file whenever a different camera driver should be used, the framework launch tools (like miracenter) allow to specify variables via command line. These variables will overwrite the ones in the configuration file.

Conditionals can also be used to check for existence of a file, class or variable. If for example in the above configuration a particular camera driver is preferred (if exists) no matter what is defined as "camera" variable, the following configuration can be used.

<root>
<var camera="USB" />
<if_exists class="mira::MySpecialCameraDriver">
<include file="${find configs/MySpecialCamera.xml}" />
</if_exists>
<else>
<if camera="USB">
<include file="${find configs/MyUSBCamera.xml}" />
</if>
<if camera="FireWire">
<include file="${find configs/MyFWCamera.xml}" />
</if>
</else>
<unit id="MyImageProcessor" class="mira::MyIP" />
</root>

This example shows the use of the <if_exists> tag and the <else> tag. Now if the class "mira::MySpecialCameraDriver" exists on the system, the special driver is used. In the other cases the value of "camera" is checked.

Defined variables as well as environment variables can also be used in attributes and content of xml tags. They will be replaced by their value using the following rules:

In the following example, the directory to the included config file is chosen by the defined "camera" variable.

<root>
<var camera="USB" />
<include file="${camera}/config.xml" />
<unit id="MyImageProcessor" class="mira::MyIP" />
</root>

Changing parameters of included units

When using the include mechanism, also the parameters of the included units are imported. So to change one or multiple parameters, one needs either to modify the included file or to copy the content of that file into the own configuration. The first option potentially affects the behavior of other files that include the changed one. The second option would make the use of includes pointless. To overcome this limitation, one can change parameters of included units using the <parameter> tag.

The following example shows different ways of how to change single or multiple selected parameters of the included unit "CameraDriver":

// CameraConfig.xml
<root>
<namespace name="camera">
<unit id="CameraDriver" class="mira::MyUSBCamera">
<Framerate>10</Framerate>
<Settings>
<Gain>0.1</Gain>
<WhiteBalance>false</WhiteBalance>
</Settings>
<Filters>
<item>
<Channel>R</Channel>
<Gain>1.0</Gain>
<Offset>10</Offset>
<item>
<item>
<Channel>G</Channel>
<Gain>0.8</Gain>
<Offset>15</Offset>
<item>
<item>
<Channel>B</Channel>
<Gain>0.95</Gain>
<Offset>7</Offset>
<item>
</Filters>
<Parameters>
<key>Noise</key>
<item>0.0</key>
<key>Gain</key>
<item>1.0</item>
</Parameters>
</unit>
</namespace>
</root>
// ApplicationConfig.xml
<root>
// Include camera config
<include file="CameraConfig.xml" />
// Owerwrite framerate parameter
<parameter name="/camera/CameraDriver">
<Framerate>15</Framerate>
</parameter>
// Owerwrite framerate parameter (shorter version)
<parameter name="/camera/CameraDriver.Framerate">15</parameter>
// Owerwrite gain parameter in settings
<parameter name="/camera/CameraDriver.Settings">
<Gain>0.5</Gain>
</parameter>
// Overwrite not yet specified offset parameter in settings
<parameter name="/camera/CameraDriver.Settings">
<Offset>10.5</Offset>
</parameter>
// Overwrite not yet specified offset parameter in settings (shorter version)
<parameter name="/camera/CameraDriver.Settings.Offset">10.5</parameter>
// Overwrite whole settings parameter
<parameter name="/camera/CameraDriver">
<Settings>
<Gain>0.5</Gain>
<WhiteBalance>true</WhiteBalance>
</Settings>
</parameter>
// Overwrite offset parameter in collection item
<parameter name="/camera/CameraDriver.Filters[0].item">
<Offset>10</Offset>
</parameter>
// Overwrite whole item in collection
<parameter name="/camera/CameraDriver.Filters[0]">
<item>
<Channel>R</Channel>
<Gain>1.5</Gain>
<Offset>30</Offset>
</item>
</parameter>
// Add alpha channel filter to collection
<parameter name="/camera/CameraDriver.Filters[+end]">
<item>
<Channel>A</Channel>
<Gain>0.1</Gain>
<Offset>5</Offset>
</item>
</parameter>
// Overwrite parameter in map item
<parameter name="/camera/CameraDriver.Parameters['Noise'].item">2.0</parameter>
// Add parameter to map
<parameter name="/camera/CameraDriver.Parameters['Offset'].item">100.0</parameter>
// finally you can use remove_parameter to remove a parameter from the config
// this can be of advantage if you want a unit to use the default value for this parameter
<remove_parameter name="/camera/CameraDriver.Framerate" />
</root>

Using the transform framework

If an application uses the transform framework, it is possible to define transform frames and links via the configuration file. This is done using the <link> tag. Assume your camera is attached on a moving robot. You know that your robots coordinate frame is "RobotFrame". You also know that the camera is mounted X:0m, Y:0m and Z:1m relative to the robots origin. The following example adds a link between the "RobotFrame" and a new "CameraFrame" and tells the camera driver unit's "Frame" parameter to use this new frame:

<root>
<link child="CameraFrame" parent="RobotFrame">
<X>0.0</X>
<Y>0.0</Y>
<Z>1.0</Z>
<Yaw>0.0</Yaw>
<Pitch>0.0</Pitch>
<Roll>0.0</Roll>
</link>
<unit id="MyCamera" class="mira::MyCameraUnit">
<Frame>CameraFrame</Frame>
</unit>
</root>

Distributed computing - connect to other frameworks

A major feature of MIRA is that an application can be run either in a single process or distributed over multiple frameworks either on the same machine or even on different machines in a network in a decentralized way. Decentralized means that there is no single server and therefore no single point of failure. Decentralized also means that each framework needs to know its remote partner frameworks that it wants to connect to. Each framework can act as a server listening on a port for incoming connections from remote frameworks and as a client connecting to other frameworks. Note that the role of client or server is only relevant for initiating the connection, but once connection is established, exchange of channel data in either direction is fully transparent. There is one connection between any two connected frameworks. Using one server and one client role for connecting two frameworks allows stricter firewall settings, compared to both acting as server for the other (only the machine running the framework that acts as a server needs to open a single port).

The following example starts a framework that listens on port 1234 for incoming connections (as a server) and connects itself (as a client) to a framework running at 192.168.0.100 on port 1234 using the <communication> tag.

<root>
<communication>
<version type="mira::RemoteModule">2</version>
<Port>1234</Port>
<KnownFrameworks>
<item>
<address>192.168.0.100:1234</address>
</item>
</KnownFrameworks>
</communication>
</root>

Loading workspace settings

If you use the the configuration file with miracenter, you may want to load a specific workspace configuration, which is adapted to the other settings within the configuration file. If you have saved the workspace configurations into a file, you can load these by the use of the following code. Thereby instead of the default workspace file, which is automatically saved whenever you close miracenter, this file is used.

<root>
<workspace file="./personal.workspace" />
</root>