-
Notifications
You must be signed in to change notification settings - Fork 108
Addresstranslation
Introduction
Default Address Translation for D-Bus
Address Translation via the deployment files
Address Translation via the CommonAPI configuration
Address Translation settings directly in the application
All IPC frameworks need a system that ensures that communication partners find each other. For CommonAPI there is a four-part address defined, the type is string:
local:interface_name:version:instance_name
❗ Note that starting from CommonAPI 3.1.9 the CommonAPI address has got its fourth element: the interface version. The version has been introduced in order to be able to deal with several versions of the same interface in one application. We assume at the moment that the version is always 1.0. The details with versioned interfaces will be discussed in a later tutorial.
The basic idea is that applications provide instances of interfaces, e.g. it could be that there are several instances of an audio player in the system; each audio player has the same interface, but the implementation of each instance can be different depending on where the audio files come from (CD player, CE device, HDD, ...). In order to call a function of the audioplayer interface which is implemented in the CD player application I need to know the name of the interface (e.g. "audioplayer") and the instance (e.g. "cd"). The CommonAPI address would then be:
local:audioplayer:v1_0:cd
The first part is the string "local" which is reserved for future use; at the moment it is always "local", so do not change it. The third part is the version; for the other two parts we clarify now the following issues:
- How does the proxy know the CommonAPI address of the corresponding stub and where can it be configured or specified?
- How is the CommonAPI address translated to the IPC specific address?
Question 1: If we go back to the first Hello World example, then we see that there is no special configuration or place where the information about the instance name is held. The developer of the service registers the service by creating an instance of the stub implementation and calling registerService with the instance name as argument.
std::string domain = "local";
std::string instance = "commonapi.examples.HelloWorld";
std::string connection = "service-sample";
std::shared_ptr<HelloWorldStubImpl> myService = std::make_shared<HelloWorldStubImpl>();
bool successfullyRegistered = runtime->registerService(domain, instance, myService, connection);
The developer of the client creates the proxy by calling buildProxy; the name of the interface is a template parameter and the instance name is an argument:
std::string domain = "local";
std::string instance = "commonapi.examples.HelloWorld";
std::string connection = "client-sample";
std::shared_ptr<HelloWorldProxy<>> myProxy = runtime->buildProxy<HelloWorldProxy>(domain, instance, connection);
In this case the developer of the proxy just has to know the instance name. There is no place where he can find it out. In larger projects this could be a problem. The instances should be defined in a deployment file. This has the advantage that everybody can look into the deployment file and find out possible instances and the code generator can support the developer adding the instance information to the generated code. See the example code below.
Question 2: The address translation from the CommonAPI address to the transport or IPC specific address is done via a CommonAPI component which is called "Address Translator"; see Address.hpp/cpp
which you find in the CommonAPI source code. But how is the address translated? In case of the D-Bus binding there is a default mechanism, if no other settings are made. In case of SOME/IP this is not so easy because the SOME/IP addressing is based on integer values (IDs) and there is no easy way to translate string uniquely in integers. In this case the address configuration has to be done by:
- the deployment or
- the binding specific CommonAPI configuration file or
- directly by using the API of the address translator.
The following pictures give an overview.
The default D-Bus address translation is as follows:
As described above there is no similar default mechanism for SOME/IP; the address translation has to be configured by one of the three possibilities; of course the same possibilities exist for D-Bus. Now lets have a look to these three possibilities in detail.
We just reactivate our first standard "Hello World" example with D-Bus. There we did no additional settings concerning the address translation. For a first analysis we start the D-Bus monitor by opening a console window in the build directory and then by typing dbus-monitor &. For the moment ignore the output and start the service (./HelloWorldService &) and then the client (./HelloWorldClient). In the console output we should see the line (among others):
method call time=1705419936.664070 sender=:1.419 -> destination=commonapi.examples.HelloWorld.v0_1_testDBus serial=45 path=/testDBus; interface=commonapi.examples.HelloWorld.v0_1; member=sayHello string "World"
and:
method return time=1705419936.665970 sender=:1.418 -> destination=:1.419 serial=45 reply_serial=46 string "Hello World!"
In the first line you see the service name which is a combination of interface name, service and instance name (commonapi.examples.HelloWorld.v0_1_testDBus
); the path the object path which is derived from the instance name and at the end you see the interface name with version. A better way to analyze the D-Bus addresses is to start the tool d-feet which should be installed at least on your host platform. Start d-feet from your console window an get the following window (or similar):
There within the Session Bus you should see the D-Bus service name in the left column under "unique bus name" and in the right part of the window the object path hierarchy which has for CommonAPI applications always only one node and one interface plus the standard D-Bus interfaces.
The address translation can also be configured via the deployment which should be the normal way. To do that not only for SOME/IP but also for D-Bus we will create an additional DBus deployment file. The file HelloWorld.fdepl
should now look like this:
import "platform:/plugin/org.genivi.commonapi.someip/deployment/CommonAPI-4-SOMEIP_deployment_spec.fdepl"
import "HelloWorld.fidl"
define org.genivi.commonapi.someip.deployment for interface commonapi.examples.HelloWorld {
SomeIpServiceID = 4660
method sayHello {
SomeIpMethodID = 30000
SomeIpReliable = true
in {
name {
SomeIpStringEncoding = utf16le
}
}
}
}
define org.genivi.commonapi.someip.deployment for provider as MyServiceSomeIP {
instance commonapi.examples.HelloWorld {
InstanceId = "testSomeIP"
SomeIpInstanceID = 22136
}
}
Now, in the same folder, create the DBus deployment file HelloWorld-DBus.fdepl
with the following content:
import "platform:/plugin/org.genivi.commonapi.dbus/deployment/CommonAPI-4-DBus_deployment_spec.fdepl"
import "HelloWorld.fidl"
define org.genivi.commonapi.dbus.deployment for provider as MyServiceDBus {
instance commonapi.examples.HelloWorld {
InstanceId = "testDBus"
DBusServiceName = "xyz.MyServiceName"
DBusObjectPath = "/abc/def"
DBusInterfaceName = "xyz.BobsHelloWorld"
}
}
Now there is for both instances of the HelloWorld Service defined how the CommonAPI address is translated into a IPC specific address (D-Bus or SOME/IP). The CommonAPI address consists of
- "local"
- the interface name (you see it after the keyword instance) or in the fidl file and
- the instance name (deployment keyword InstanceId which is part or the core deployment specification)
In our case the CommonAPI addresses are:
local:commonapi.examples.HelloWorld:testDBus
and
local:commonapi.examples.HelloWorld:testSomeIP
.
❗ Note that:
- the complete information for the address translator is defined in the "instance deployment" in case of D-Bus, but the instance deployment of SOME/IP only defines the SomeIpInstanceID. The SomeIpServiceID is defined in the deployment of the interface not in the instance.
- DBusServiceName and DBusInterfaceName must contain at least one dot! This is part of the D-Bus specification and is checked by the address translator.
It is necessary to re-generate the source code using the new deployment files and the generators:
<.>/project$ rm -r src-gen
<.>/project$ cgen/commonapi_core_generator/commonapi-core-generator-linux-x86 --skel --dest-common ./src-gen/core/common --dest-proxy ./src-gen/core/proxy --dest-stub ./src-gen/core/stub --dest-skel ./src-gen/core/skel ./fidl/HelloWorld.fidl
<.>/project$ cgen/commonapi_dbus_generator/commonapi-dbus-generator-linux-x86 --dest-common ./src-gen/dbus/common --dest-proxy ./src-gen/dbus/proxy --dest-stub ./src-gen/dbus/stub ./fidl/HelloWorld.fidl
<.>/project$ cgen/commonapi_someip_generator/commonapi-someip-generator-linux-x86 --dest-common ./src-gen/someip/common --dest-proxy ./src-gen/someip/proxy --dest-stub ./src-gen/someip/stub ./fidl/HelloWorld.fdepl
Now let's check with d-feet if our deployment settings were successful:
Now we see the unique bus name xyz.MyServicename
on the left side.
The SOME/IP address translation cannot be checked so easy. For that we need enhanced log & trace features of CommonAPI. Therefore we skip this check here and come back to it when we discuss the log & trace features.
Before we come to the second possibility to configure the address translator let's have a deeper look into the internal structure of CommonAPI in order to understand which effect the deployment settings have. For that we look into the src-gen
folder with the generated files and open the file HelloWorldDBusProxy.cpp
. It should look like:
/*
* This file was generated by the CommonAPI Generators.
* Used org.genivi.commonapi.dbus 3.2.14.v202310241612.
* Used org.franca.core 0.13.1.201807231814.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*/
#include <v0/commonapi/examples/HelloWorldDBusProxy.hpp>
namespace v0 {
namespace commonapi {
namespace examples {
std::shared_ptr<CommonAPI::DBus::DBusProxy> createHelloWorldDBusProxy(
const CommonAPI::DBus::DBusAddress &_address,
const std::shared_ptr<CommonAPI::DBus::DBusProxyConnection> &_connection) {
return std::make_shared< HelloWorldDBusProxy>(_address, _connection);
}
void initializeHelloWorldDBusProxy() {
CommonAPI::DBus::DBusAddressTranslator::get()->insert(
"local:commonapi.examples.HelloWorld:v0_1:testDBus",
"xyz.MyServiceName",
"/abc/def",
"xyz.BobsHelloWorld");
CommonAPI::DBus::Factory::get()->registerProxyCreateMethod(
HelloWorld::getInterface(),
&createHelloWorldDBusProxy);
}
INITIALIZER(registerHelloWorldDBusProxy) {
CommonAPI::DBus::Factory::get()->registerInterface(initializeHelloWorldDBusProxy);
}
...
} // namespace examples
} // namespace commonapi
} // namespace v0
There we see that the address translator is configured by generated code from the instance deployment. You find the INITIALIZER
macro in the file Types.hpp
in the CommonAPI source code; there you see that on Linux platforms it is a wrapper for the __attribute__
mechanism; which is run when a shared library is loaded. Very similar you find in the generated SOME/IP proxy:
void initializeHelloWorldSomeIPProxy() {
CommonAPI::SomeIP::AddressTranslator::get()->insert(
"local:commonapi.examples.HelloWorld:v0_1:testSomeIP",
0x1234, 0x5678, 0, 1);
CommonAPI::SomeIP::Factory::get()->registerProxyCreateMethod(
"commonapi.examples.HelloWorld:v0_1",
&createHelloWorldSomeIPProxy);
}
The four numbers are the SOME/IP service ID, the SOME/IP instance ID, the major and minor version.
The translation of CommonAPI addresses can also be configured by using the CommonAPI ini-files. To show that we first remove all instance information from the deployment file (HelloWorld.fdepl):
import "platform:/plugin/org.genivi.commonapi.someip/deployment/CommonAPI-4-SOMEIP_deployment_spec.fdepl"
import "HelloWorld.fidl"
define org.genivi.commonapi.someip.deployment for interface commonapi.examples.HelloWorld {
SomeIpServiceID = 4660
method sayHello {
SomeIpMethodID = 30000
SomeIpReliable = true
in {
name {
SomeIpStringEncoding = utf16le
}
}
}
}
Now we re-generate code, run CMake and compile. A short check of the HelloWorldDBusProxy.cpp
file (and HelloWorldSomeIPProxy.cpp
) shows us that the address translator is not initialized anymore. If we run our little test application we see that the D-Bus communication still works but the SOME/IP communication not, because as mentioned before there is no default translation for SOME/IP identifiers. But now we can create CommonAPI configuration files. For that we make a new directory in our project folder (we we don't have it yet) with the name config
and create 2 new *.ini
files there (commonapi-dbus.ini
):
[local:commonapi.examples.HelloWorld:v0_1:testDBus]
service=xyz.MyServiceName
path=/abc/def
interface=xyz.BobsHelloWorld
and commonapi-someip.ini
:
[local:commonapi.examples.HelloWorld:v0_1:testSomeIP]
service=0x1234
instance=0x5678
In order to help CommonAPI to find these configuration files we set two environment variables (we could also copy them to /etc
):
export COMMONAPI_DBUS_CONFIG=<my-project-path>/config/commonapi-dbus.ini
export COMMONAPI_SOMEIP_CONFIG=<my-project-path>/config/commonapi-someip.ini
When we start now service and client again it should work as before with deployment.
The address translator can also be configured directly in the application code. How this can be done can be seen in the initializer code of the proxy when we define the address translation in the deployment.
- First remove the environment settings with unset
COMMONAPI_DBUS_CONFIG
and unsetCOMMONAPI_SOMEIP_CONFIG
to make sure that the configuration files have no effect. - Then change your client application (HelloWorldClient.cpp) and the service (HelloWorldService.cpp).
#include <iostream>
#include <thread>
#include <CommonAPI/CommonAPI.hpp>
#include <CommonAPI/DBus/CommonAPIDBus.hpp>
#include <CommonAPI/SomeIP/CommonAPISomeIP.hpp>
#include "HelloWorldStubImpl.hpp"
using namespace std;
int main() {
CommonAPI::Runtime::setProperty("LogContext", "E01S");
CommonAPI::Runtime::setProperty("LogApplication", "E01S");
CommonAPI::Runtime::setProperty("LibraryBase", "HelloWorld");
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();
std::string domain = "local";
std::string instanceDBus = "testDBus";
std::string instanceSomeIP = "testSomeIP";
std::string connection = "service-sample";
CommonAPI::DBus::DBusAddressTranslator::get()->insert("local:commonapi.examples.HelloWorld:testDBus", "xyz.MyServiceName", "/abc/def", "xyz.BobsHelloWorld");
CommonAPI::SomeIP::AddressTranslator::get()->insert("local:commonapi.examples.HelloWorld:testSomeIP", 0x1234, 0x5678, 1, 0);
std::shared_ptr<HelloWorldStubImpl> myServiceDBus = std::make_shared<HelloWorldStubImpl>();
std::shared_ptr<HelloWorldStubImpl> myServiceSomeIP = std::make_shared<HelloWorldStubImpl>();
runtime->registerService(domain, instanceDBus, myServiceDBus, connection);
runtime->registerService(domain, instanceSomeIP, myServiceSomeIP, connection);
std::cout << "Successfully Registered Service!" << std::endl;
while (true) {
std::cout << "Waiting for calls... (Abort with CTRL+C)" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(60));
}
return 0;
}
Add the same changes to HelloWorldClient.cpp! Now your application should work as before.
❗ By configuring the addresstranslator in you application code you break the CommonAPI abstraction from the underlying middleware! The address translation is binding specific and therefore you have to include binding specific header files and call binding specific member functions of the address translator.