This is a initial version of the MaCI - Interface documentation. If you find anything that doesn't make sense or even find some errors, please report them! If nobody gives us feedback, the problems with documentation will never get fixed!

What are Interfaces in MaCI?

MaCI interfaces are standardized methods for communicating with software/hardware devices (real or virtual) over the GIMnet. Device drivers implement the 'Server' side of the Interface and application writers implement the 'Client'. Application and device driver developers using MaCIs predefined interfaces benefit avoiding writing of complex communication schemes but can instead focus on their real task at hand just relying on heavily tested MaCI Interfaces.

For those who already know everything: Quick link to MaCI interface reference.

Understanding the structure of interfaces

For every interface type the structure should be defined similarly. Interface implementations should be split in three parts:

  • Datatypes (<InterfaceName>Types.*)
This module defines all the atoms inside the communication packet for the interface. For example, the 'RangingTypes.hpp' under directory 'interfaces/Ranging'.
  • Data encoder/decoder (<InterfaceName>Data.*)
This module uses the 'Datatypes' by encapsulating them inside a 'BinBag' container. A group of 'Datatypes' defines a specific transmission between a Server and Client modules. Data encoder/decoder should be built so that it can be used as a standalone interpreter for sending/receiving GIMI messages. For an example of building a data interpreter conforming to this, see 'interfaces/Ranging/RangingData.hpp'
  • Client / Server (<InterfaceName>Server.*, <InterfaceName>Client.*)
This is the part what really makes MaCI what it is: each interface must always have a Client/Server implementation for it. This allows developers to focus on the real application level implementations instead of recoding communication layer over and over again. These are the modules which are used by most developers when interfacing with MaCI. Server/Client modules use the 'Data encoder/decoder' for sending/receiving commands over the GIMnet. Example of creating such module can be seen in 'interfaces/Ranging/RangingServer.*' and 'interfaces/Ranging/RangingClient.*'

Using interfaces

Quick link to MaCI interface reference.

To be able to use MaCI interfaces, one must include the sources in their own software (or use the library versions instead - when they come available).

We have provided a Makefile helper file which includes all the MaCI library files required to use any or all of the interfaces.

Before including this file, be sure to define the following variables:

  • 'GDIR' - pointing to GIM src root. (only 'utils' are searched here, this may change in the future to 'UTILSPATH')
  • 'MACIPATH' - Pointing to root directory of MaCI

After these are defined, include the file:
in your own Makefile by using the 'include' keyword of 'make'.

Then you will have the following variables defined:

  • 'MACI_EXT_SRC_CPP' - MaCI External required modules.
  • 'MACI_BASE_SRC_CPP' - MaCI Base files, required for all MaCI user applications.
  • 'MACI_INCLUDE_PATHS' - MaCI Include paths. Include this on your compile flags.

Then for example, to create application using the Ranging Client interface, you need to have the following:

  • 'MACI_INTERFACE_RANGING_COMMON_CPP' - MaCI Ranging interface common files
  • 'MACI_INTERFACE_RANGINGCLIENT_CPP' - MaCI Ranging interface Client files

Using interface Clients

MaCI interfaces are always split in two parts. Server and Client. This section describes how to use the Client side interfaces of MaCI.

Applications just using the MaCI interfaces are not directly parts of MaCI, so you should not place your client application implementation under the MaCI sourcetree.

Setting up environment

First thing to do when starting to create a new MaCI Client application is to setup the working environment. Include correct Client interface sources for your application using the Makefile constants in file 'MaCI/interfaces/MaCI-interfaces.Makefile'. Easiest way to setup a Makefile for a new project is to copy a template from '/MaCI/examples/Makefile' and use appropriate sections from it. The example Makefile should be quite readable and easily modifiable. Remember not to modify the original Makefile.

Building application

After you have a working environment (meaning; you can compile the project without errors) you can start experimenting with the interface(s) features.

First of all, all Client interfaces require a pointer to preconnected GIMI instance. We are not going into details here, see the documentation for GIMI for more information.

All Client interfaces have a set of common features, like; Open(), Close(), SetDefaultTarget(), etc. More information about common parts can be found from MaCI base class located in 'MaCI/interfaces/MaCI.hpp' or from respective API documentation. Usually when using any Client interface, the following order of operations apply:

  • Construct instance of Client interface, giving pointer to preconnected GIMI instance.
  • Call 'SetDefaultTarget()' - For defining the service location to use
  • Call 'Open()' - For Open():ing the interface. This also verifies that the service really exists and is accessible.
  • Call ' .. Any interface function ... () ' - Call any interface functions needed. See interface specific documentation from the API Reference for more details.
  • Call 'Close()' - Close the interface after you are done.

And you are set!

All interfaces have an example usage case in 'MaCI/examples' directory, so check there was usage examples and test software.

Using interface Servers

This section describes how using Server interfaces differ from Client interfaces. To fully understand this section, you should first read and understand the part about using Client interfaces.

MaCI server modules can be used in any application requiring to provide a MaCI compatible service (For example; Simulator implementations, like FSRSim). However, implementations intended as 'MaCI Modules' must follow a strict definition of behaviour and supported features. If you are planning to implement a MaCI Module compatible program, for example; implementation of a hardware device for MaCI, please also read guidelines for using and creating MaCI modules.

Setting up environment

As when using the Client interfaces, you should first setup the environment for compiling the project. You can copy a Makefile example from a appropriate subdirectory. For example; when creating an software implementing the interfaces 'Position' and 'SpeedCtrl', you can copy the Makefile from directory 'MaCI/modules/Position_SpeedCtrl', and add in lines lines like (Assuming the new module source would be 'MyNewModule.cpp'):

        @$(LINK) -o $@ $^
        @echo "      [LD] $@"

Building applications

All MaCI compatible server interfaces require a pointer to a instance of class 'CMaCICtrlServer'. This class is responsible for managing the server interfaces. 'CMaCICtrlServer' itself requires a pointer to a preconnected GIMI instance (similar to Client interfaces). Note that all Interfaces belonging to same group (group usually represents a single machine or working unit) should be given the same instance of MaCICtrlServer. In most cases, you module should have only one instance of MaCICtrlServer even it uses multiple Server interfaces.

All server interfaces also have a set of common features, like; Open(), Close(), SetGroupName(), etc. More information about common parts can be found from MaCI base class located in 'MaCI/interfaces/MaCI.hpp' or from respective API documentation.

Normally when using any MaCI Server interface, the following order of operations apply:

  • Construct a instance of CMaCICtrlServer, giving pointer to preconnected GIMI instance.
  • Construct a instance of desired Server interface(s), giving pointer to preconstructed instance of CMaCICtrlServer.
  • Call 'SetGroupName()' - For defining the MaCI group this module is part of (For example; J2B2 or WoPa)
  • Call 'Open()' - For Open():ing the interface and registering all provided services.
  • Call ' .. Any interface function ... () ' - Call any interface functions needed. See interface specific documentation from the API Reference for more details.
  • Call 'Close()' - Close the interface after you are done.

This was the basic operation of a applicationg using a MaCI Server interface. For more examples on how to create applications, see MaCI Modules under directory: 'MaCI/modules'

Creating new interfaces

There are no strict rules how to make a new interface. On the other hand, there is a lot of common pactices and guidelines how to make it in a proper way.

At the minimum, an interface must include at least a client and a server library which work together as a gateway between a module and a client application. The standard, and currently the only way, is to divide the interface into four parts: server, client, data types and data container.

Before starting to develop a new interface, the developer have to think a few things:

  • What he is modelling?
    • Try to make the interface as general as possible
    • Avoid to make it too specific for one hardware
  • What kind of variables are needed for describing the interface
    • Which of them belongs together
  • Which information are sent continuosly and which only once or few times.

Data types

The data type part, describes all the datatypes that are sent over the network. All the variables which combines bigger entities are coupled by using structs. An example of a two-dimesional position struct is below:

struct TPose2D
  TPose2D(const float aX = 0.0,
          const float aY = 0.0,
          const float aHeading = 0.0)
    : x(aX),

  float x; ///< X-axis in meters
  float y; ///< Y-axis in meters
  float a; ///< Heading in radians (positive CCW, Zero towards X-axis)


TPose2D contains three values, all in floats, which describes the x-coordinate, the y-coordinate and the heading. All the values are described in SI-units, coordinates in meters and heading in radians. By describing it, all the two dimensional position sent with the position interface, are always in the same format.

In addition to the structs all the commands and events have to be enu- merated to be able to identify the packet on the other side.

Data container

Data container class is for encapsulating the datatypes in a container, which is sent over the network. The main functionalities of the data container class is to add and to get data structures from the container. There is a class(CMaCIData) which have multiple functions for helping to access the bingbag-container. When creating the data container for a new interface, it is recomended to inherit the CMaCIData-class.

Data container class have usually simple “set” and “get” functions for all the structures defined at the data types. The CMaCIData contains a macro for adding easily data structures to the binbag named ADDELEMENT. With this handy macro all the addings are very easy to implement. Below is an example of adding and returning the structure introduced earlier:

bool CPositionData::SetPose2D(const TPose2D &aPose2D)
bool result = false;
if (!IsReadOnlyContainer() && iPose2DPtr == NULL) {
// Use the handy Macro found from MaCIData
ADDELEMENT(TPose2D, KTypePose2D, iPose2DPtr, aPose2D);
result = true;
return result;
const CPositionData::TPose2D *GetPose2D(void) const {
return iPose2DPtr;

In addition to the getters and setters, one function have to be imple- mented, DecodeFrom, which is inherited from the CMaCIData class. This function goes through the binbag and assigns data class pointers to all cor- rectly defined elements.

Client and server

When the data types and data container has been implemented, client and server can be implemented. It depends on the interface, which side is the server and which the client. Server side is the side which operates something, usually in the machine side, and the client is the user side. The direction of the data flow doesn’t specify the server and client side. As an example, position server produces position data and sends it to the client, but SpeedCtrl server takes speed commands from the client.

MaCI class has helpful function for communicating via GIMnet, without having to use GIM interface directly. There is simple functions to provide or to subscribe to service, sending and receiving events/messages and many other helping functions. Also, if there is a need to use some advanced GIMI features, a pointer to GIMI is available. By inheriting the MaCI-class gives the ability to use these function directly. The most useful functions are : CreateGIMnetConnection & DestroyGIMnetConnection

  • SetMaCICtrlServer
  • SetDefaultTarget
  • SetGroupName
  • RegisterProvidedService & UnregisterProvidedService
  • RegisterAcceptedService & UnregisterAcceptedSercvice
  • SendInterfaceMessage & ReceiveInterfaceMessage
  • SendInterfaceSubscriberMessage
  • SubscribeToInterface & UnsubscribeToInterface


Currently all the interfaces uses binbag for containing the data in binary format for communication purposes. Binbag is a simple binary container which can contain any number of elements which can be added and extracted independently. When using a binary protocol, no conversions are required. Incoming data may be simply typecasted to predefined structure, and any datafield may be directly accessed. BinBag is a very simple data packing format. A complete frame consists of two header bytes and after that, any number of data elements. Data element consist of a type field, a data size field and data field. Type field is just a small integer, intended to distinct different field types from each other. Field type codes are not intended to be globally unique, and most MaCI interfaces use the same few values per each interface.

Something else of great importance