This guide teaches you how to:
As you might already know, we are using a *.plugindescription
file to describe an ADTF Component (e.g. Filter, Service, Streaming Source/Sink, ...)
delivered within a corresponding *.adtfplugin
. The containing information, such as for example pins, properties or default runlevel, will be
used by ADTF Configuration Editor to provide the ADTF Component and its dependencies during configuration time without
loading the binary itself. For more information please have a look at ADTF Plugin Description Generator, in this guide here we will
focus which part of the API will be used for creation by using CMake and the post-build generation.
As you might already know from our different examples and guides, the "magic" CMake command for Plugin Description generation is called adtf_create_plugindescription
:
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
project (filter_for_plugin_description)
find_package(ADTF COMPONENTS filtersdk)
# Adds the filter_for_plugin_description project to the Visual Studio solution, which when build
# creates a shared object called filter_for_plugin_description.adtfplugin
adtf_add_filter(filter_for_plugin_description
filter_for_plugin_description.h
filter_for_plugin_description.cpp
)
# Adds the INSTALL project to the Visual Studio solution, which when build
# copies our Filter to the subdirectory given as the second argument into ${CMAKE_INSTALL_PREFIX}
adtf_install_filter(filter_for_plugin_description src/examples/bin)
# Generate a plugindescription for our Filter
# Note: The node <platform> will be generated automatically
adtf_create_plugindescription(
TARGET filter_for_plugin_description
PLUGIN_SUBDIR "src/examples/bin"
VERSION "0.8.15" # this will fill the node <plugin_version> (0.0.0 if not defined)
LICENSE "MPL" # this will fill the node <license> (empty if not defined)
SUPPORT_MAIL "support@mycompany.org" # this will fill the node <support_mail> (empty if not defined)
HOMEPAGE_URL "www.mycompany.org" # this will fill the node <homepage_url> (empty if not defined)
ISSUE_URL "www.mycompany.org/issues/1234" # this will fill the node <issue_url> (empty if not defined)
DEPENDENT_PLUGINS # if required, a list of adtfplugins which have to be loaded before this adtfplugin at runtime
"adtf_xsystem"
"some_custom_plugin"
DEPENDENT_DYNAMIC_LIBS # if required, a list of shared libraries which have to be loaded before this adtfplugin (only for generating plugindescription)
"${DEPENDENCY_DIR}/lib/library.dll" # macro with ${CMake} syntax, will be resolved for generation
PLATFORM_DEPENDENCIES # if required, this will fill the node <platform_dependency> (runtime only, empty if not defined)
"/library.dll" # macro with $(ADTF) syntax, won't be resolved, will be stored in plugindescription as is
"/other_library.dll" # Workaround to prevent CMake resolving: use macro with syntax to be store as $(ADTF) in plugindescription, identifier between two '@' will be included as a macro
PLATFORM_DEPENDENCIES_RELEASE # if required, this will fill the node <platform_dependency> for release only (runtime only, empty if not defined)
"@MACRO_I_KNOW_AT_RUNTIME/bin/fancy.dll" # Workaround to prevent CMake resolving: use macro with syntax to be store as $(ADTF) in plugindescription, identifier between two '@' will be included as a macro
PLATFORM_DEPENDENCIES_DEBUG # if required, this will fill the node <platform_dependency> for debug only (runtime only, empty if not defined)
"@MACRO_I_KNOW_AT_RUNTIME/bin/debug/fancyd.dll" # Workaround to prevent CMake resolving: use macro with syntax to be store as $(ADTF) in plugindescription, identifier between two '@' will be included as a macro
)
#pragma once
// Include all necessary headers from the ADTF SDK
#include <adtffiltersdk/adtf_filtersdk.h>
// For simplicity use the necessary namespaces
using namespace adtf::util;
using namespace adtf::ucom;
using namespace adtf::base;
using namespace adtf::streaming;
using namespace adtf::filter;
// To implement a Filter we subclass adtf::filter::cFilter
class cPluginDescriptionFilter: public cFilter
{
public:
// If use this macro, the Class ID and Label Name will be exported:
// <name>plugin_description_filter.filter.adtf_guides.cid</name>
// <label>Plugin Description Filter</label>
ADTF_CLASS_ID_NAME(cPluginDescriptionFilter,
"plugin_description_filter.filter.adtf_guides.cid",
"Plugin Description Filter");
// If use this macro, the Plugin Description Generator will add required interfaces:
// <required_interfaces>
// <interface_description>
// <iid>reference_clock.giant.streaming.adtf.iid< / iid>
// </interface_description>
// </required_interfaces>
ADTF_CLASS_DEPENDENCIES(REQUIRE_INTERFACE(adtf::services::IReferenceClock));
public:
// In the constructor, the Plugin Description Generator will look for some basic plugin information, see implementation.
cPluginDescriptionFilter();
// If use this function, dynamic input pins will be enabled in Plugin Description
// node:
// <dynamic_input_pins>true</dynamic_input_pins>
tResult RequestDynamicInputPin(const char* /*strName*/,
const adtf::ucom::ant::iobject_ptr<const adtf::streaming::ant::IStreamType>& /*pType*/) override
{
RETURN_NOERROR;
};
// If use this function, dynamic output pins will be enabled in Plugin Description
// node:
// <dynamic_output_pins>true</dynamic_input_pins>
tResult RequestDynamicOutputPin(const char* /*strName*/,
const adtf::ucom::ant::iobject_ptr<const adtf::streaming::ant::IStreamType>& /*pType*/) override
{
RETURN_NOERROR;
};
// If use this function, dynamic input binding pins will be enabled in Plugin Description
// node:
// <dynamic_input_binding_pins>true</dynamic_input_binding_pins>
tResult RequestDynamicInterfaceClient(const char* /*strName*/,
const adtf::ucom::ant::iobject_ptr<const adtf::streaming::ant::IBindingType>& /*pType*/) override
{
RETURN_NOERROR;
};
// If use this function, dynamic output binding pins will be enabled in Plugin Description
// node:
// <dynamic_output_binding_pins>true</dynamic_output_binding_pins>
tResult RequestDynamicInterfaceServer(const char* /*strName*/,
const adtf::ucom::ant::iobject_ptr<const adtf::streaming::ant::IBindingType>& /*pType*/) override
{
RETURN_NOERROR;
};
private:
// Here are all the Pin Reader/Writer for our Filter
ISampleReader* m_pReader = nullptr;
ISampleWriter* m_pWriter = nullptr;
// Here are all the Property Variables for our Filter
property_variable<double> m_fFloatProperty = 3.1415;
property_variable<cString> m_strProperty = { "i am a string" };
};
#include "filter_for_plugin_description.h"
// If use this macro, the basic plugin information will be exported
// <cache_info>
// <plugin_name>Plugin Description Filter Plugin</plugin_name>
// <plugin_checksum/>
// </cache_info>
// <debug>false</debug>
// <versions>
// <version>
// <name>adtf</name>
// <major>3</major>
// <minor>999</minor>
// <patch>999</patch>
// <description>TODO!</description>
// </version>
// <version>
// <name>adtf::ucom</name>
// <major>3</major>
// <minor>1</minor>
// <patch>0</patch>
// <description>TODO!</description>
// </version>
// </versions>
ADTF_PLUGIN("Plugin Description Filter Plugin",
cPluginDescriptionFilter);
cPluginDescriptionFilter::cPluginDescriptionFilter()
{
// This will generate the description for the pins:
// <pin_descriptions>
// <pin_description>
// <name>in</name>
// <streamtype_description>
// <metatype_name>adtf/plaintype</metatype_name>
// <property_descriptions>
// <property_description>
// <name>c-type</name>
// <type>cString</type>
// <value>FLOAT32</value>
// <list/>
// </property_description>
// <property_description>
// <name>md_definitions</name>
// <type>cString</type>
// <value>< struct name = "plain" alignment = "1" version = "1"> < element name = "value" arraysize = "1" type = "tFloat32"> < deserialized alignment = "1" / > < serialized bytepos = "0" byteorder = "LE" / > < / element & gt; (< / struct> < / value>
// <list/>
// </property_description>
// <property_description>
// <name>md_struct</name>
// <type>cString</type>
// <value>plain</value>
// <list/>
// </property_description>
// </property_descriptions>
// </streamtype_description>
// <direction>1</direction>
// <description>This is the input that is processed.</description>
// </pin_description>
// <pin_description>
// <name>out</name>
// <streamtype_description>
// <metatype_name>adtf/plaintype</metatype_name>
// <property_descriptions>
// <property_description>
// <name>c-type</name>
// <type>cString</type>
// <value>FLOAT32</value>
// <list/>
// </property_description>
// <property_description>
// <name>md_definitions</name>
// <type>cString</type>
// <value>< struct name = "plain" alignment = "1" version = "1"> < element name = "value" arraysize = "1" type = "tFloat32"> < deserialized alignment = "1" / > < serialized bytepos = "0" byteorder = "LE" / > < / element & gt; (< / struct> < / value>
// <list/>
// </property_description>
// <property_description>
// <name>md_struct</name>
// <type>cString</type>
// <value>plain</value>
// <list/>
// </property_description>
// </property_descriptions>
// </streamtype_description>
// <direction>1</direction>
// <description>This is the output that is generated.</description>
// </pin_description>
// </pin_descriptions>
m_pReader = CreateInputPin("in", stream_type_plain<float>(), true);
SetDescription("in", "This is the input that is processed.");
m_pWriter = CreateOutputPin("out", stream_type_plain<float>());
SetDescription("out", "This is the output that is generated.");
// This will generate the description for the properties:
// <property_descriptions>
// <property_description>
// <name>float_property</name>
// <type>double</type>
// <value>3.141500</value>
// <list/>
// </property_description>
// <property_description>
// <name>string_property</name>
// <type>cString</type>
// <value>i am a string</value>
// <list/>
// </property_description>
// </property_descriptions>
m_fFloatProperty.SetDescription("Use this property to set some float values.");
RegisterPropertyVariable("float_property", m_fFloatProperty);
m_strProperty.SetDescription("Use this property to set a string.");
RegisterPropertyVariable("string_property", m_strProperty);
// This will generate the description for the component:
// <description>Use this filter to show how the plugin description works.</description>
SetDescription("This filter shows the mapping of filter code to plugindescription.");
// This will generate the description for the component:
// <help_link>path/to/documentation.html</help_link>
SetHelpLink("path/to/documentation.html");
// This will generate the description for a filter editor:
// <editor_descriptions>
// <editor_description>
// <name>Edit with fancy Filter Editor</name>
// <url>fancy_filter_editor.qml</url>
// </editor_description>
// </editor_descriptions>
SetEditor("Edit with fancy Filter Editor", "fancy_filter_editor.qml");
}
So this is all what you have to do to create the plugin description for your Filter.
But what if you want to implement a Streaming Source, Streaming Sink or ADTF Service ? The basics like <plugin_name>
, <version>
and stuff follow always the same rules, the variation
might appear in the ADTF Component type itself. Let's have a look at it.
The main difference is of course that a Service does not have any Pins. The rest works the same as shown in Filter example above.
The information will be exported under the <service_descriptions>
node instead of <filter_descriptions>
.
Besides required interfaces a Service often provides one or more interface. To do so, have a look again at the Filter example and the macro ADTF_CLASS_DEPENDENCIES
,
but use it now with PROVIDE_INTERFACE
instead of REQUIRE_INTERFACE
. This works also in combination, for example:
// If use this macro, the Plugin Description Generator will add provided and required interfaces:
// <provided_interfaces>
// <interface_description>>
// <iid>myinterface.ant.services.adtf.iid</iid>
// </interface_description>
// <interface_description>
// <iid>myinterface.bat.services.adtf.iid</iid>
// </interface_description>
// <interface_description>
// <iid>myinterface.elasto.services.adtf.iid</iid>
// </interface_description>
// </provided_interfaces>
// <required_interfaces>
// <interface_description>
// <iid>reference_clock.giant.streaming.adtf.iid</iid>
// </interface_description>
// <interface_description>
// <iid>kernel.devil.services.adtf.iid</iid>
// </interface_description>
// </required_interfaces>
ADTF_CLASS_DEPENDENCIES(REQUIRE_INTERFACE(adtf::services::IReferenceClock),
REQUIRE_INTERFACE(adtf::services::IKernel),
PROVIDE_INTERFACE(adtf::services::ant::IMyInterface),
PROVIDE_INTERFACE(adtf::services::bat::IMyInterface),
PROVIDE_INTERFACE(adtf::services::elasto::IMyInterface));
The second difference is the default runlevel for your System or UI Service, for that, just use
SetDefaultRunlevel(tInt8 nDefaultRunlevel)
in your constructor:
// constructor
cMyService::cMyService()
{
// This will generate the description for the default runlevel:
// <runlevel>session</runlevel>
SetDefaultRunlevel(tADTFRunLevel::RL_Session);
}
The generation of the Plugin Description for Streaming Services follows exactly the rules as for Filters.
The only difference is the node where the information will be exported, instead of <filter_descriptions>
you will find the descriptions under <streaming_source_descriptions>
(for Streaming Sources) and
<streaming_sink_descriptions>
(for Streaming Sinks).
Yes, there are even more ADTF Components which can be implemented by you than Filters or Streaming/System/UI Services, but this might be
the minor use case for this topic. Anyway, also these ADTF Components must be described to be used for example within
ADTF Configuration Editor, but you can treat them almost like a System Service,
except the not needed default runlevel. Of course, for each type there is a specified node, the Plugin Description will be generated under
<activerunner_descriptions>
(for Active Runners), <samplestream_descriptions>
(for Sample Streams)
and last but not least <bindingproxy_descriptions>
(for Binding Proxies).
Have a look at Data Processor Filter Tutorial to learn how to generate data samples in ADTF.