Skip to content

Creating Your First Component

In the OpenJAUS SDK, a JAUS component is represented as a class and the desired JAUS services are added to a component via inheritance. When a service is added to a component all the functionality from the parent services are also added.

Introduction

Base Component Classes

The OpenJAUS SDK provides a few base component classes that provide all the core functionality needed to quickly start communicating between JAUS components and should be used as a starting point for creating your own custom component. These classes are:

  • openjaus::core::EventsBase
  • openjaus::core::Base
  • openjaus::core::Managed

openjaus::core::EventsBase

The EventsBase component class includes the following services:

  • urn:jaus:jss:core:Transport
  • urn:jaus:jss:core:Events
  • urn:jaus:jss:core:Discovery
  • urn:jaus:jss:core:Liveness

The Discovery and Liveness services can be enabled or disabled via the OpenJAUS configuration file.

It also includes the following custom OpenJAUS services:

  • urn:openjaus:core:DiscoveryClient
    • A Discovery service client that will find other JAUS components on the network. The frequency for running the Discovery process can be controlled via the OpenJAUS configuration file.
  • urn:openjaus:core:Configuration
    • A non-standard, custom service that configure the JAUS ID of the component based on the settings specified in the OpenJAUS configuration file.

openjaus::core::Base

The Base component class includes all the services in the EventsBase component and adds the following services:

  • urn:jaus:jss:core:AccessControl
  • urn:jaus:jss:core:Time

The Time service can be enabled or disabled via the OpenJAUS configuration file.

openjaus::core::Managed

The Managed component class includes all the services in the Base component and adds the following services:

  • urn:jaus:jss:core:Management

Which Base Component Class should I use?

The base component class that should be used when creating your own component will depend on the inheritance chain for the services you want to add to your component.

  • If want to add a service that inherits from the Management service, then you should inherit from the openjaus::core::Managed component class.

    • Example: the PrimitiveDriver service (urn:jaus:jss:mobility:PrimitiveDriver)
  • If you want to add a service that inherits from the AccessControl service but not the Management service, then you should inherit from the openjaus::core::Base component class.

    • Example: the GlobalPoseSensor service (urn:jaus:jss:mobility:GlobalPoseSensor)
  • If you want to add a services that inhertis from the Events service but not the AccessControl or Management services, then you should inherit from the openjaus::core::EventsBase component class.

    • Example: the PanTiltJointPositionSensor service (urn:jaus:jss:manipulator:PanTiltJointPositionSensor)
  • If you are adding multiple services, then you should inherit the from the component class includes all the services you need.

    • Example: if adding both the PrimitiveDriver and GlobalPoseSensor services then you should inherit from the openjaus::core::Managed component class.

Most components will either start from the openjaus::core::Base or openjaus::core::Managed component classes.

Note

Avoid adding unnecessary services to your component and pick the appropriate base component class to start from. You should avoid always starting from the openjaus::core::Managed component class if the Management service is not needed as it could lead to unintended side effects.

Creating a Custom Component

The following code snippets show how to create a custom component class.

1
2
3
4
5
6
7
8
9
// .h file

class MyComponent :
    public openjaus::core::Base
{
public:
      MyComponent(std::string name);
      ~MyComponent() override = default;
}
1
2
3
4
5
6
// .cpp file

MyComponent::MyComponent(std::string name) :
      openjaus::core::Base(name)
{
}

Note

A component name is required by the Base component class and is used for two things:

  • As part of the Discovery service to identify the Component on the JAUS network
  • In the OpenJAUS configuration file to identify Component specific configuration parameters

Warning

Use of the openjaus::core::EventsBase(), openjaus::core::Base(), openjaus::core::Managed()s constructors that do not take a name parameter is deprecated and will be removed in the future.

Adding Services to a Component

Services are added to a component using inheritance. This adds support to the component’s message and protocol behavior (also called the state machine) handlers such that the service's input messages will be properly processed and the necessary protocol behavior transitions will be executed.

See AS5710A for a better understanding of messages, internal events, and protocol behavior transitions.

Note

The terms service protocol behavior and service state machine are often used interchangeably.

In the following code snippet, we add the GlobalPoseSensor service to the custom component created previously.

// .h file

class MyComponent :
    public virtual openjaus::mobility_v1_0::services::GlobalPoseSensor, // [1]
    public openjaus::core::Base

public:
    MyComponent(const std::string& name);
    ~MyComponent() override = default;

    openjaus::mobility_v1_0::ReportGlobalPose getReportGlobalPose(openjaus::mobility_v1_0::QueryGlobalPose *queryGlobalPose) override;  // [2]

    bool updateGlobalPose(openjaus::mobility_v1_0::SetGlobalPose *setGlobalPose) override; // [2]
    bool updateGeomagneticProperty(openjaus::mobility_v1_0::SetGeomagneticProperty *setGeomagneticProperty) override; // [2]
}

Note

  1. Notice that the openjaus::mobility::GlobalPoseSensor class is inherited as public virtual. In JAUS, if two services that have a shared parent service are used together in the same component, the shared parent service is the same service instance. For example, if two services (Service A and Service B) both inherit from the AccessControl service and are used together in the same component, then when a client takes control of the component using the AccessControl service, it has control of both Service A and Service B. For this to be properly handled in the OpenJAUS SDK, all service classes must be inherited as `public virtual.
  2. We need to override/reimplement all the methods inherited from the GlobalPoseSensor class. If these methods are not overridden/reimplemented then the service will do nothing when the associated messages are received.
//.cpp file

MyComponent::MyComponent(const std::string& name) :    // This initializer is not needed for proper operation but it is recommended for clarity.
    openjaus::mobility_v1_0::services::GlobalPoseSensor(),
    openjaus::core::Base(name)
{
    // Services must be registered for them to be reported by the Discovery service.
    registerService(openjaus::mobility_v1_0::services::GlobalPoseSensor::create());

    // Messages that will be supported by Events service must be registered with the Events service.
    // The Events service allows a client to receive these messages at a periodic rate (PERIODIC_EVENT),
    // when there is a data change (ON_CHANGE_EVENT) or both (ALL_EVENTS).
    // The maximum periodic rate (Hz) supported is passed as the second parameter.
    Events::publish(openjaus::mobility_v1_0::ReportGlobalPose::ID, -1.0, openjaus::model::ALL_EVENTS);
}

openjaus::mobility_v1_0::ReportGlobalPose MyComponent::getReportGlobalPose(openjaus::mobility_v1_0::QueryGlobalPose *queryGlobalPose)
{
    openjaus::mobility_v1_0::ReportGlobalPose message;
    return message;     // [1]
}

bool MyComponent::updateGlobalPose(openjaus::mobility_v1_0::SetGlobalPose *setGlobalPose)
{
    return true;        // [2]
}

Note

  1. Some methods (usually named as getXXX) return a JAUS message. The message needs to be populated correctly based on the details provided in the AS-4 standard documents. These methods are usually called when a message needs to be sent in response to some incoming message. For example, an incoming QueryXXX message will trigger the getReportXXX method call and the ReportXXX message will be automatically sent.

  2. Some methods return a boolean true or false and are usually used to trigger some internal action. In the above example, the SetGlobalPose message is intended to cause the GlobalPoseSensor service to update its current global position. The state machine handler will move through the state machine calling methods that take a SetGlobalPose message until one of them returns true. It is recommended that you always return true unless you are attempting to extend some existing functionality.