Create a component

What is a component ?

A SolAR component is element embedding processing (and soon storage) capabilities. Components are designed to be connected together in order to create a full pipeline. For this reason, a component defines ideally one (potentially several) processing function defining input (the data to process) and ouptut (the processed data). For interoperability issues, the component has to implement a SolAR component interface defined by the SolAR Framework API available here.

As each component implementation can require dedicated configuration parameters, the SolAR framework provides an easy-to-use mechanism to initialize them at load-time with a external xml configuration file.

Finally, when a SolAR component is implemented, it has to be embedded in a SolAR module for its publication to the SolAR pipeline assemblers.

Create a component with the wizard

We are aware that creating a component and embedding it into a module can be a little tedious. A QTCreator wizard should be soon available to automate file generation and make the component creation easier for you. But for now, follow the instructions below to create a component and add it to a module step-by-step.

Create a component step-by-step

Create a component header

Create with your IDE a header file and put it in the interfaces folder of your module project. Then copy the following code in it:

MyComponent.h
/**
 * Your header defining your copyright and your license
 */

#ifndef MYCOMPONENT_H
#define MYCOMPONENT_H

// mandatory include files
#include "api/componentType/ISolARComponentInterface.h" (1)
#include "MyModuleAPI.h" (2)
//#include "xpcf/component/ComponentBase.h" (3)
#include "xpcf/component/ConfigurableBase.h"

// Optional include files
#include "ThirdPartyLibrary/ClassA.h" (4)

namespace SolAR {
using namespace datastructure;
namespace MODULES {
namespace MYMODULE { (5)
  /**
   * @class MyComponent
   * @brief A short description of MyComponent with its UUID to publish it in the API documentation
   */
  class MYMODULE_EXPORT_API MyComponent : public org::bcom::xpcf::ConfigurableBase, (6)
                                          public api::componentCategory::ISolARComponentInterface (7)
  {
  public:
      ///@brief MyComponent constructor;
      SolARProjectOpencv();
      ///@brief MyComponent destructor;
      ~SolARProjectOpencv() = default;

      /// @brief Processing method
      /// @param[in] input1 the first input parameter.
      /// @param[in] input2 the second input parameter.
      /// @param[out] output1 the first output parameter.
      /// @param[out] output2 the second output parameter.
      /// @return A framework return code
      FrameworkReturnCode myComponentProcesingFunction(const int input1, (8)
                                                       const SRef<SolARDataStructure> input2,
                                                       int output1,
                                                       SRef<SolARDatastructure> output2) override;

      org::bcom::xpcf::XPCFErrorCode onConfigured() override final; (9)

      void unloadComponent () override final; (10)

  private: (11)
      int                       m_configurableAttribute;
      ThirdPartyLibrary::ClassA m_nonConfigurableAttribute;
  };
}
}
}

#endif // MYCOMPONENT_H
1 The SolAR component interface header implemented by your component among the ones defined by the SolAR Framework API available here.
2 The API definition header file of your module to export the interfaces of your component.
3 If you want to implement a configurable component, include the XPCF header file ConfigurableBase (Otherwise, ComponentBase).
4 Include any dependencies to third parties here (std, boost, openCV, etc.). Do not use types or class from these third parties in public functions or attributes. Be sure that these dependencies have been included in the packagedependencies.txt file of your module (for more information, see the section on module dependencies configuration).
5 Embed your component in the namespace of your module that must comply with the following convention: SOLAR/MODULES/MYMODULE.
6 Declare your component class. Your component class must inherit from org::bcom::xpcf::ConfigurableBase if you want to configure parameters from an xml file, or from org::bcom::xpcf::ComponentBase otherwise.
7 Inherit your component class from the SolAR interface it implements. The namespace of SolAR API are hierarchically organized complying the following convention: api/ComponentCategoryLevel_1/ComponentCategoryLevel_1/..ComponentCategoryLevel_n.
8 Declare your processing function(s) by complying with the function prototype(s) of the abstract component interface your component implements (defined in the SolAR framework API).
9 Optional, callback if you need to apply some processing during initialization step when the configuration parameters of your component have just been loaded from the xml file.
10 Mandatory function used by XPCF to unload your component.
11 Declare the private attributes of your component. Normally, no public attributes should be declared since the component is only accessible by the public functions declared above.

Create your component sources

Create with your IDE a source file MyComponent.cpp, and put it in the src folder of your module project. Then copy the following code in it:

MyComponent.cpp
/**
 * Your header defining your copyright and your license
 */

#include "MyComponent.h" (1)
#include "core/Log.h" (2)

// Optional include files
#include "ThirdPartyLibrary/ClassB.h" (3)

XPCF_DEFINE_FACTORY_CREATE_INSTANCE(SolAR::MODULES::MYMODULE::MyComponent); (4)

namespace xpcf  = org::bcom::xpcf;
namespace SolAR {
using namespace datastructure;
namespace MODULES {
namespace MYMODULE { (5)

MyComponent::MyComponent():ConfigurableBase(xpcf::toUUID<MyComponent>()) (6)
{
    addInterface<api::componentCategory::ISolARComponentInterface>(this); (7)

    SRef<xpcf::IPropertyMap> params = getPropertyRootNode(); (8)
    params->wrapInteger("configurableAttribute", m_configurableAttribute);
}

xpcf::XPCFErrorCode MyComponent::onConfigured() (9)
{
  // Do initialization here when your configurable parameters have been updated with the xml configuration file.

  return xpcf::_SUCCESS;
}

FrameworkReturnCode MyComponent::myComponentProcesingFunction(
          const int input1,
          const SRef<SolARDataStructure> input2,
          int output1,
          SRef<SolARDatastructure> output2) (10)
{
    // Processing code here

    return FrameworkReturnCode::_SUCCESS;
}
}}}
1 Include the header file of your component.
2 The SolAR framework provides a dedicated solution for logging. Inlcude core/Log.h if you want to use it.
3 Include any third partiy header files required for your processing. Be sure that these dependencies have been included in the packagedependencies.txt file of your module (for more information, see the section on module dependencies configuration).
4 Add the component to the XPCF factory to be able to create an instance of it at run-time.
5 Embed your component implementation in the namespace of your module that must comply with the following convention: SOLAR/MODULES/MYMODULE.
6 Implement your default component constructor. Inherit your component from ConfigurableBase, or ComponentBase if the component is not configurable, and in both case, pass as the argument of the parent constructor the UUID of your component.
7 During component construction, add its interface to XPCF.
8 To add a configurable parameter and map it to the xml configuration file, retrieve a reference to the properties of the component, and add a wrapper for each configuration parameter you want to map. Here, when an xml element with the name configurableAttribute of type integer will be specified in the configuration file, when exiting the constructor, its value will be set to the attribute m_configurableAttribute.
9 If you want to initialize internal structures requiring that the value of the configurable parameters was set, you can do it in the onConfigured callback.
10 Implement the processing function(s) of your component.

Embed your component in its module

Module traits update

To add a component to your module, you need to define its traits in the file MyModule_traits.h:

MyModule_traits.h
/**
 * Your header defining your copyright and your license
 */

#ifndef MYMODULE_TRAITS_H
#define MYMODULE_TRAITS_H

#include "xpcf/core/traits.h"

namespace SolAR {
namespace MODULES {
namespace MYMODULE {

class MyComponent; (1)

XPCF_DEFINE_COMPONENT_TRAITS(MyComponent, (2)
                             "MyComponent UUID", (3)
                             "MyComponent name", (4)
                             "MyComponent description"); (5)

}
}
}
#endif // MYMODULE_TRAITS_H
1 Declare the class of your component.
2 Provide the class corresponding to your component.
3 Replace MyComponent UUID by a Universally Unique IDentifier. To get one, you can use an online UUID generator.
4 Replace MyModule name by the name of your module.
5 Please, provide a detailed description of your component that could be introspected by authoring tools.

You have to add to this file as many component class declarations and traits as you have components in your module. Thanks to this trait file relative to a module, any user will just have to include it to instantiate any of its components.

Module source update

Then, you have to add you component to the module factory and declare it to XPCF in the MyModule.cpp:

MyModule.cpp
/**
 * Your header defining your copyright and your license
 */

#include "xpcf/module/ModuleFactory.h"
#include "MyModule_traits.h"

#include "MyComponent.h" (1)
#include "MyComponent1.h"
#include "MyComponent2.h"

namespace xpcf=org::bcom::xpcf;

XPCF_DECLARE_MODULE("MyModule UUID", "MyModule", "MyModule description");

extern "C" XPCF_MODULEHOOKS_API xpcf::XPCFErrorCode XPCF_getComponent(const boost::uuids::uuid& componentUUID,SRef<xpcf::IComponentIntrospect>& interfaceRef)
{
    xpcf::XPCFErrorCode errCode = xpcf::XPCFErrorCode::_FAIL;

    errCode = xpcf::tryCreateComponent<SolAR::MODULES::MYMODULE::MyComponent>(componentUUID,interfaceRef); (2)
    if (errCode != xpcf::XPCFErrorCode::_SUCCESS)
    {
        errCode = xpcf::tryCreateComponent<SolAR::MODULES::MYMODULE::MyComponent1>(componentUUID,interfaceRef);
    }
    if (errCode != xpcf::XPCFErrorCode::_SUCCESS)
    {
        errCode = xpcf::tryCreateComponent<SolAR::MODULES::MYMODULE::MyComponent2>(componentUUID,interfaceRef);
    }
    return errCode;
}using

XPCF_BEGIN_COMPONENTS_DECLARATION
XPCF_ADD_COMPONENT(SolAR::MODULES::MYMODULE::MyComponent) (3)
XPCF_ADD_COMPONENT(SolAR::MODULES::MYMODULE::MyComponent1)
XPCF_ADD_COMPONENT(SolAR::MODULES::MYMODULE::MyComponent2)
XPCF_END_COMPONENTS_DECLARATION
1 Include your component header.
2 Add a call to xpcf_tryCreateComponent to create your component when the module factory tries to instantiate it.
3 Add your component to XPCF.

Update the module registry

Finally, edit the xpcf_MyModule_registry.xml that should be at the root folder of your module project. Add a decsription for each component you want to embed in your module as follows:

xpcf_MyModule_registry.xml
<xpcf-registry>
<module uuid="MyModule_UUID" name="MyModule_Name" description="MyModule_Description" path="$REMAKENROOT/MyModule/MyModule_version/lib/x86_64/shared">

<component uuid="MyComponent UUID" name="MyComponent name" description="MyComponent Description"> (1)
        <interface uuid="125f2007-1bf9-421d-9367-fbdc1210d006" name="IComponentIntrospect" description="IComponentIntrospect"/> (2)
        <interface uuid="UUID of the Inherited SolAR Interface" name="name of the Inherited SolAR Interface" description="description of the Inherited SolAR Interface"/> (3)
</component>

</module>
</xpcf-registry>
1 Copy and paste the component UUID, name and description defined in the file MyModule_traits.h.
2 Keep always this description to the inheritance to _IComponentIntrospect.
3 Add a description to all SolAR interfaces inherited by MyComponent. To get access to UUID of SolAR interfaces, please refer at the end of the header file of each interface, into the parameters of the MACRO XPCF_DEFINE_INTERFACE_TRAITS.