Skip to content

CommonAPI C SomeIP in 10 minutes

dibpinto edited this page Feb 8, 2024 · 4 revisions

CommonAPI C++ in 10 minutes (with SOME/IP)

Table of contents

Step 1 & 2: Preparation / Prerequisites
Step 3: Build the CommonAPI SOME/IP Runtime Library
Step 4: Write the Franca file and generate code
Step 5: Write the client and the service application
Step 6: Build and run

Step 1 & 2: Preparation / Prerequisites

The following description is tested with CommonAPI 3.2.3 and assumes that you use a standard Linux distribution (I tested it with Ubuntu 22.04) and that you have installed git and (CMake >=3.13). Please note that the code generators need the java 8 runtime environment. If you didn't pass the instructions of the page "CommonAPI C++ D-Bus in 10 minutes" then do now step 2 in order to get and build the CommonAPI runtime library.

Step 3: Build the CommonAPI SOME/IP Runtime Library

Start again with cloning the source code of CommonAPI-SomeIP:

$ git clone https://github.com/GENIVI/capicxx-someip-runtime.git

Just as CommonAPI C++ D-Bus requires the D-Bus library libdbus, the SOME/IP binding builds on the SOME/IP core library vsomeip. Fortunately. the source code of this library can be easily downloaded from github. Just clone the vsomeip repository:

$ git clone https://github.com/GENIVI/vsomeip.git

Before you can go ahead with compiling vsomeip you must ensure that the prerequisites are fulfilled. vsomeip uses the standard cross-platform C++ library Boost.Asio for the implementation of some core network functions. Before you can compile the vsomeip library make sure that Boost (>=1.55) is installed on your system. You can follow the installation instructions at http://www.boost.org/doc/libs/1_59_0/more/getting_started/unix-variants.html, but if you have an Ubuntu platform it is easier to install it with the package manager APT (see also the README on https://github.com/GENIVI/vsomeip). When Boost has been successfully installed, you can build vsomeip without further difficulty as usual:

$ cd vsomeip
<.>/vsomeip$ cmake -Bbuild -DCMAKE_INSTALL_PREFIX=../install_folder -DENABLE_SIGNAL_HANDLING=1 -DDIAGNOSIS_ADDRESS=0x10 .
<.>/vsomeip$ cmake --build build --target install

On my system CMake prints out the following lines concerning BOOST:

-- Found Boost: /usr/lib/x86_64-linux-gnu/cmake/Boost-1.74.0/BoostConfig.cmake (found suitable version "1.74.0", minimum required is "1.55") found components: system thread filesystem 
-- Using boost version: 107400

With ENABLE_SIGNAL_HANDLING=1 the signal handling of vsomeip (SIGINT/SIGTERM) is enabled; that you can abort vsomeip applications by ctrl-c. The second parameter DIAGNOSIS_ADDRESS specifies the first byte of the SOME/IP client ID (don't care if you do not know at the moment what it is). You just have to know that the client ID is a number in the SOME/IP message header which has to be unique in the system. In order to make sure that it is unique, for each device (or node) the vsomeip implementation sets the first byte of this client ID to a fixed value which must be different for all devices in the SOME/IP network. If you only intend to communicate locally via SOME/IP this is not necessary.

Now it should be no problem to build the CommonAPI C++ SOME/IP runtime library:

$ cd capicxx-someip-runtime
<.>/capicxx-someip-runtime$ cmake -Bbuild -DCMAKE_INSTALL_PREFIX=../install_folder -DCMAKE_PREFIX_PATH=../install_folder -DUSE_INSTALLED_COMMONAPI=OFF .
<.>/capicxx-someip-runtime$ cmake --build build --target install

Step 4: Write the Franca file and generate code

Now follow again the instructions of the page "CommonAPI C++ D-Bus in 10 minutes", Step 4:

  • Create a sub-directory project in your work directory and then the sub-directory fidl in the project directory.
  • Change to this sub-directory.
  • Now create HelloWorld.fidl as described in the D-Bus tutorial. It's CommonAPI: concerning the interface definition in the fidl there is no difference.

But the SOME/IP specification additionally requires the definition of service and method identifiers. Franca IDL provides the possibility to provide this kind of IPC framework specific parameters by means of so-called Franca deployment files. These deployment files have the ending .fdepl and have a Franca-like syntax (deployment parameter file). The exact content (which deployment parameters must be provided) must be specified also in a fdepl-file (deployment specification file). The deployment specification file which is implemented by the CommonAPI C++ SOME/IP specification can be found in the SOME/IP code generator project. If you don't want to check out the complete tools project just have a look at https://github.com/GENIVI/capicxx-someip-tools; you will find the file CommonAPI-4-SOMEIP_deployment_spec.fdepl in the directory org.genivi.commonapi.someip/deployment.

Based on this deployment specification it is possible to write the deployment parameter file; usually we call it like the fidl file, just with the ending fdepl, 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
            }
        }
    }
}

define org.genivi.commonapi.someip.deployment for provider as Service {
    instance commonapi.examples.HelloWorld {
        InstanceId = "commonapi.examples.HelloWorld"
        
        SomeIpInstanceID = 4660
    
        SomeIpUnicastAddress = "192.168.0.2"
        SomeIpReliableUnicastPort = 30499
        SomeIpUnreliableUnicastPort = 30499
    }
}

The SOME/IP deployment specification has some mandatory parameters; it imports the binding independent CommonAPI deployment specification. Therefore we find the parameter InstanceId in the instance deployment.

Now you need the SOME/IP code generator. It can be downloaded for a certain release just as the other code generators from github. Call the SOME/IP generator similar to the CommonAPI C++ D-Bus code generator before (I assume again that you have the code generator executables in cgen subfolder):

<.>/project$ ./cgen/commonapi_core_generator/commonapi-core-generator-linux-x86 -d src-gen/core -sk ./fidl/HelloWorld.fidl
<.>/project$ ./cgen/commonapi_someip_generator/commonapi-someip-generator-linux-x86 -d src-gen/someip ./fidl/HelloWorld.fdepl

❗ Please note that there are some SOME/IP parameters mandatory. Therefore the input file for the code generator is the deployment parameter file (fdepl); the fidl file itself cannot be used.


Step 5: Write the client and the service application

It's CommonAPI C++ 👏 Just follow step 5 of "CommonAPI C++ D-Bus in 10 minutes". There is no difference.

Step 6: Build and run

Since you must have installed CMake in order to build the CommonAPI runtime libraries, the fastest way to compile and build our applications is to write a simple CMake file. If already existent, replace your CMakeLists.txt directly in the project directory for the following:

cmake_minimum_required(VERSION 2.8)

set(PRJ_NAME HelloWorld)

set(CMAKE_VERBOSE_MAKEFILE on)

OPTION(USE_FILE "Set to OFF to disable file logging" OFF )
message(STATUS "USE_FILE is set to value: ${USE_FILE}")

OPTION(USE_CONSOLE "Set to OFF to disable console logging" OFF )
message(STATUS "USE_CONSOLE is set to value: ${USE_CONSOLE}")

IF(USE_FILE)
  add_definitions(-DUSE_FILE)
ENDIF(USE_FILE)
IF(USE_CONSOLE)
  add_definitions(-DUSE_CONSOLE)
ENDIF(USE_CONSOLE)

SET(MAX_LOG_LEVEL "DEBUG" CACHE STRING "maximum log level")
message(STATUS "MAX_LOG_LEVEL is set to value: ${MAX_LOG_LEVEL}")
add_definitions(-DCOMMONAPI_LOGLEVEL=COMMONAPI_LOGLEVEL_${MAX_LOG_LEVEL})

if (MSVC)
# Visual C++ is not always sure whether he is really C++
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /EHsc /wd\\\"4503\\\"")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd\\\"4503\\\"")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -Wall -O0 -std=c++11 -D_GLIBCXX_USE_NANOSLEEP -DLINUX")
endif()

message(STATUS "Compiler options: ${CMAKE_CXX_FLAGS}") 

if(NOT CMAKE_BUILD_TYPE)
   set(CMAKE_BUILD_TYPE "Debug" CACHE STRING
       "Choose the type of build, options are: Debug Release." FORCE)
endif(NOT CMAKE_BUILD_TYPE)
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")

OPTION(USE_INSTALLED_COMMONAPI "Set to OFF to use the local (build tree) version of CommonAPI" ON)
message(STATUS "USE_INSTALLED_COMMONAPI is set to value: ${USE_INSTALLED_COMMONAPI}")

if ("${USE_INSTALLED_COMMONAPI}" STREQUAL "ON")
    FIND_PACKAGE(CommonAPI 3.2.0 REQUIRED CONFIG NO_CMAKE_PACKAGE_REGISTRY)
    FIND_PACKAGE(CommonAPI-DBus 3.2.0 REQUIRED CONFIG NO_CMAKE_PACKAGE_REGISTRY)
else()
    FIND_PACKAGE(CommonAPI 3.2.0 REQUIRED CONFIG NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
    FIND_PACKAGE(CommonAPI-DBus 3.2.0 REQUIRED CONFIG NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH)
endif()

message(STATUS "CommonAPI_CONSIDERED_CONFIGS: ${CommonAPI_CONSIDERED_CONFIGS}")
message(STATUS "COMMONAPI_INCLUDE_DIRS: ${COMMONAPI_INCLUDE_DIRS}")
message(STATUS "CommonAPI-DBus_CONSIDERED_CONFIGS: ${CommonAPI-DBus_CONSIDERED_CONFIGS}")
message(STATUS "COMMONAPI_DBUS_INCLUDE_DIRS: ${COMMONAPI_DBUS_INCLUDE_DIRS}")

# CommonAPI
include(FindPkgConfig)
###############################################################################
# find DBus by using the 'pkg-config' tool
if (MSVC)
    #Not beautiful, but it works
    if (DBus_DIR)
        if (DBus_BUILD_DIR)
            set(DBus_INCLUDE_DIRS "${DBus_DIR};${DBus_BUILD_DIR};")
        else ()
            message (FATAL_ERROR "DBus_BUILD_DIR not set! Cannot continue.")
        endif ()
    else()
        message (FATAL_ERROR "DBus_DIR not set! Cannot continue.")
    endif ()
else()
    pkg_check_modules(DBus REQUIRED dbus-1>=1.4)
endif()

# SOME/IP
find_package (CommonAPI-SomeIP 3.2.0 REQUIRED)
find_package (vsomeip3 3.1.0 REQUIRED)

# Source Files
set(PRJ_SRC_PATH src)
set(PRJ_SRC_GEN_PATH src-gen)
set(PRJ_SRC_GEN_COMMONAPI_PATH ${PRJ_SRC_GEN_PATH}/core/v0/commonapi/examples)
set(PRJ_SRC_GEN_COMMONAPI_DBUS_PATH ${PRJ_SRC_GEN_PATH}/dbus/v0/commonapi/examples)
set(PRJ_SRC_GEN_COMMONAPI_SOMEIP_PATH ${PRJ_SRC_GEN_PATH}/someip/v0/commonapi/examples)

set(PRJ_NAME_CLIENT ${PRJ_NAME}Client)
set(PRJ_NAME_SERVICE ${PRJ_NAME}Service)

# Application
FILE(GLOB PRJ_PROXY_GEN_SRCS ${PRJ_SRC_GEN_COMMONAPI_PATH}/*Proxy.cpp)
FILE(GLOB PRJ_STUB_GEN_SRCS ${PRJ_SRC_GEN_COMMONAPI_PATH}/*Stub*.cpp)
FILE(GLOB PRJ_STUB_IMPL_SRCS ${PRJ_SRC_COMMONAPI_PATH}/*Stub*.cpp)
set(PRJ_CLIENT_SRCS ${PRJ_SRC_PATH}/${PRJ_NAME_CLIENT}.cpp ${PRJ_PROXY_GEN_SRCS})
set(PRJ_SERVICE_SRCS ${PRJ_SRC_PATH}/${PRJ_NAME_SERVICE}.cpp ${PRJ_SRC_PATH}/${PRJ_NAME}StubImpl.cpp ${PRJ_STUB_GEN_SRCS} ${PRJ_STUB_IMPL_SRCS})

# Boost
find_package( Boost 1.54 COMPONENTS system thread log REQUIRED )
include_directories( ${Boost_INCLUDE_DIR} )

# DBus library
FILE(GLOB PRJ_DBUS_LIB_SRCS ${PRJ_SRC_GEN_COMMONAPI_DBUS_PATH}/*cpp)

# SOME/IP library
FILE(GLOB PRJ_SOMEIP_LIB_SRCS ${PRJ_SRC_GEN_COMMONAPI_SOMEIP_PATH}/*cpp)

# Paths
OPTION(USE_INSTALLED_DBUS "Set to OFF to use the local (patched) version of dbus" ON)
message(STATUS "USE_INSTALLED_DBUS is set to value: ${USE_INSTALLED_DBUS}")

include_directories(
    src-gen/core
    src-gen/dbus
    src-gen/someip
    ${COMMONAPI_INCLUDE_DIRS}
    ${COMMONAPI_DBUS_INCLUDE_DIRS}
    ${COMMONAPI_SOMEIP_INCLUDE_DIRS}
    ${DBus_INCLUDE_DIRS}
    ${VSOMEIP_INCLUDE_DIRS}
)

if ("${USE_INSTALLED_DBUS}" STREQUAL "ON")
link_directories(
    ${COMMONAPI_LIBDIR}
    ${COMMONAPI_DBUS_LIBDIR}
    ${COMMONAPI_SOMEIP_CMAKE_DIR}/build
    ${DBus_LIBRARY_DIRS}
    ${Boost_LIBRARY_DIR}
)
else()
link_directories(
    ${COMMONAPI_LIBDIR}
    ${COMMONAPI_DBUS_LIBDIR}
    ${COMMONAPI_SOMEIP_CMAKE_DIR}/build
    ${DBus_INCLUDE_DIRS}/dbus/.libs
    ${Boost_LIBRARY_DIR}
)
endif()

if (MSVC)
  set(LINK_LIBRARIES CommonAPI)
else()
  set(LINK_LIBRARIES -Wl,--as-needed CommonAPI)
endif()

# Build Client
add_executable(${PRJ_NAME_CLIENT} ${PRJ_CLIENT_SRCS})
target_link_libraries(${PRJ_NAME_CLIENT} ${LINK_LIBRARIES})

# Build service
add_executable(${PRJ_NAME_SERVICE} ${PRJ_SERVICE_SRCS})
target_link_libraries(${PRJ_NAME_SERVICE} ${LINK_LIBRARIES})

# Build DBus library
add_library (${PRJ_NAME}-dbus SHARED ${PRJ_DBUS_LIB_SRCS})
target_link_libraries(${PRJ_NAME}-dbus CommonAPI-DBus)

# Build SOME/IP library
add_library (${PRJ_NAME}-someip SHARED ${PRJ_SOMEIP_LIB_SRCS})
target_link_libraries(${PRJ_NAME}-someip CommonAPI-SomeIP)

Now call CMake and make:

<.>/project$ cmake -Bbuild -DCMAKE_PREFIX_PATH=$(realpath ../install_folder) -DPKG_CONFIG_USE_CMAKE_PREFIX_PATH=ON .
<.>/project$ cmake --build build

Now we are on the verge to start the service and the client application. With earlier vsomeip versions it was necessary to create a configuration file for vsomeip but for simple HelloWorld applications without any external communication this is not necessary. However, by default CommonAPI uses the binding dbus, in order to use someip we need to create the file commonapi4someip.ini where it is possible to define other configurations as the logging options (not mandatory), more detail about this is provided on the next tutorial Loading Bindings And Libraries (D-Bus and SOME/IP).

<.>/project$ vi commonapi4someip.ini
[default]
binding=someip

[logging]
console = true
file = ./mylog.log
dlt = true
level = verbose

Start the Service and the Client in two different terminals and they should communicate with each other using vsomeip.

<.>/project$ COMMONAPI_CONFIG=commonapi4someip.ini LD_LIBRARY_PATH=../install_folder/lib:$PWD/build/ build/HelloWorldService
[23950.507151]~DLT~236605~INFO     ~FIFO /tmp/dlt cannot be opened. Retrying later...
[CAPI][INFO] Loading configuration file 'commonapi4someip.ini'
[CAPI][INFO] Using default binding 'someip'
[CAPI][INFO] Using default shared library folder '/usr/local/lib/commonapi'
[CAPI][DEBUG] Loading library for local:commonapi.examples.HelloWorld:v0_1:commonapi.examples.HelloWorld stub.
[CAPI][INFO] Loading configuration file /etc//commonapi-someip.ini
[CAPI][DEBUG] Added address mapping: local:commonapi.examples.HelloWorld:v0_1:commonapi.examples.HelloWorld <--> [1234.1234(0.1)]
[CAPI][VERBOSE] Registering function for creating "commonapi.examples.HelloWorld:v0_1" proxy.
[CAPI][INFO] Registering function for creating "commonapi.examples.HelloWorld:v0_1" stub adapter.
[CAPI][DEBUG] Loading interface library "libHelloWorld-someip.so" succeeded.
[CAPI][INFO] Registering stub for "local:commonapi.examples.HelloWorld:v0_1:commonapi.examples.HelloWorld"
2024-01-15 15:19:37.859585 [info] Parsed vsomeip configuration in 0ms
2024-01-15 15:19:37.860096 [info] Configuration module loaded.
2024-01-15 15:19:37.860236 [info] Security disabled!
2024-01-15 15:19:37.860327 [info] Initializing vsomeip (3.4.10) application "service-sample".
2024-01-15 15:19:37.860947 [info] Instantiating routing manager [Host].
2024-01-15 15:19:37.862274 [info] create_routing_root: Routing root @ /tmp/vsomeip-0
2024-01-15 15:19:37.863333 [info] Service Discovery enabled. Trying to load module.
2024-01-15 15:19:37.866415 [info] Service Discovery module loaded.
2024-01-15 15:19:37.866855 [info] vsomeip tracing not enabled. . vsomeip service discovery tracing not enabled. 
2024-01-15 15:19:37.866967 [info] Application(service-sample, 1000) is initialized (11, 100).
2024-01-15 15:19:37.868205 [info] Starting vsomeip application "service-sample" (1000) using 2 threads I/O nice 255
2024-01-15 15:19:37.869827 [info] Client [1000] routes unicast:127.0.0.1, netmask:255.255.255.0
2024-01-15 15:19:37.869839 [info] shutdown thread id from application: 1000 (service-sample) is: 7fabd1aa0640 TID: 236612
2024-01-15 15:19:37.869587 [info] main dispatch thread id from application: 1000 (service-sample) is: 7fabd22a1640 TID: 236611
2024-01-15 15:19:37.871275 [info] create_local_server: Listening @ /tmp/vsomeip-1000
2024-01-15 15:19:37.871338 [info] Watchdog is disabled!
2024-01-15 15:19:37.871649 [info] OFFER(1000): [1234.1234:0.1] (true)
Successfully Registered Service!
Waiting for calls... (Abort with CTRL+C)
2024-01-15 15:19:37.872233 [info] io thread id from application: 1000 (service-sample) is: 7fabd2aa2640 TID: 236610
2024-01-15 15:19:37.872284 [info] io thread id from application: 1000 (service-sample) is: 7fabd0a9e640 TID: 236614
2024-01-15 15:19:37.873185 [info] vSomeIP 3.4.10 | (default)
2024-01-15 15:19:37.873665 [info] Network interface "lo" state changed: up
2024-01-15 15:19:47.873624 [info] vSomeIP 3.4.10 | (default)
2024-01-15 15:19:48.958002 [info] Application/Client 1001 is registering.
2024-01-15 15:19:48.958866 [info] Client [1000] is connecting to [1001] at /tmp/vsomeip-1001
2024-01-15 15:19:48.965954 [info] REGISTERED_ACK(1001)
2024-01-15 15:19:48.047561 [info] REQUEST(1001): [1234.1234:0.4294967295]
sayHello('World'): 'Hello World!'
sayHello('World'): 'Hello World!'
<.>/project$ COMMONAPI_CONFIG=commonapi4someip.ini LD_LIBRARY_PATH=../install_folder/lib:$PWD/build/  build/HelloWorldClient
[23961.712646]~DLT~236711~INFO     ~FIFO /tmp/dlt cannot be opened. Retrying later...
[CAPI][INFO] Loading configuration file 'commonapi4someip.ini'
[CAPI][INFO] Using default binding 'someip'
[CAPI][INFO] Using default shared library folder '/usr/local/lib/commonapi'
[CAPI][DEBUG] Loading library for local:commonapi.examples.HelloWorld:v0_1:commonapi.examples.HelloWorld proxy.
[CAPI][INFO] Loading configuration file /etc//commonapi-someip.ini
[CAPI][DEBUG] Added address mapping: local:commonapi.examples.HelloWorld:v0_1:commonapi.examples.HelloWorld <--> [1234.1234(0.1)]
[CAPI][VERBOSE] Registering function for creating "commonapi.examples.HelloWorld:v0_1" proxy.
[CAPI][INFO] Registering function for creating "commonapi.examples.HelloWorld:v0_1" stub adapter.
[CAPI][DEBUG] Loading interface library "libHelloWorld-someip.so" succeeded.
[CAPI][VERBOSE] Creating proxy for "local:commonapi.examples.HelloWorld:v0_1:commonapi.examples.HelloWorld"
2024-01-15 15:19:48.930730 [info] Parsed vsomeip configuration in 0ms
2024-01-15 15:19:48.932947 [info] Configuration module loaded.
2024-01-15 15:19:48.933987 [info] Security disabled!
2024-01-15 15:19:48.934975 [info] Initializing vsomeip (3.4.10) application "client-sample".
2024-01-15 15:19:48.936279 [info] Instantiating routing manager [Proxy].
2024-01-15 15:19:48.937839 [info] Client [ffff] is connecting to [0] at /tmp/vsomeip-0
2024-01-15 15:19:48.943738 [info] vsomeip tracing not enabled. . vsomeip service discovery tracing not enabled. 
2024-01-15 15:19:48.943958 [info] Application(client-sample, ffff) is initialized (11, 100).
Checking availability!
2024-01-15 15:19:48.947412 [info] Starting vsomeip application "client-sample" (ffff) using 2 threads I/O nice 255
2024-01-15 15:19:48.950385 [info] io thread id from application: ffff (client-sample) is: 7f8b4b048640 TID: 236717
2024-01-15 15:19:48.950519 [info] io thread id from application: ffff (client-sample) is: 7f8b49845640 TID: 236720
2024-01-15 15:19:48.951232 [info] shutdown thread id from application: ffff (client-sample) is: 7f8b4a046640 TID: 236719
2024-01-15 15:19:48.951872 [info] main dispatch thread id from application: ffff (client-sample) is: 7f8b4a847640 TID: 236718
2024-01-15 15:19:48.955628 [info] create_local_server: Listening @ /tmp/vsomeip-1001
2024-01-15 15:19:48.956241 [info] Client 1001 (client-sample) successfully connected to routing  ~> registering..
2024-01-15 15:19:48.956510 [info] Registering to routing manager @ vsomeip-0
2024-01-15 15:19:48.963835 [info] Application/Client 1001 (client-sample) is registered.
2024-01-15 15:19:48.048616 [info] ON_AVAILABLE(1001): [1234.1234:0.1]
Available...
2024-01-15 15:19:48.049840 [info] Client [1001] is connecting to [1000] at /tmp/vsomeip-1000
[CAPI][DEBUG] Message sent: SenderID: 1234 - ClientID: 4097, SessionID: 1
Got message: 'Hello World!'
[CAPI][DEBUG] Message sent: SenderID: 1234 - ClientID: 4097, SessionID: 2
Got message: 'Hello World!'