diff options
author | Jürgen Gehring <Juergen.Gehring@bmw.de> | 2016-10-11 05:20:33 -0700 |
---|---|---|
committer | Jürgen Gehring <Juergen.Gehring@bmw.de> | 2016-10-11 05:20:33 -0700 |
commit | 1375432503c0a72df7ad5c793c3e1f04e6b9e730 (patch) | |
tree | 4b229a9c5a7767db69db015f8e92e01c64614bf0 | |
parent | 273814c76be4a8f906dc053492529b8d53b9e807 (diff) | |
download | vSomeIP-1375432503c0a72df7ad5c793c3e1f04e6b9e730.tar.gz |
vsomeip 2.4.22.4.2
146 files changed, 9097 insertions, 2655 deletions
@@ -1,7 +1,10 @@ +/CMakeFiles /build*/* /examples/hello_world/build /.settings /doc +/daemon/CMakeFiles +/examples/CMakeFiles /implementation/configuration/include/internal.hpp /test/big_payload_tests/big_payload_test_tcp_client.json /test/big_payload_tests/big_payload_test_tcp_service.json @@ -34,7 +37,27 @@ /test/subscribe_notify_tests/subscribe_notify_test_same_client_ids_same_ports_slave.json /test/subscribe_notify_one_tests/subscribe_notify_one_test_diff_client_ids_diff_ports_master.json /test/subscribe_notify_one_tests/subscribe_notify_one_test_diff_client_ids_diff_ports_slave.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_master.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_slave.json /test/cpu_load_tests/cpu_load_test_client_slave.json /test/cpu_load_tests/cpu_load_test_client_master.json /test/cpu_load_tests/cpu_load_test_service_slave.json /test/cpu_load_tests/cpu_load_test_service_master.json +/tools/CMakeFiles +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_master.json +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_same_service_id_master.json +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_same_service_id_slave.json +/test/initial_event_tests/initial_event_test_diff_client_ids_diff_ports_slave.json +/test/initial_event_tests/initial_event_test_diff_client_ids_partial_same_ports_master.json +/test/initial_event_tests/initial_event_test_diff_client_ids_partial_same_ports_slave.json +/test/initial_event_tests/initial_event_test_diff_client_ids_same_ports_master.json +/test/initial_event_tests/initial_event_test_diff_client_ids_same_ports_slave.json +/test/initial_event_tests/initial_event_test_same_client_ids_diff_ports_master.json +/test/initial_event_tests/initial_event_test_same_client_ids_diff_ports_slave.json +/test/initial_event_tests/initial_event_test_same_client_ids_same_ports_master.json +/test/initial_event_tests/initial_event_test_same_client_ids_same_ports_slave.json +/test/offer_tests/offer_test_external_master.json +/test/offer_tests/offer_test_external_slave.json +/test/offer_tests/offer_test_external_master_starter.sh +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_master.json +/test/subscribe_notify_tests/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_slave.json @@ -5,20 +5,27 @@ v1.1.0 - Local communication in multiprocessor environments was fixed - Runtime access was changed from raw to shared pointer - vsomeip logger is used whereever possible (replacing std::cerr calls) -- Ensure the logger is not deleted before issueing tha last log message when shutting down -- Fixed shutdown crash by checking the existence of endpoint host before accessing it -- Routing info processing in case of multiple instances of the same service was fixed +- Ensure the logger is not deleted before issueing tha last log message when + shutting down +- Fixed shutdown crash by checking the existence of endpoint host before + accessing it +- Routing info processing in case of multiple instances of the same service + was fixed - Support for local communication on Windows was added v1.2.0 - Added (optional) thread pool for distribution of messages to the application -- Made configuration of service groups optional (as it is unneeded in pure client applications) -- Support specification of transportation mode (reliable (TCP) / unreliable (UDP)) when creating messages +- Made configuration of service groups optional (as it is unneeded in pure + client applications) +- Support specification of transportation mode (reliable (TCP) / unreliable + (UDP)) when creating messages - Fixed internal distribution of notication events - Block messages that are received on the wrong port - Fixed deregistration of local clients -- Fixed startup of applications that were started earlier than the routing manager -- Resetting all events of a service if it becomes unavailable (to ensure initial events are sent when it becomes available again) +- Fixed startup of applications that were started earlier than the routing + manager +- Resetting all events of a service if it becomes unavailable (to ensure initial + events are sent when it becomes available again) - Ensure consistency of version information - Fixed Service Discovery state machine @@ -41,17 +48,21 @@ v2.0.0 - Added support for IPv6 multicast - Improved handling of endpoints - Report service state changes instead of service state to the application -- Set and process TTL field in Service Discovery to support detection of "lost" services +- Set and process TTL field in Service Discovery to support detection of "lost" + services - Support automatic configuration of local communication -- Added compile time variable DIAGNOSIS_ADDRESS (which maps to the high byte of the SOME/IP client identifier) +- Added compile time variable DIAGNOSIS_ADDRESS (which maps to the high byte of + the SOME/IP client identifier) - Configuration of events was moved from configuration file to API. - Fixed routing of notication events. - Increased robustness of configuration loader - Changed default watchdog cycle from 1s to 5s - Removed TTL arguments from public interface -- Allow Service Discovery to report non-SOME/IP services by setting the configuration variable "protocol" +- Allow Service Discovery to report non-SOME/IP services by setting the + configuration variable "protocol" - Fixed serialization of major version in Eventgroup entries -- Magic Cookies are no longer forwarded to the routing manager but handled in the receiving endpoint +- Magic Cookies are no longer forwarded to the routing manager but handled in + the receiving endpoint - vsomeip daemon was added v2.0.1 @@ -78,7 +89,8 @@ v2.0.3 v2.0.4 - Service Discovery now used configured Client ID prefixes (=DIAGNOSIS_ADDRESS) - Reworked reboot detection (now based on the destination address) -- Aligned default TTL setting (was 5 in vsomeip and 0xFFFFFF in vsomeip-sd, now its constently 0xFFFFFF) +- Aligned default TTL setting (was 5 in vsomeip and 0xFFFFFF in vsomeip-sd, now + its constently 0xFFFFFF) v2.0.5 - Fixed reboot detection behavior @@ -87,15 +99,18 @@ v2.0.6 - Diagnosis address can be configured at runtime v2.1.0 -- Avoid duplicate notifications if a selective event is in more than one eventgroup +- Avoid duplicate notifications if a selective event is in more than one + eventgroup - Ensure SD messages are sent from the SD port - Ignore SD messages with wrong message identifier -- Accept unreliable subscription for eventgroups without configured multicast address +- Accept unreliable subscription for eventgroups without configured multicast + address - Reject subscriptions that contain invalid IP address or port - Reject subscriptions for TCP if the connection is not established - Exclude vsomeip_ctrl from default installation - Only accept SD messages from SD port -- Acknowledge multiple subscriptions sent within the same message with a single message +- Acknowledge multiple subscriptions sent within the same message with a single + message - Allow to specify an application specific DLT application - Ensure correct ordering of availability notifications - Automatically expire subscription based on the given TTL @@ -119,8 +134,93 @@ v2.2.0 - Implemented Peer-to-Peer data exchange for notifications - Fixed handling of minor version during service discovery - Made initialization of application objects reentrant -- Routing manager proxies now reconnect to the routing manager if the connection got lost +- Routing manager proxies now reconnect to the routing manager if the + connection got lost - Auto-configuration supports multiple (different) configuration files - The opening of TCP connections is no longer done without an explicit request - Request No Respose messages are no longer answered in case of errors - Notifications over IP were fixed + +v2.2.1 +- Backward compatibility fixes + +v2.2.2 +- Ensure multicast messages are sent by the network adapter that is configured + to be used for unicasts instead of relying on the configured routes + +v2.2.3 +- Ensure service discovery messages to not exceed maximum packet size + +v2.2.4 +- Set default log level to DEBUG +- Improved segmentation of service discovery messages +- Fixed a race condition during subscriptions + +v2.3.0 +- Extend the API to force field notifications +- Implemented cyclic updated for events/fields +- Implemented epsilon updates (the used can provide a function to decide + whether or not a value update shall be considered as a change) +- Fixed lifecycle: Wait acknowledge of de/register application +- Periodically log version information +- Avoid (shadow) event registrations for services only offered locally +- Fixed determination of routing manager host in case auto-configuration + fails +- Removed initial flag from internal message format +- Fixed calling of registered message handlers for cases where wildcards + were used during registration. +- Fixed availability reporting of reliable (TCP) remote services offered + on the same port + +v2.3.1 +- Fix shutdown crashes (logger & application shutdown) +- Fix race condition in client identifier configuration +- Fix vsomeipd crash +- Fixed handling of notifications (compliance) + +v2.3.2 +- Fix client deregistration during the client registration +- Fix handling of pending commands during registration + +v2.3.3 +- Added -q/--quiet switch to the daemon to allow it to be started without + DLT logging +- Fix event caching in routing manager + +v2.3.4 +- Exhaust client id range before reuse +- Provide public interface to ask for available instances + +v2.3.5 +- Fix TTL in Subscribe Eventgroup Entries + +v2.4.0 +- Disabled tracing SOME/IP-SD messages by default. Set "tracing/sd_enable" + switch to "true" to enable it. +- Trace notification events once instead of per target. + +v2.4.1 +- Extended number of endpoints that can be referenced from entries array in + service discovery messages +- Remove DLT contexts on application shutdown +- Avoid initialization of vsomeip-applications if the maximum number of + applications (client identifiers) has been reached +- Prevent sending of OfferService entry as a reply to FindService message for + internal services +- Fixed deregistration of vsomeip-applications that became unresponsive +- Fixed loop in endpoints causing high load during shutdown of vsomeip + applications +- Fixed loop in endpoints causing temporary high load if other devices become + unavailable without deregistering + +v2.4.2 +- TCP connections for services no longer requested aren't reestablished anymore +- The minor version of a service instance is no longer considered when reporting + the service instance's availability +- Introduce new internal_services json file parameter to define the internal + service instances. This parameter can be used to control the sending behaviour + for find service entries +- Fixed event processing if service and client shared the same application +- Incoming find service entries with unicast flag set to 0 are now replied with + a unicast offer service message instead of a multicast offer service message. +- application::stop() now blocks until the shutdown has finished completely diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f85a14..2d886cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ cmake_minimum_required (VERSION 2.8.12) project (vsomeip) set (VSOMEIP_MAJOR_VERSION 2) -set (VSOMEIP_MINOR_VERSION 2) -set (VSOMEIP_PATCH_VERSION 4) +set (VSOMEIP_MINOR_VERSION 4) +set (VSOMEIP_PATCH_VERSION 2) set (VSOMEIP_VERSION ${VSOMEIP_MAJOR_VERSION}.${VSOMEIP_MINOR_VERSION}.${VSOMEIP_PATCH_VERSION}) set (PACKAGE_VERSION ${VSOMEIP_VERSION}) # Used in documentatin/doxygen.in set (CMAKE_VERBOSE_MAKEFILE off) @@ -59,9 +59,15 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") set(OS_CXX_FLAGS "-pthread") endif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") +# Signal handling +if (ENABLE_SIGNAL_HANDLING) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVSOMEIP_ENABLE_SIGNAL_HANDLING") +endif () + include_directories( "interface" ) + # Threads find_package(Threads REQUIRED) @@ -89,6 +95,14 @@ IF(DLT_FOUND) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_DLT") ENDIF(DLT_FOUND) +# SystemD +pkg_check_modules(SystemD "libsystemd") + +if(NOT SystemD_FOUND) +MESSAGE( STATUS "Systemd was not found, watchdog disabled!") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITHOUT_SYSTEMD") +endif(NOT SystemD_FOUND) + include_directories( include implementation/helper @@ -110,17 +124,23 @@ file(GLOB vsomeip_SRC "implementation/runtime/src/*.cpp" "implementation/utility/src/*.cpp" ) - -if (MSVC) -SET(BOOST_WINDOWS_VERSION "0x600" CACHE STRING "Set the same Version as the Version with which Boost was built, otherwise there will be errors. (normaly 0x600 is for Windows 7 and 0x501 is for Windows XP)") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_WINSOCK_DEPRECATED_NO_WARNINGS -D_WIN32_WINNT=${BOOST_WINDOWS_VERSION} -DWIN32 -DUSE_VSOMEIP_STATISTICS -DCOMMONAPI_INTERNAL_COMPILATION -DBOOST_LOG_DYN_LINK -DBOOST_ASIO_DISABLE_IOCP /EHsc") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS -D_WIN32_WINNT=${BOOST_WINDOWS_VERSION} -DWIN32 -DUSE_VSOMEIP_STATISTICS -DCOMMONAPI_INTERNAL_COMPILATION -DBOOST_LOG_DYN_LINK -DBOOST_ASIO_DISABLE_IOCP /EHsc") -set(USE_RT "") -set(Boost_LIBRARIES "") -link_directories(${Boost_LIBRARY_DIR}) +list(SORT vsomeip_SRC) + +add_definitions(-DVSOMEIP_VERSION="${VSOMEIP_VERSION}") + +if (MSVC) + message("using MSVC Compiler") + add_definitions(-DVSOMEIP_DLL_COMPILATION) + SET(BOOST_WINDOWS_VERSION "0x600" CACHE STRING "Set the same Version as the Version with which Boost was built, otherwise there will be errors. (normaly 0x600 is for Windows 7 and 0x501 is for Windows XP)") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_WINSOCK_DEPRECATED_NO_WARNINGS -D_WIN32_WINNT=${BOOST_WINDOWS_VERSION} -DWIN32 -DUSE_VSOMEIP_STATISTICS -DCOMMONAPI_INTERNAL_COMPILATION -DBOOST_LOG_DYN_LINK -DBOOST_ASIO_DISABLE_IOCP /EHsc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS -D_WINSOCK_DEPRECATED_NO_WARNINGS -D_WIN32_WINNT=${BOOST_WINDOWS_VERSION} -DWIN32 -DUSE_VSOMEIP_STATISTICS -DCOMMONAPI_INTERNAL_COMPILATION -DBOOST_LOG_DYN_LINK -DBOOST_ASIO_DISABLE_IOCP /EHsc") + set(USE_RT "") + set(Boost_LIBRARIES "") + link_directories(${Boost_LIBRARY_DIR_DEBUG}) + ADD_DEFINITIONS( -DBOOST_ALL_DYN_LINK ) else() -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${OS} ${OS_CXX_FLAGS} -DUSE_VSOMEIP_STATISTICS -DBOOST_LOG_DYN_LINK -g ${OPTIMIZE} -std=c++0x ${NO_DEPRECATED} ${EXPORTSYMBOLS}") -set(USE_RT "rt") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${OS} ${OS_CXX_FLAGS} -DUSE_VSOMEIP_STATISTICS -DBOOST_LOG_DYN_LINK -g ${OPTIMIZE} -std=c++0x ${NO_DEPRECATED} ${EXPORTSYMBOLS}") + set(USE_RT "rt") endif() add_library(vsomeip SHARED ${vsomeip_SRC}) @@ -130,15 +150,16 @@ set_target_properties (vsomeip PROPERTIES VERSION ${VSOMEIP_VERSION} SOVERSION $ # them (which shouldn't be required). ${Boost_LIBRARIES} includes absolute # build host paths as of writing, which also makes this important as it breaks # the build. -target_link_libraries(vsomeip PRIVATE ${Boost_LIBRARIES} ${USE_RT} ${DL_LIBRARY} ${DLT_LIBRARIES}) +target_link_libraries(vsomeip PRIVATE ${Boost_LIBRARIES} ${USE_RT} ${DL_LIBRARY} ${DLT_LIBRARIES} ${SystemD_LIBRARIES} ) file(GLOB vsomeip-sd_SRC "implementation/service_discovery/src/*.cpp" ) +list(SORT vsomeip-sd_SRC) add_library(vsomeip-sd SHARED ${vsomeip-sd_SRC}) set_target_properties (vsomeip-sd PROPERTIES VERSION ${VSOMEIP_VERSION} SOVERSION ${VSOMEIP_MAJOR_VERSION}) -target_link_libraries(vsomeip-sd vsomeip ${Boost_LIBRARIES} ${USE_RT} ${DL_LIBRARY}) +target_link_libraries(vsomeip-sd vsomeip ${Boost_LIBRARIES} ${USE_RT} ${DL_LIBRARY} ${SystemD_LIBRARIES}) # Configuration files @@ -168,12 +189,12 @@ message("Predefined unicast address: ${VSOMEIP_UNICAST_ADDRESS}") message("Predefined diagnosis address: ${VSOMEIP_DIAGNOSIS_ADDRESS}") message("Predefined routing application: ${VSOMEIP_ROUTING}") - ################################################################################################### set(INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/interface/vsomeip") file (GLOB_RECURSE vsomeip_INCLUDE RELATIVE ${INCLUDE_PATH} "interface/*.hpp" ) +list (SORT vsomeip_INCLUDE) foreach ( file ${vsomeip_INCLUDE} ) get_filename_component( dir ${file} DIRECTORY ) @@ -184,8 +205,9 @@ install ( TARGETS vsomeip # IMPORTANT: Add the vsomeip library to the "export-set" EXPORT vsomeipTargets - RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin - LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT dev ) @@ -96,6 +96,17 @@ cmake -DDIAGNOSIS_ADDRESS=<YOUR DIAGNOSIS ADDRESS> .. ---- The diagnosis address is a single byte value. +Compilation with signal handling +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To compile vsomeip with signal handling (SIGINT/SIGTERM) enabled, +call cmake like: +[source,bash] +---- +cmake -DENABLE_SIGNAL_HANDLING=1 .. +---- +In the default setting, the application has to take care of shutting +down vsomeip in case these signals are received. + Compilation of examples ^^^^^^^^^^^^^^^^^^^^^^^ For compilation of the examples call: @@ -289,9 +300,15 @@ _true, false_). + ** 'enable' + -Specifies whether the tracing of the internal messages is enabled -(valid values: _true, false_). If tracing is enabled, the messages will be -forwarded to DLT by the <<traceconnector, Trace Connector>> +Specifies whether the tracing of the SOME/IP messages is enabled +(valid values: _true, false_). Default value is _false_. +If tracing is enabled, the messages will be forwarded to DLT by +the <<traceconnector, Trace Connector>> ++ +** 'sd_enable' ++ +Specifies whether the tracing of the SOME/IP service discovery messages is +enabled (valid values: _true, false_). Default value is _false_. + ** 'channels (array)' (optional) + @@ -456,6 +473,15 @@ The multicast address. + The multicast port. +*** `threshold` ++ +Specifies when to use multicast and when to use unicast to send a notification event. +Must be set to a non-negative number. If it is set to zero, all events of the eventgroup +will be sent by unicast. Otherwise, the events will be sent by unicast as long as the +number of subscribers is lower than the threshold and by multicast if the number +of subscribers is greater or equal. This means, a threshold of 1 will lead to all events +being sent by multicast. The default value is _0_. + * `clients` (array) + The client-side ports that shall be used to connect to a specific service. @@ -519,6 +545,40 @@ service offered on previously specified IP and port. If multiple services are hosted on the same port all of them are allowed to receive oversized messages and send oversized responses. +* `internal_services` (optional array) ++ +Specifies service/instance ranges for pure internal service-instances. +This information is used by vSomeIP to avoid sending Find-Service messages +via the Service-Discovery when a client is requesting a not available service- +instance. Its can either be done on service/instance level or on service level +only which then includes all instance from 0x0000-0xffff. + +** `first` ++ +The lowest entry of the internal service range. + +*** `service` ++ +The lowest Service-ID in hex of the internal service range. + +*** `instance` (optional) ++ +The lowest Instance-ID in hex of a internal service-instance range. +If not specified the lowest Instance-ID is 0x0000. + +** `last` ++ +The highest entry of the internal service range. + +*** `service` ++ +The highest Service-ID in hex of a internal service range. + +*** `instance` (optional) ++ +The highest Instance-ID in hex of a internal service-instance range. +If not specified the highest Instance-ID is 0xFFFF. + * `routing` + The name of the application that is responsible for the routing. @@ -535,11 +595,11 @@ _false_). The default value is _true_. ** `multicast` + The multicast address which the messages of the Service Discovery will be sent -to. The default value is "224.0.0.1". +to. The default value is _224.0.0.1_. ** `port` + -The port of the Service Discovery. The default setting is 30490. +The port of the Service Discovery. The default setting is _30490_. ** `protocol` + diff --git a/README.md b/README.md deleted file mode 100755 index 8864fec..0000000 --- a/README.md +++ /dev/null @@ -1,751 +0,0 @@ -# vSOMEIP - -Copyright -+++++++++ -Copyright (C) 2015, Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - -License -+++++++ -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/. - -Version -+++++++ -// set the version to the one we get from cmake -// or pass it via -a version=$VSOMEIP_VERSION to asciidoc -This documentation was generated for version {version} of vsomeip. - -vsomeip Overview ----------------- -The vsomeip stack implements the http://some-ip.com/[Scalable service-Oriented -MiddlewarE over IP (SOME/IP)] protocol. The stack consists out of: - -* a shared library for SOME/IP (`libvsomeip.so`) -* a second shared library for SOME/IP's service discovery (`libvsomeip-sd.so`) - which is loaded during runtime if the service discovery is enabled. - -Build Instructions ------------------- -Dependencies -~~~~~~~~~~~~ -* A C++11 enabled compiler like gcc >= 4.8 is needed. -* vsomeip uses cmake as buildsystem. -* vsomeip uses Boost >= 1.54: -** Ubuntu 14.04: -*** `sudo apt-get install libboost-system1.54-dev libboost-thread1.54-dev - libboost-log1.54-dev` -** Ubuntu 12.04: a PPA is necessary to use version 1.54 of Boost: -*** URL: https://launchpad.net/~boost-latest/+archive/ubuntu/ppa -*** `sudo add-apt-repository ppa:boost-latest/ppa` -*** `sudo apt-get install libboost-system1.54-dev libboost-thread1.54-dev - libboost-log1.54-dev` -* For the tests Google's test framework - https://code.google.com/p/googletest/[gtest] in version 1.7.0 is needed -** URL: https://googletest.googlecode.com/files/gtest-1.7.0.zip[direct link, - version 1.7.0] -* To build the documentation asciidoc, source-highlight, doxygen and graphviz is needed: -** `sudo apt-get install asciidoc source-highlight doxygen graphviz` - -Compilation -~~~~~~~~~~~ -anchor:Compilation[] -For compilation call: -[source, bash] ----- -mkdir build -cd build -cmake .. -make ----- - -To specify a installation directory (like `--prefix=` if you're used to -autotools) call cmake like: -[source, bash] ----- -cmake -DCMAKE_INSTALL_PREFIX:PATH=$YOUR_PATH .. -make -make install ----- - -Compilation of examples -^^^^^^^^^^^^^^^^^^^^^^^ -For compilation of the examples call: -[source, bash] ----- -mkdir build -cd build -cmake .. -make examples ----- - -Compilation of tests -^^^^^^^^^^^^^^^^^^^^ -To compile the tests, first unzip gtest to location of your desire. -Some of the tests require a second node on the same network. There are two cmake -variables which are used to automatically adapt the json files to the used -network setup: - -* `TEST_IP_MASTER`: The IP address of the interface which will act as test - master. -* `TEST_IP_SLAVE`: The IP address of the interface of the second node which will - act as test slave. - -If one of this variables isn't specified, only the tests using local -communication exclusively will be runnable. - -Example, compilation of tests: -[source, bash] ----- -mkdir build -cd build -export GTEST_ROOT=$PATH_TO_GTEST/gtest-1.7.0/ -cmake -DTEST_IP_MASTER=10.0.3.1 -DTEST_IP_SLAVE=10.0.3.125 .. -make check ----- - -Additional make targets for the tests: - -* Call `make build_tests` to only compile the tests -* Call `ctest` in the build directory to execute the tests without a verbose - output -* To run single tests call `ctest --verbose --tests-regex $TESTNAME` short - form: `ctest -V -R $TESTNAME` -* To list all available tests run `ctest -N`. -* For further information about the tests please have a look at the - `readme.txt` in the `test` subdirectory. - -For development purposes two cmake variables exist which control if the -json files and test scripts are copied (default) or symlinked into the build -directory. These settings are ignored on Windows. - -* `TEST_SYMLINK_CONFIG_FILES`: Controls if the json and scripts needed - to run the tests are copied or symlinked into the build directory. (Default: - OFF, ignored on Windows) -* `TEST_SYMLINK_CONFIG_FILES_RELATIVE`: Controls if the json and scripts needed - to run the tests are symlinked relatively into the build directory. - (Default: OFF, ignored on Windows) - -Example cmake call: -[source, bash] ----- -cmake -DTEST_SYMLINK_CONFIG_FILES=ON -DTEST_SYMLINK_CONFIG_FILES_RELATIVE=ON .. ----- - -For compilation of only a subset of tests (for a quick -functionality check) the cmake variable `TESTS_BAT` has -to be set: - -Example cmake call: -[source, bash] ----- -cmake -DTESTS_BAT=ON .. ----- - -Generating the documentation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To generate the documentation call cmake as described in <<Compilation>> and -then call `make doc`. -This will generate: - -* The README file in html: `$BUILDDIR/documentation/README.html` -* A doxygen documentation in `$BUILDDIR/documentation/html/index.html` - -Starting vsomeip Applications / Used environment variables ----------------------------------------------------------- -On startup the following environment variables are read out: - -* `VSOMEIP_APPLICATION_NAME`: This environment variable is used to specify the - name of the application. This name is later used to map a client id to the - application in the configuration file. It is independent from the - application's binary name. -* `VSOMEIP_CONFIGURATION`: vsomeip uses the default configuration file `/etc/vsomeip.json` - and/or the default configuration folder `/etc/vsomeip`. This can be overridden by a - local configuration file `./vsomeip.json` and/or a local configuration folder `./vsomeip`. - If `VSOMEIP_CONFIGURATION` is set to a valid file or directory path, this is used instead - of the standard configuration (thus neither default nor local file/folder will be parsed). - -NOTE: If the file/folder that is configured by `VSOMEIP_CONFIGURATION` does _not_ exist, -the default configuration locations will be used. - -NOTE: vsomeip will parse and use the configuration from all files in a configuration folder -but will _not_ consider directories within the configuration folder. - -In the following example the application `my_vsomeip_application` is started. -The settings are read from the file `my_settings.json` in the current working -directory. The client id for the application can be found under the name -`my_vsomeip_client` in the configuration file. - -[source, bash] ----- -#!/bin/bash -export VSOMEIP_APPLICATION_NAME=my_vsomeip_client -export VSOMEIP_CONFIGURATION=my_settings.json -./my_vsomeip_application ----- - -Configuration File Structure ----------------------------- -The configuration files for vsomeip are http://www.json.org/[JSON]-Files and are -composed out of multiple key value pairs and arrays. - -[quote, , json.org] -____ -* An object is an unordered set of name/value pairs. An object begins with `{ -(left brace)` and ends with `} (right brace)`. Each name is followed by `: -(colon)` and the name/value pairs are separated by `, (comma)`. - -* An array is an ordered collection of values. An array begins with `[ (left -bracket)` and ends with `] (right bracket)`. Values are separated by `, -(comma)`. - -* A value can be a _string_ in double quotes, or a _number_, or `true` or `false` -or `null`, or an _object_ or an _array_. These structures can be nested. -____ - -Configuration file element explanation: - - -* 'unicast' -+ -The IP address of the host system. -+ -* 'netmask' -+ -The netmask to specify the subnet of the host system. -+ -//Logging -* 'logging' -+ -** 'level' -+ -Specifies the log level (valid values: _trace_, _debug_, _info_, _warning_, -_error_, _fatal_). -+ -** 'console' -+ -Specifies whether logging via console is enabled (valid values: _true, false_). -+ -** 'file' -+ -*** 'enable' -+ -Specifies whether a log file should be created (valid values: _true, false_). -+ -*** 'path' -+ -The absolute path of the log file. -+ -** 'dlt' -+ -Specifies whether Diagnostic Log and Trace (DLT) is enabled (valid values: -_true, false_). -+ -//Applications -* 'applications (array)' -+ -Contains the applications of the host system that use this config file. -+ -** 'name' -+ -The name of the application. -+ -** 'id' -+ -The id of the application. -+ -** 'num_dispatchers' -+ -The number of threads that shall be used to execute the callbacks to the application. -If 'num_dispatchers' is set to '0', the callbacks will be executed within the -application thread. If an application wants/must do time consuming work directly -within event, availability or message callbacks, 'num_dispatchers' should be set -to '2' or higher. -+ -** `services` (array) -+ -Contains the services of the service provider. - -*** `service` -+ -The id of the service. - -*** `instance` -+ -The id of the service instance. - -*** `protocol` (optional) -+ -The protocol that is used to implement the service instance. The default setting -is _someip_. If a different setting is provided, vsomeip does not open the specified -port (server side) or does not connect to the specified port (client side). Thus, -this option can be used to let the service discovery announce a service that is -externally implemented. - -*** `unicast` (optional) -+ -The unicast that hosts the service instance. -+ -NOTE: The unicast address is needed if external service instances shall be used, -but service discovery is disabled. In this case, the provided unicast address -is used to access the service instance. - -*** `reliable` -+ -Specifies that the communication with the service is reliable respectively the -TCP protocol is used for communication. - -**** `port` -+ -The port of the TCP endpoint. - -**** `enable-magic-cookies` -+ -Specifies whether magic cookies are enabled (valid values: _true_, _false_). - -*** `unreliable` -+ -Specifies that the communication with the service is unreliable respectively the -UDP protocol is used for communication (valid values: the _port_ of the UDP -endpoint). - -*** `multicast` -+ -A service can be offered to a specific group of clients via multicast. - -**** `address` -+ -The specific multicast address. - -**** `port` -+ -The specific port. - -*** `events` (array) -+ -Contains the events of the service. - -**** `event` -+ -The id of the event. - -***** `is_field` -+ -Specifies whether the event is of type field. -+ -NOTE: A field is a combination of getter, setter and notification event. It -contains at least a getter, a setter, or a notifier. The notifier sends an event -message that transports the current value of a field on change. - -***** `is_reliable` -+ -Specifies whether the communication is reliable respectively whether the event -is sent with the TCP protocol (valid values: _true_,_false_). -+ -If the value is _false_ the UDP protocol will be used. - -*** `eventgroups` (array) -+ -Events can be grouped together into on event group. For a client it is thus -possible to subscribe for an event group and to receive the appropriate events -within the group. - -**** `eventgroup` -+ -The id of the event group. - -**** `events` (array) -+ -Contains the ids of the appropriate events. - -**** `is_multicast` -+ -Specifies whether the events should be sent via multicast (valid values: -_true_,_false_). - -**** `multicast` -+ -The multicast address which the events are sent to. - -* `payload-sizes` (array) -+ -Array to specify the maximum allowed payload sizes per IP and port. If not -specified, or a smaller value than the default values is specified, the default -values are used. The settings in this array only affect communication over TCP -and local communication over UNIX domain sockets. - -** `unicast` -+ -On client side: the IP of the remote service to which the oversized messages -should be sent. -On service side: the IP of the offered service which should receive the -oversized messages and is allowed to respond with oversized messages. -If client and service only communicate locally, any IP can be entered here as -for local communication only the maximum specified payload size is relevant. - -** `ports` (array) -+ -Array which holds pairs of port and payload-size statements. - -*** `port` -+ -On client side: the port of the remote service to which the oversized messages -should be sent. -On service side: the port of the offered service which should receive the -oversized messages and is allowed to respond with oversized messages. -If client and service only communicate locally, any port number can be entered. - -*** `max-payload-size` -+ -On client side: the maximum payload size in bytes of a message sent to the -remote service hosted on beforehand specified IP and port. -On service side: the maximum payload size in bytes of messages received by the -service offered on previously specified IP and port. If multiple services are -hosted on the same port all of them are allowed to receive oversized messages -and send oversized responses. - -* `routing` -+ -The name of the application that is responsible for the routing. - -* `service-discovery` -+ -Contains settings related to the Service Discovery of the host application. - -** `enable` -+ -Specifies whether the Service Discovery is enabled (valid values: _true_, -_false_). The default value is _true_. - -** `multicast` -+ -The multicast address which the messages of the Service Discovery will be sent -to. The default value is "224.0.0.1". - -** `port` -+ -The port of the Service Discovery. The default setting is 30490. - -** `protocol` -+ -The protocol that is used for sending the Service Discovery messages (valid -values: _tcp_, _udp_). The default setting is _udp_. - -** `initial_delay_min` -+ -Minimum delay before first offer message. - -** `initial_delay_max` -+ -Maximum delay before first offer message. - -** `repetitions_base_delay` -+ -Base delay sending offer messages within the repetition phase. - -** `repetitions_max` -+ -Maximum number of repetitions for provided services within the -repetition phase. - -** `ttl` -+ -Lifetime of entries for provided services as well as consumed services and eventgroups. - -** `cyclic_offer_delay` -+ -Cycle of the OfferService messages in the main phase. - -** `request_response_delay` -+ -Minimum delay of a unicast message to a multicast message for -provided services and eventgroups. - -Autoconfiguration ------------------ -vsomeip supports the automatic configuration of client identifiers and the routing. -The first application that starts using vsomeip will automatically become the -routing manager if it is _not_ explicitly configured. The client identifiers -are generated from the diagnosis address that can be specified by defining -DIAGNOSIS_ADDRESS when compiling vsomeip. vsomeip will use the diagnosis address -as the high byte and enumerate the connecting applications within the low byte -of the client identifier. - -vsomeipd --------- -The vsomeipd is a minimal vsomeip application intended to offer routing manager -functionality on a node where one system wide configuration file is present. - -The vsomeipd uses the application name `vsomeipd` by default. This name can be -overridden by specifying `-DROUTING=$DESIRED_NAME` during the cmake call. - -Example: Starting the daemon on a system where the system wide configuration is -stored under `/etc/vsomeip.json`: -[source, bash] ----- -VSOMEIP_CONFIGURATION=/etc/vsomeip.json ./vsomeipd ----- - -When using the daemon it should be ensured that: - -* In the system wide configuration file the vsomeipd is defined as - routing manager, meaning it contains the line `"routing" : "vsomeipd"`. - If the default name is overridden the entry has to be adapted accordingly. - The system wide configuration file should contain the information about all - other offered services on the system as well. -* There's no other vsomeip configuration file used on the system which contains - a `"routing"` entry. As there can only be one routing manager per system. - - -vsomeip Hello World -------------------- -In this paragraph a Hello World program consisting out of a client and a service -is developed. The client sends a message containing a string to the service. -The service appends the received string to the string `Hello` and sends it back -to the client. -Upon receiving a response from the service the client prints the payload of the -response ("Hello World"). -This example is intended to be run on the same host. - -All files listed here are contained in the `examples\hello_world` subdirectory. - -Build instructions -~~~~~~~~~~~~~~~~~~ -The example can build with its own CMakeFile, please compile the vsomeip stack -before hand as described in <<Compilation>>. Then compile the example starting -from the repository root directory as followed: -[source, bash] ----- -cd examples/hello_world -mkdir build -cd build -cmake .. -make ----- - -Starting and expected output -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Starting and expected output of service -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -[source, bash] ----- -$ VSOMEIP_CONFIGURATION=../helloworld-local.json \ - VSOMEIP_APPLICATION_NAME=hello_world_service \ - ./hello_world_service -2015-04-01 11:31:13.248437 [info] Using configuration file: ../helloworld-local.json -2015-04-01 11:31:13.248766 [debug] Routing endpoint at /tmp/vsomeip-0 -2015-04-01 11:31:13.248913 [info] Service Discovery disabled. Using static routing information. -2015-04-01 11:31:13.248979 [debug] Application(hello_world_service, 4444) is initialized. -2015-04-01 11:31:22.705010 [debug] Application/Client 5555 got registered! ----- - -Starting and expected output of client -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -[source, bash] ----- -$ VSOMEIP_CONFIGURATION=../helloworld-local.json \ - VSOMEIP_APPLICATION_NAME=hello_world_client \ - ./hello_world_client -2015-04-01 11:31:22.704166 [info] Using configuration file: ../helloworld-local.json -2015-04-01 11:31:22.704417 [debug] Connecting to [0] at /tmp/vsomeip-0 -2015-04-01 11:31:22.704630 [debug] Listening at /tmp/vsomeip-5555 -2015-04-01 11:31:22.704680 [debug] Application(hello_world_client, 5555) is initialized. -Sending: World -Received: Hello World ----- - -CMakeFile -~~~~~~~~~ - -[source, bash] ----- -include::examples/hello_world/CMakeLists.txt[] ----- - -Configuration File For Client and Service -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -[source, bash] ----- -include::examples/hello_world/helloworld-local.json[] ----- - -Service -~~~~~~~ - -[source, bash] ----- -include::examples/hello_world/hello_world_service.cpp[] ----- - -The service example results in the following program execution: - -:numbered!: - -[float] - -Main -^^^^^ - -. __main()__ -+ -First the application is initialized. After the initialization is -finished the application is started. - -[float] -Initialization -^^^^^^^^^^^^^^ - -[start=2] -. __init()__ -+ -The initialization contains the registration of a message -handler and an event handler. -+ -The message handler declares a callback (__on_message_cbk__) for messages that -are sent to the specific service (specifying the service id, the service -instance id and the service method id). -+ -The event handler declares a callback (__on_event_cbk__) for events that occur. -One event can be the successful registration of the application at the runtime. - -[float] -Start -^^^^^ - -[start=3] -. __start()__ -+ -The application will be started. This function only returns when the application -will be stopped. - -[float] -Callbacks -^^^^^^^^^ - -[start=4] -. __on_state_cbk()__ -+ -This function is called by the application when an state change occurred. If -the application was successfully registered at the runtime then the specific -service is offered. - -. __on_message_cbk()__ -+ -This function is called when a message/request from a client for the specified -service was received. -+ -First a response based upon the request is created. -Afterwards the string 'Hello' will be concatenated with the payload of the -client's request. -After that the payload of the response is created. The payload data is set with -the previously concatenated string. -Finally the response is sent back to the client and the application is stopped. - -[float] -Stop -^^^^ - -[start=6] -. __stop()__ -+ -This function stops offering the service, unregister the message and the event -handler and shuts down the application. - -:numbered: - -Client -~~~~~~ -[source, bash] ----- -include::examples/hello_world/hello_world_client.cpp[] ----- - -The client example results in the following program execution: - -:numbered!: - -[float] -Main -^^^^^ - -. __main()__ -+ -First the application is initialized. After the initialization is finished the -application is started. - -[float] -Initialization -^^^^^^^^^^^^^^ - -[start=2] -. __init()__ -+ -The initialization contains the registration of a message handler, an event -handler and an availability handler. -+ -The event handler declares again a callback (__on_state_cbk__) for state changes -that occur. -+ -The message handler declares a callback (__on_message_cbk__) for messages that -are received from any service, any service instance and any method. -+ -The availability handler declares a callback (__on_availability_cbk__) which is -called when the specific service is available (specifying the service id and the -service instance id). - -[float] -Start -^^^^^ - -[start=3] -. __start()__ -+ -The application will be started. This function only returns when the application -will be stopped. - -[float] -Callbacks -^^^^^^^^^ - -[start=4] -. __on_state_cbk()__ -+ - -This function is called by the application when an state change occurred. If the -application was successfully registered at the runtime then the specific service -is requested. - -. __on_availability_cbk()__ -+ -This function is called when the requested service is available or no longer -available. -+ -First there is a check if the change of the availability is related to the -'hello world service' and the availability changed to true. -If the check is successful a service request is created and the appropriate -service information are set (service id, service instance id, service method -id). -After that the payload of the request is created. The data of the payload is -'World' and will be set afterwards. -Finally the request is sent to the service. - -. __on_message_cbk()__ -+ -This function is called when a message/response was received. -If the response is from the requested service, of type 'RESPONSE' and the return -code is 'OK' then the payload of the response is printed. Finally the -application is stopped. - -[float] -Stop -^^^^ - -[start=7] -. __stop()__ -+ -This function unregister the event and the message handler and shuts down the -application. - -:numbered: diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index 09d8b03..43a8f0a 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -7,7 +7,7 @@ cmake_minimum_required (VERSION 2.8) # Daemon add_executable(vsomeipd vsomeipd.cpp)
-target_link_libraries(vsomeipd vsomeip ${Boost_LIBRARIES} ${systemd-journal_LIBRARIES} ${DL_LIBRARY} ${DLT_LIBRARIES})
+target_link_libraries(vsomeipd vsomeip ${Boost_LIBRARIES} ${DL_LIBRARY} ${DLT_LIBRARIES})
install (
TARGETS vsomeipd diff --git a/daemon/vsomeipd.cpp b/daemon/vsomeipd.cpp index 54c6b9b..d924ac9 100644 --- a/daemon/vsomeipd.cpp +++ b/daemon/vsomeipd.cpp @@ -5,6 +5,7 @@ #include <sys/types.h> #include <sys/stat.h> +#include <signal.h> #include <unistd.h> #include <iostream> @@ -18,13 +19,29 @@ #include "../implementation/logging/include/defines.hpp" #endif +static std::shared_ptr<vsomeip::application> its_application; + +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +/* + * Handle signal to stop the daemon + */ +void vsomeipd_stop(int _signal) { + if (_signal == SIGINT || _signal == SIGTERM) + its_application->stop(); +} +#endif + /* * Create a vsomeip application object and start it. */ -int process(void) { +int vsomeipd_process(bool _is_quiet) { #ifdef USE_DLT - DLT_REGISTER_APP(VSOMEIP_LOG_DEFAULT_APPLICATION_ID, VSOMEIP_LOG_DEFAULT_APPLICATION_NAME); + if (!_is_quiet) + DLT_REGISTER_APP(VSOMEIP_LOG_DEFAULT_APPLICATION_ID, VSOMEIP_LOG_DEFAULT_APPLICATION_NAME); +#else + (void)_is_quiet; #endif + std::shared_ptr<vsomeip::runtime> its_runtime = vsomeip::runtime::get(); @@ -32,9 +49,13 @@ int process(void) { return -1; } - std::shared_ptr<vsomeip::application> its_application - = its_runtime->create_application(VSOMEIP_ROUTING); - + // Create the application object + its_application = its_runtime->create_application(VSOMEIP_ROUTING); +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + // Handle signals + signal(SIGINT, vsomeipd_stop); + signal(SIGTERM, vsomeipd_stop); +#endif if (its_application->init()) { if (its_application->is_routing()) { its_application->start(); @@ -49,19 +70,23 @@ int process(void) { * Parse command line options * -h | --help print usage information * -d | --daemonize start background processing by forking the process + * -q | --quiet do _not_ use dlt logging * * and start processing. */ int main(int argc, char **argv) { bool must_daemonize(false); + bool is_quiet(false); if (argc > 1) { for (int i = 0; i < argc; i++) { std::string its_argument(argv[i]); if (its_argument == "-d" || its_argument == "--daemonize") { must_daemonize = true; + } else if (its_argument == "-q" || its_argument == "--quiet") { + is_quiet = true; } else if (its_argument == "-h" || its_argument == "--help") { std::cout << "usage: " - << argv[0] << " [-h|--help][-d|--daemonize]" + << argv[0] << " [-h|--help][-d|--daemonize][-q|--quiet]" << std::endl; return 0; } @@ -90,5 +115,5 @@ int main(int argc, char **argv) { } } - return process(); + return vsomeipd_process(is_quiet); } diff --git a/examples/hello_world/CMakeLists.txt b/examples/hello_world/CMakeLists.txt index cb32665..d6bc994 100644 --- a/examples/hello_world/CMakeLists.txt +++ b/examples/hello_world/CMakeLists.txt @@ -14,12 +14,14 @@ if (NOT vsomeip_FOUND) message("vsomeip was not found. Please specify vsomeip_DIR") endif() +find_package(Threads REQUIRED) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") include_directories(${VSOMEIP_INCLUDE_DIRS}) add_executable (hello_world_service hello_world_service.cpp) -target_link_libraries(hello_world_service ${VSOMEIP_LIBRARIES}) +target_link_libraries(hello_world_service ${VSOMEIP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) add_executable (hello_world_client hello_world_client.cpp) -target_link_libraries(hello_world_client ${VSOMEIP_LIBRARIES}) +target_link_libraries(hello_world_client ${VSOMEIP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/hello_world/hello_world_client.cpp b/examples/hello_world/hello_world_client.cpp index 8007c32..8d5b33c 100644 --- a/examples/hello_world/hello_world_client.cpp +++ b/examples/hello_world/hello_world_client.cpp @@ -2,7 +2,9 @@ // 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/. - +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +#include <csignal> +#endif #include <vsomeip/vsomeip.hpp> #include <iostream> @@ -125,9 +127,23 @@ private: std::shared_ptr<vsomeip::application> app_; }; +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +hello_world_client *hw_cl_ptr(nullptr); + void handle_signal(int _signal) { + if (hw_cl_ptr != nullptr && + (_signal == SIGINT || _signal == SIGTERM)) + hw_cl_ptr->stop(); + } +#endif + int main(int argc, char **argv) { hello_world_client hw_cl; +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + hw_cl_ptr = &hw_cl; + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); +#endif hw_cl.init(); hw_cl.start(); return 0; diff --git a/examples/hello_world/hello_world_service.cpp b/examples/hello_world/hello_world_service.cpp index b99a401..7e6b615 100644 --- a/examples/hello_world/hello_world_service.cpp +++ b/examples/hello_world/hello_world_service.cpp @@ -2,7 +2,9 @@ // 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/. - +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +#include <csignal> +#endif #include <vsomeip/vsomeip.hpp> #include <chrono> #include <thread> @@ -75,6 +77,12 @@ public: app_->stop(); } + void terminate() { + std::lock_guard<std::mutex> its_lock(mutex_); + stop_ = true; + condition_.notify_one(); + } + void on_state_cbk(vsomeip::state_type_e _state) { if(_state == vsomeip::state_type_e::ST_REGISTERED) @@ -104,9 +112,7 @@ public: // Send the response back app_->send(resp, true); // we're finished stop now - std::lock_guard<std::mutex> its_lock(mutex_); - stop_ = true; - condition_.notify_one(); + terminate(); } private: @@ -118,9 +124,23 @@ private: std::thread stop_thread_; }; +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +hello_world_service *hw_srv_ptr(nullptr); + void handle_signal(int _signal) { + if (hw_srv_ptr != nullptr && + (_signal == SIGINT || _signal == SIGTERM)) + hw_srv_ptr->terminate(); + } +#endif + int main(int argc, char **argv) { hello_world_service hw_srv; +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + hw_srv_ptr = &hw_srv; + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); +#endif hw_srv.init(); hw_srv.start(); return 0; diff --git a/examples/notify-sample.cpp b/examples/notify-sample.cpp index 12c9a4c..172bf7d 100644 --- a/examples/notify-sample.cpp +++ b/examples/notify-sample.cpp @@ -2,7 +2,9 @@ // 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/. - +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +#include <csignal> +#endif #include <chrono> #include <condition_variable> #include <iomanip> @@ -22,6 +24,7 @@ public: use_tcp_(_use_tcp), cycle_(_cycle), blocked_(false), + running_(true), is_offered_(false), offer_thread_(std::bind(&service_sample::run, this)), notify_thread_(std::bind(&service_sample::notify, this)) { @@ -67,6 +70,21 @@ public: app_->start(); } +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + /* + * Handle signal to shutdown + */ + void stop() { + running_ = false; + blocked_ = true; + condition_.notify_one(); + notify_condition_.notify_one(); + offer_thread_.join(); + notify_thread_.join(); + app_->stop(); + } +#endif + void offer() { std::lock_guard<std::mutex> its_lock(notify_mutex_); app_->offer_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID); @@ -117,13 +135,15 @@ public: condition_.wait(its_lock); bool is_offer(true); - while (true) { + while (running_) { if (is_offer) offer(); else stop_offer(); - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + for (int i = 0; i < 10 && running_; i++) + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + is_offer = !is_offer; } } @@ -139,11 +159,11 @@ public: vsomeip::byte_t its_data[10]; uint32_t its_size = 1; - while (true) { + while (running_) { std::unique_lock<std::mutex> its_lock(notify_mutex_); - while (!is_offered_) + while (!is_offered_ && running_) notify_condition_.wait(its_lock); - while (is_offered_) { + while (is_offered_ && running_) { if (its_size == sizeof(its_data)) its_size = 1; @@ -171,6 +191,7 @@ private: std::mutex mutex_; std::condition_variable condition_; bool blocked_; + bool running_; std::mutex notify_mutex_; std::condition_variable notify_condition_; @@ -183,6 +204,15 @@ private: std::thread notify_thread_; }; +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + service_sample *its_sample_ptr(nullptr); + void handle_signal(int _signal) { + if (its_sample_ptr != nullptr && + (_signal == SIGINT || _signal == SIGTERM)) + its_sample_ptr->stop(); + } +#endif + int main(int argc, char **argv) { bool use_tcp = false; uint32_t cycle = 1000; // default 1s @@ -210,6 +240,11 @@ int main(int argc, char **argv) { } service_sample its_sample(use_tcp, cycle); +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + its_sample_ptr = &its_sample; + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); +#endif its_sample.init(); its_sample.start(); diff --git a/examples/request-sample.cpp b/examples/request-sample.cpp index 86834d0..93b8f0e 100644 --- a/examples/request-sample.cpp +++ b/examples/request-sample.cpp @@ -2,7 +2,9 @@ // 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/. - +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +#include <csignal> +#endif #include <chrono> #include <condition_variable> #include <iomanip> @@ -77,6 +79,19 @@ public: app_->start(); } +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + /* + * Handle signal to shutdown + */ + void stop() { + running_ = false; + blocked_ = true; + condition_.notify_one(); + sender_.join(); + app_->stop(); + } +#endif + void on_state(vsomeip::state_type_e _state) { if (_state == vsomeip::state_type_e::ST_REGISTERED) { app_->request_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID); @@ -165,6 +180,14 @@ private: std::thread sender_; }; +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + client_sample *its_sample_ptr(nullptr); + void handle_signal(int _signal) { + if (its_sample_ptr != nullptr && + (_signal == SIGINT || _signal == SIGTERM)) + its_sample_ptr->stop(); + } +#endif int main(int argc, char **argv) { bool use_tcp = false; @@ -194,6 +217,11 @@ int main(int argc, char **argv) { } client_sample its_sample(use_tcp, be_quiet, cycle); +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + its_sample_ptr = &its_sample; + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); +#endif its_sample.init(); its_sample.start(); return 0; diff --git a/examples/response-sample.cpp b/examples/response-sample.cpp index c3ef177..883d821 100644 --- a/examples/response-sample.cpp +++ b/examples/response-sample.cpp @@ -2,7 +2,9 @@ // 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/. - +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +#include <csignal> +#endif #include <chrono> #include <condition_variable> #include <iomanip> @@ -21,6 +23,7 @@ public: is_registered_(false), use_static_routing_(_use_static_routing), blocked_(false), + running_(true), offer_thread_(std::bind(&service_sample::run, this)) { } @@ -44,6 +47,19 @@ public: app_->start(); } +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + /* + * Handle signal to shutdown + */ + void stop() { + running_ = false; + blocked_ = true; + condition_.notify_one(); + offer_thread_.join(); + app_->stop(); + } +#endif + void offer() { app_->offer_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID); app_->offer_service(SAMPLE_SERVICE_ID + 1, SAMPLE_INSTANCE_ID); @@ -101,14 +117,16 @@ public: if (use_static_routing_) { offer(); - while (true); + while (running_); } else { - while (true) { + while (running_) { if (is_offer) offer(); else stop_offer(); - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + + for (int i = 0; i < 10 && running_; i++) + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); is_offer = !is_offer; } } @@ -122,11 +140,21 @@ private: std::mutex mutex_; std::condition_variable condition_; bool blocked_; + bool running_; // blocked_ must be initialized before the thread is started. std::thread offer_thread_; }; +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + service_sample *its_sample_ptr(nullptr); + void handle_signal(int _signal) { + if (its_sample_ptr != nullptr && + (_signal == SIGINT || _signal == SIGTERM)) + its_sample_ptr->stop(); + } +#endif + int main(int argc, char **argv) { bool use_static_routing(false); @@ -139,6 +167,11 @@ int main(int argc, char **argv) { } service_sample its_sample(use_static_routing); +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + its_sample_ptr = &its_sample; + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); +#endif its_sample.init(); its_sample.start(); diff --git a/examples/subscribe-sample.cpp b/examples/subscribe-sample.cpp index 915ea11..38b80c8 100644 --- a/examples/subscribe-sample.cpp +++ b/examples/subscribe-sample.cpp @@ -2,7 +2,9 @@ // 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/. - +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING +#include <csignal> +#endif #include <chrono> #include <condition_variable> #include <iomanip> @@ -57,6 +59,15 @@ public: app_->start(); } +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + /* + * Handle signal to shutdown + */ + void stop() { + app_->stop(); + } +#endif + void on_state(vsomeip::state_type_e _state) { if (_state == vsomeip::state_type_e::ST_REGISTERED) { app_->request_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID); @@ -134,6 +145,15 @@ private: bool be_quiet_; }; +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + client_sample *its_sample_ptr(nullptr); + void handle_signal(int _signal) { + if (its_sample_ptr != nullptr && + (_signal == SIGINT || _signal == SIGTERM)) + its_sample_ptr->stop(); + } +#endif + int main(int argc, char **argv) { bool use_tcp = false; @@ -151,6 +171,11 @@ int main(int argc, char **argv) { } client_sample its_sample(use_tcp); +#ifndef VSOMEIP_ENABLE_SIGNAL_HANDLING + its_sample_ptr = &its_sample; + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); +#endif its_sample.init(); its_sample.start(); return 0; diff --git a/implementation/configuration/include/configuration.hpp b/implementation/configuration/include/configuration.hpp index 8dab1b4..e33a0df 100644 --- a/implementation/configuration/include/configuration.hpp +++ b/implementation/configuration/include/configuration.hpp @@ -25,7 +25,7 @@ namespace vsomeip { class event; -class VSOMEIP_EXPORT configuration { +class VSOMEIP_IMPORT_EXPORT configuration { public: static std::shared_ptr<configuration> get( const std::set<std::string> &_input = std::set<std::string>()); @@ -65,6 +65,9 @@ public: virtual bool get_multicast(service_t _service, instance_t _instance, eventgroup_t _eventgroup, std::string &_address, uint16_t &_port) const = 0; + virtual uint8_t get_threshold(service_t _service, instance_t _instance, + eventgroup_t _eventgroup) const = 0; + virtual client_t get_id(const std::string &_name) const = 0; virtual bool is_configured_client_id(client_t _id) const = 0; @@ -77,6 +80,10 @@ public: virtual bool supports_selective_broadcasts(boost::asio::ip::address _address) const = 0; + virtual bool is_offered_remote(service_t _service, instance_t _instance) const = 0; + + virtual bool is_local_service(service_t _service, instance_t _instance) const = 0; + // Service Discovery configuration virtual bool is_sd_enabled() const = 0; @@ -103,6 +110,9 @@ public: // File permissions virtual std::uint32_t get_umask() const = 0; virtual std::uint32_t get_permissions_shm() const = 0; + + virtual bool log_version() const = 0; + virtual uint32_t get_log_version_interval() const = 0; }; } // namespace vsomeip diff --git a/implementation/configuration/include/configuration_impl.hpp b/implementation/configuration/include/configuration_impl.hpp index 87a1d15..46b3166 100644 --- a/implementation/configuration/include/configuration_impl.hpp +++ b/implementation/configuration/include/configuration_impl.hpp @@ -17,6 +17,7 @@ #include "trace.hpp" #include "configuration.hpp" #include "watchdog.hpp" +#include "service_instance_range.hpp" namespace vsomeip { namespace cfg { @@ -24,6 +25,7 @@ namespace cfg { struct client; struct service; struct servicegroup; +struct event; struct eventgroup; struct watchdog; @@ -85,12 +87,22 @@ public: VSOMEIP_EXPORT bool get_multicast(service_t _service, instance_t _instance, eventgroup_t _eventgroup, std::string &_address, uint16_t &_port) const; + VSOMEIP_EXPORT uint8_t get_threshold(service_t _service, instance_t _instance, + eventgroup_t _eventgroup) const; + VSOMEIP_EXPORT std::uint32_t get_max_message_size_local() const; VSOMEIP_EXPORT std::uint32_t get_message_size_reliable(const std::string& _address, std::uint16_t _port) const; VSOMEIP_EXPORT bool supports_selective_broadcasts(boost::asio::ip::address _address) const; + VSOMEIP_EXPORT bool is_offered_remote(service_t _service, instance_t _instance) const; + + VSOMEIP_EXPORT bool log_version() const; + VSOMEIP_EXPORT uint32_t get_log_version_interval() const; + + VSOMEIP_EXPORT bool is_local_service(service_t _service, instance_t _instance) const; + // Service Discovery configuration VSOMEIP_EXPORT bool is_sd_enabled() const; @@ -159,6 +171,7 @@ private: std::string &_criteria, std::shared_ptr<trace_filter_rule> &_filter_rule); void get_permission_configuration(const element &_element); + void get_internal_services(const boost::property_tree::ptree &_tree); servicegroup *find_servicegroup(const std::string &_name) const; std::shared_ptr<client> find_client(service_t _service, instance_t _instance) const; @@ -166,6 +179,11 @@ private: std::shared_ptr<eventgroup> find_eventgroup(service_t _service, instance_t _instance, eventgroup_t _eventgroup) const; + void set_magic_cookies_unicast_address(); + + bool is_remote(std::shared_ptr<service> _service) const; + bool is_internal_service(service_t _service, instance_t _instance) const; + private: static std::shared_ptr<configuration_impl> the_configuration; static std::mutex mutex_; @@ -218,6 +236,11 @@ protected: std::shared_ptr<watchdog> watchdog_; + std::vector<service_instance_range> internal_service_ranges_; + + bool log_version_; + uint32_t log_version_interval_; + enum element_type_e { ET_UNICAST, ET_DIAGNOSIS, @@ -241,7 +264,8 @@ protected: ET_WATCHDOG_TIMEOUT, ET_WATCHDOG_ALLOWED_MISSING_PONGS, ET_TRACING_ENABLE, - ET_MAX = 22 + ET_TRACING_SD_ENABLE, + ET_MAX = 23 }; bool is_configured_[ET_MAX]; diff --git a/implementation/configuration/include/event.hpp b/implementation/configuration/include/event.hpp index d488ec4..bc12ff1 100644 --- a/implementation/configuration/include/event.hpp +++ b/implementation/configuration/include/event.hpp @@ -17,8 +17,8 @@ namespace cfg { struct eventgroup; struct event { - event(event_t _id, bool _is_field, bool _is_reliable) : - id_(_id), is_field_(_is_field), is_reliable_(_is_reliable) { + event(event_t _id, bool _is_field, bool _is_reliable) + : id_(_id), is_field_(_is_field), is_reliable_(_is_reliable) { } event_t id_; diff --git a/implementation/configuration/include/eventgroup.hpp b/implementation/configuration/include/eventgroup.hpp index 93a91a3..7974d98 100644 --- a/implementation/configuration/include/eventgroup.hpp +++ b/implementation/configuration/include/eventgroup.hpp @@ -20,6 +20,7 @@ struct eventgroup { std::set<std::shared_ptr<event> > events_; std::string multicast_address_; uint16_t multicast_port_; + uint8_t threshold_; }; } // namespace cfg diff --git a/implementation/configuration/include/internal.hpp.in b/implementation/configuration/include/internal.hpp.in index d7770c9..d10c6d6 100644 --- a/implementation/configuration/include/internal.hpp.in +++ b/implementation/configuration/include/internal.hpp.in @@ -85,7 +85,7 @@ #define VSOMEIP_SUBSCRIBE_COMMAND_SIZE 16 #define VSOMEIP_SUBSCRIBE_ACK_COMMAND_SIZE 13 #define VSOMEIP_SUBSCRIBE_NACK_COMMAND_SIZE 13 -#define VSOMEIP_UNSUBSCRIBE_COMMAND_SIZE 13 +#define VSOMEIP_UNSUBSCRIBE_COMMAND_SIZE 14 #define VSOMEIP_REGISTER_EVENT_COMMAND_SIZE 15 #define VSOMEIP_UNREGISTER_EVENT_COMMAND_SIZE 14 @@ -100,20 +100,22 @@ #define VSOMEIP_DEFAULT_SHM_PERMISSION 0666 #define VSOMEIP_DEFAULT_UMASK_LOCAL_ENDPOINTS 0000 -#define VSOMEIP_MAX_CLIENTS 255 +#define VSOMEIP_MAX_CLIENTS 255 namespace vsomeip { struct configuration_data_t { -#ifdef WIN32 - void* mutex_; -#else +#ifndef WIN32 + volatile char initialized_; pthread_mutex_t mutex_; #endif unsigned short client_base_; unsigned short used_client_ids_[VSOMEIP_MAX_CLIENTS]; int max_used_client_ids_index_; + unsigned char max_assigned_client_id_low_byte_; + + unsigned short routing_manager_host_; }; } // namespace vsomeip diff --git a/implementation/configuration/include/service_instance_range.hpp b/implementation/configuration/include/service_instance_range.hpp new file mode 100644 index 0000000..57a8c21 --- /dev/null +++ b/implementation/configuration/include/service_instance_range.hpp @@ -0,0 +1,24 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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/. + +#ifndef VSOMEIP_CFG_SERVICE_INSTANCE_RANGE_HPP +#define VSOMEIP_CFG_SERVICE_INSTANCE_RANGE_HPP + +#include <vsomeip/primitive_types.hpp> + +namespace vsomeip { +namespace cfg { + +struct service_instance_range { + service_t first_service_; + service_t last_service_; + instance_t first_instance_; + instance_t last_instance_; +}; + +} +} + +#endif // VSOMEIP_CFG_SERVICE_INSTANCE_RANGE_HPP diff --git a/implementation/configuration/include/trace.hpp b/implementation/configuration/include/trace.hpp index cba4da8..3ffdf00 100644 --- a/implementation/configuration/include/trace.hpp +++ b/implementation/configuration/include/trace.hpp @@ -46,7 +46,8 @@ struct trace { trace() : channels_(), filter_rules_(), - is_enabled_(false) { + is_enabled_(false), + is_sd_enabled_(false) { channels_.push_back(std::make_shared<trace_channel>()); } @@ -54,6 +55,7 @@ struct trace { std::vector<std::shared_ptr<trace_filter_rule>> filter_rules_; bool is_enabled_; + bool is_sd_enabled_; }; } // namespace cfg diff --git a/implementation/configuration/src/configuration_impl.cpp b/implementation/configuration/src/configuration_impl.cpp index 3c78de0..ef5b706 100644 --- a/implementation/configuration/src/configuration_impl.cpp +++ b/implementation/configuration/src/configuration_impl.cpp @@ -42,6 +42,8 @@ std::shared_ptr<configuration> configuration_impl::get( static bool has_reading_failed(false); if (!the_configuration) { + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + the_configuration = std::make_shared<configuration_impl>(); std::vector<element> its_configuration_elements; @@ -95,7 +97,13 @@ std::shared_ptr<configuration> configuration_impl::get( its_configuration_elements.end()); for (auto e : its_configuration_elements) the_configuration->load(e); + // set global unicast address for all services with magic cookies enabled + the_configuration->set_magic_cookies_unicast_address(); } + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + VSOMEIP_DEBUG << "Parsed vSomeIP configuration in " + << std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() + << "ms"; } // There is only one attempt to read the configuration file(s). @@ -131,6 +139,8 @@ configuration_impl::configuration_impl() : max_configured_message_size_(0), trace_(std::make_shared<trace>()), watchdog_(std::make_shared<watchdog>()), + log_version_(true), + log_version_interval_(10), permissions_shm_(VSOMEIP_DEFAULT_SHM_PERMISSION), umask_(VSOMEIP_DEFAULT_UMASK_LOCAL_ENDPOINTS) { unicast_ = unicast_.from_string(VSOMEIP_UNICAST_ADDRESS); @@ -197,6 +207,7 @@ void configuration_impl::load(const element &_element) { get_trace_configuration(_element); get_supports_selective_broadcasts(_element.tree_); get_watchdog_configuration(_element); + get_internal_services(_element.tree_); } catch (std::exception &e) { #ifdef WIN32 e; // silence MSVC warning C4101 @@ -281,6 +292,18 @@ void configuration_impl::get_logging_configuration( boost::log::trivial::severity_level::info)))))); is_configured_[ET_LOGGING_LEVEL] = true; } + } else if (its_key == "version") { + std::stringstream its_converter; + for (auto j : i->second) { + std::string its_sub_key(j.first); + std::string its_sub_value(j.second.data()); + if (its_sub_key == "enable") { + log_version_ = (its_sub_value == "true"); + } else if (its_sub_key == "interval") { + its_converter << std::dec << its_sub_value; + its_converter >> log_version_interval_; + } + } } } } catch (...) { @@ -325,7 +348,7 @@ void configuration_impl::get_services_configuration( try { auto its_services = _tree.get_child("services"); for (auto i = its_services.begin(); i != its_services.end(); ++i) - get_service_configuration(i->second, ""); + get_service_configuration(i->second, "local"); } catch (...) { try { auto its_servicegroups = _tree.get_child("servicegroups"); @@ -337,6 +360,19 @@ void configuration_impl::get_services_configuration( } } +void configuration_impl::set_magic_cookies_unicast_address() { + // get services with static routing that have magic cookies enabled + std::map<std::string, std::set<uint16_t> > its_magic_cookies_ = magic_cookies_; + its_magic_cookies_.erase("local"); + + //set unicast address of host for all services without static routing + its_magic_cookies_[get_unicast_address().to_string()].insert(magic_cookies_["local"].begin(), + magic_cookies_["local"].end()); + magic_cookies_.clear(); + magic_cookies_ = its_magic_cookies_; +} + + void configuration_impl::get_clients_configuration( const boost::property_tree::ptree &_tree) { try { @@ -553,8 +589,7 @@ void configuration_impl::get_service_configuration( services_[its_service->service_][its_service->instance_] = its_service; if (use_magic_cookies) { - magic_cookies_[get_unicast_address(its_service->service_, - its_service->instance_)].insert(its_service->reliable_); + magic_cookies_[its_service->unicast_address_].insert(its_service->reliable_); } } } catch (...) { @@ -682,9 +717,8 @@ void configuration_impl::get_eventgroup_configuration( for (auto j = i->second.begin(); j != i->second.end(); ++j) { std::stringstream its_converter; std::string its_key(j->first); + std::string its_value(j->second.data()); if (its_key == "eventgroup") { - - std::string its_value(j->second.data()); if (its_value.size() > 1 && its_value[0] == '0' && its_value[1] == 'x') { its_converter << std::hex << its_value; } else { @@ -706,6 +740,12 @@ void configuration_impl::get_eventgroup_configuration( its_converter >> its_eventgroup->multicast_port_; } catch (...) { } + } else if (its_key == "threshold") { + int its_threshold(0); + std::stringstream its_converter; + its_converter << std::dec << its_value; + its_converter >> std::dec >> its_threshold; + its_eventgroup->threshold_ = static_cast<uint8_t>(its_threshold); } else if (its_key == "events") { for (auto k = j->second.begin(); k != j->second.end(); ++k) { std::stringstream its_converter; @@ -984,6 +1024,14 @@ void configuration_impl::get_trace_configuration(const element &_element) { trace_->is_enabled_ = (its_value == "true"); is_configured_[ET_TRACING_ENABLE] = true; } + } else if (its_key == "sd_enable") { + if (is_configured_[ET_TRACING_SD_ENABLE]) { + VSOMEIP_WARNING << "Multiple definitions of tracing.sd_enable." + << " Ignoring definition from " << _element.name_; + } else { + trace_->is_sd_enabled_ = (its_value == "true"); + is_configured_[ET_TRACING_SD_ENABLE] = true; + } } else if(its_key == "channels") { get_trace_channels_configuration(i->second); } else if(its_key == "filters") { @@ -1144,6 +1192,92 @@ void configuration_impl::get_watchdog_configuration(const element &_element) { } } +void configuration_impl::get_internal_services( + const boost::property_tree::ptree &_tree) { + try { + auto optional = _tree.get_child_optional("internal_services"); + if (!optional) { + return; + } + auto its_internal_services = _tree.get_child("internal_services"); + for (auto found_range = its_internal_services.begin(); + found_range != its_internal_services.end(); ++found_range) { + service_instance_range range; + range.first_service_ = 0x0; + range.last_service_ = 0x0; + range.first_instance_ = 0x0; + range.last_instance_ = 0xffff; + for (auto i = found_range->second.begin(); + i != found_range->second.end(); ++i) { + if (i->first == "first") { + if (i->second.size() == 0) { + std::stringstream its_converter; + std::string value = i->second.data(); + its_converter << std::hex << value; + its_converter >> range.first_service_; + } + for (auto n = i->second.begin(); + n != i->second.end(); ++n) { + if (n->first == "service") { + std::stringstream its_converter; + std::string value = n->second.data(); + its_converter << std::hex << value; + its_converter >> range.first_service_; + } else if (n->first == "instance") { + std::stringstream its_converter; + std::string value = n->second.data(); + its_converter << std::hex << value; + its_converter >> range.first_instance_; + } + } + } else if (i->first == "last") { + if (i->second.size() == 0) { + std::stringstream its_converter; + std::string value = i->second.data(); + its_converter << std::hex << value; + its_converter >> range.last_service_; + } + for (auto n = i->second.begin(); + n != i->second.end(); ++n) { + if (n->first == "service") { + std::stringstream its_converter; + std::string value = n->second.data(); + its_converter << std::hex << value; + its_converter >> range.last_service_; + } else if (n->first == "instance") { + std::stringstream its_converter; + std::string value = n->second.data(); + its_converter << std::hex << value; + its_converter >> range.last_instance_; + } + } + } + } + if (range.last_service_ >= range.first_service_) { + if (range.last_instance_ >= range.first_instance_) { + internal_service_ranges_.push_back(range); + } + } + } + } catch (...) { + VSOMEIP_ERROR << "Error parsing internal service range configuration!"; + } +} + +bool configuration_impl::is_internal_service(service_t _service, + instance_t _instance) const { + + for (auto its_range : internal_service_ranges_) { + if (_service >= its_range.first_service_ && + _service <= its_range.last_service_ && + _instance >= its_range.first_instance_ && + _instance <= its_range.last_instance_) { + return true; + } + } + return false; +} + // Public interface const boost::asio::ip::address & configuration_impl::get_unicast_address() const { return unicast_; @@ -1311,16 +1445,21 @@ configuration_impl::get_remote_services() const { std::set<std::pair<service_t, instance_t> > its_remote_services; for (auto i : services_) { for (auto j : i.second) { - if (j.second->unicast_address_ != "local" && - j.second->unicast_address_ != "" && - j.second->unicast_address_ != unicast_.to_string() && - j.second->unicast_address_ != VSOMEIP_UNICAST_ADDRESS) + if (is_remote(j.second)) { its_remote_services.insert(std::make_pair(i.first, j.first)); + } } } return its_remote_services; } +bool configuration_impl::is_remote(std::shared_ptr<service> _service) const { + return (_service->unicast_address_ != "local" && + _service->unicast_address_ != "" && + _service->unicast_address_ != unicast_.to_string() && + _service->unicast_address_ != VSOMEIP_UNICAST_ADDRESS); +} + bool configuration_impl::get_multicast(service_t _service, instance_t _instance, eventgroup_t _eventgroup, std::string &_address, uint16_t &_port) const @@ -1338,6 +1477,13 @@ bool configuration_impl::get_multicast(service_t _service, return true; } +uint8_t configuration_impl::get_threshold(service_t _service, + instance_t _instance, eventgroup_t _eventgroup) const { + std::shared_ptr<eventgroup> its_eventgroup + = find_eventgroup(_service, _instance, _eventgroup); + return (its_eventgroup ? its_eventgroup->threshold_ : 0); +} + std::shared_ptr<client> configuration_impl::find_client(service_t _service, instance_t _instance) const { std::shared_ptr<client> its_client; @@ -1394,7 +1540,7 @@ std::uint32_t configuration_impl::get_max_message_size_local() const { // to the routing_manager stub return std::uint32_t(its_max_message_size + VSOMEIP_COMMAND_HEADER_SIZE + sizeof(instance_t) - + sizeof(bool) + sizeof(bool) + sizeof(bool)); + + sizeof(bool) + sizeof(bool)); } std::uint32_t configuration_impl::get_message_size_reliable( @@ -1413,6 +1559,32 @@ bool configuration_impl::supports_selective_broadcasts(boost::asio::ip::address return supported_selective_addresses.find(_address.to_string()) != supported_selective_addresses.end(); } +bool configuration_impl::log_version() const { + return log_version_; +} + +uint32_t configuration_impl::get_log_version_interval() const { + return log_version_interval_; +} + +bool configuration_impl::is_offered_remote(service_t _service, instance_t _instance) const { + uint16_t reliable_port = get_reliable_port(_service, _instance); + uint16_t unreliable_port = get_unreliable_port(_service, _instance); + return (reliable_port != ILLEGAL_PORT || unreliable_port != ILLEGAL_PORT); +} + +bool configuration_impl::is_local_service(service_t _service, instance_t _instance) const { + std::shared_ptr<service> s = find_service(_service, _instance); + if (s && !is_remote(s)) { + return true; + } + if (is_internal_service(_service, _instance)) { + return true; + } + + return false; +} + // Service Discovery configuration bool configuration_impl::is_sd_enabled() const { return is_sd_enabled_; diff --git a/implementation/endpoints/include/client_endpoint_impl.hpp b/implementation/endpoints/include/client_endpoint_impl.hpp index f80e385..9d4fb25 100644 --- a/implementation/endpoints/include/client_endpoint_impl.hpp +++ b/implementation/endpoints/include/client_endpoint_impl.hpp @@ -60,17 +60,20 @@ public: public:
virtual void connect() = 0;
virtual void receive() = 0;
+ typedef std::function<void()> endpoint_error_handler_t;
+ void register_error_callback(endpoint_error_handler_t _callback);
protected:
virtual void send_queued() = 0;
+ void shutdown_and_close_socket();
socket_type socket_;
endpoint_type remote_;
uint16_t local_port_;
- boost::asio::system_timer flush_timer_;
- boost::asio::system_timer connect_timer_;
+ boost::asio::steady_timer flush_timer_;
+ boost::asio::steady_timer connect_timer_;
uint32_t connect_timeout_;
bool is_connected_;
@@ -81,6 +84,9 @@ protected: std::mutex mutex_;
bool was_not_connected_;
+
+ std::mutex error_handler_mutex_;
+ endpoint_error_handler_t error_handler_;
};
} // namespace vsomeip
diff --git a/implementation/endpoints/include/endpoint_impl.hpp b/implementation/endpoints/include/endpoint_impl.hpp index bd05698..b000011 100644 --- a/implementation/endpoints/include/endpoint_impl.hpp +++ b/implementation/endpoints/include/endpoint_impl.hpp @@ -10,7 +10,7 @@ #include <memory>
#include <boost/asio/io_service.hpp>
-#include <boost/asio/system_timer.hpp>
+#include <boost/asio/steady_timer.hpp>
#include "buffer.hpp"
#include "endpoint.hpp"
diff --git a/implementation/endpoints/include/local_client_endpoint_impl.hpp b/implementation/endpoints/include/local_client_endpoint_impl.hpp index a6d777e..6b57e96 100644 --- a/implementation/endpoints/include/local_client_endpoint_impl.hpp +++ b/implementation/endpoints/include/local_client_endpoint_impl.hpp @@ -49,6 +49,8 @@ private: void connect(); void receive(); + void receive_cbk(boost::system::error_code const &_error, + std::size_t _bytes); }; } // namespace vsomeip diff --git a/implementation/endpoints/include/local_server_endpoint_impl.hpp b/implementation/endpoints/include/local_server_endpoint_impl.hpp index d8f5288..b0ac2ba 100644 --- a/implementation/endpoints/include/local_server_endpoint_impl.hpp +++ b/implementation/endpoints/include/local_server_endpoint_impl.hpp @@ -57,39 +57,35 @@ public: bool is_local() const; - bool queue_message(const byte_t *_data, uint32_t _size); - private: class connection: public boost::enable_shared_from_this<connection> { public: typedef boost::shared_ptr<connection> ptr; - static ptr create(local_server_endpoint_impl *_server, + static ptr create(std::weak_ptr<local_server_endpoint_impl> _server, std::uint32_t _max_message_size); socket_type & get_socket(); void start(); + void stop(); void send_queued(queue_iterator_type _queue_iterator); - bool queue_message(const byte_t *_data, uint32_t _size); - private: - connection(local_server_endpoint_impl *_owner, std::uint32_t _max_message_size); + connection(std::weak_ptr<local_server_endpoint_impl> _server, + std::uint32_t _max_message_size); void send_magic_cookie(); local_server_endpoint_impl::socket_type socket_; - local_server_endpoint_impl *server_; + std::weak_ptr<local_server_endpoint_impl> server_; uint32_t max_message_size_; receive_buffer_t recv_buffer_; size_t recv_buffer_size_; - static std::vector<byte_t> queued_data_; - private: void receive_cbk(boost::system::error_code const &_error, std::size_t _bytes); @@ -101,6 +97,7 @@ private: boost::asio::local::stream_protocol::acceptor acceptor_; #endif + std::mutex connections_mutex_; std::map<endpoint_type, connection::ptr> connections_; connection::ptr current_; diff --git a/implementation/endpoints/include/server_endpoint_impl.hpp b/implementation/endpoints/include/server_endpoint_impl.hpp index 15d001b..c3f6b59 100644 --- a/implementation/endpoints/include/server_endpoint_impl.hpp +++ b/implementation/endpoints/include/server_endpoint_impl.hpp @@ -67,7 +67,7 @@ protected: std::mutex clients_mutex_;
std::map<client_t, std::map<session_t, endpoint_type> > clients_;
- boost::asio::system_timer flush_timer_;
+ boost::asio::steady_timer flush_timer_;
std::mutex mutex_;
};
diff --git a/implementation/endpoints/include/tcp_server_endpoint_impl.hpp b/implementation/endpoints/include/tcp_server_endpoint_impl.hpp index 16197cc..eb0047a 100644 --- a/implementation/endpoints/include/tcp_server_endpoint_impl.hpp +++ b/implementation/endpoints/include/tcp_server_endpoint_impl.hpp @@ -61,7 +61,7 @@ private: public:
typedef boost::shared_ptr<connection> ptr;
- static ptr create(tcp_server_endpoint_impl *_server,
+ static ptr create(std::weak_ptr<tcp_server_endpoint_impl> _server,
std::uint32_t _max_message_size);
socket_type & get_socket();
@@ -74,11 +74,12 @@ private: void send_queued(queue_iterator_type _queue_iterator);
private:
- connection(tcp_server_endpoint_impl *_owner, std::uint32_t _max_message_size);
+ connection(std::weak_ptr<tcp_server_endpoint_impl> _server,
+ std::uint32_t _max_message_size);
void send_magic_cookie(message_buffer_ptr_t &_buffer);
tcp_server_endpoint_impl::socket_type socket_;
- tcp_server_endpoint_impl *server_;
+ std::weak_ptr<tcp_server_endpoint_impl> server_;
uint32_t max_message_size_;
@@ -93,10 +94,12 @@ private: };
boost::asio::ip::tcp::acceptor acceptor_;
+ std::mutex connections_mutex_;
std::map<endpoint_type, connection::ptr> connections_;
connection *current_;
private:
+ void remove_connection(connection *_connection);
void accept_cbk(connection::ptr _connection,
boost::system::error_code const &_error);
};
diff --git a/implementation/endpoints/src/client_endpoint_impl.cpp b/implementation/endpoints/src/client_endpoint_impl.cpp index f68bad7..3d2a752 100644 --- a/implementation/endpoints/src/client_endpoint_impl.cpp +++ b/implementation/endpoints/src/client_endpoint_impl.cpp @@ -74,13 +74,18 @@ void client_endpoint_impl<Protocol>::stop() { }
}
- boost::system::error_code its_error;
- socket_.close(its_error);
+ if (socket_.is_open()) {
+ socket_.cancel();
+ }
}
}
template<typename Protocol>
void client_endpoint_impl<Protocol>::restart() {
+ {
+ std::lock_guard<std::mutex> its_lock(mutex_);
+ queue_.clear();
+ }
is_connected_ = false;
connect_timer_.expires_from_now(
std::chrono::milliseconds(connect_timeout_));
@@ -152,9 +157,8 @@ bool client_endpoint_impl<Protocol>::send(const uint8_t *_data, template<typename Protocol>
bool client_endpoint_impl<Protocol>::flush() {
bool is_successful(true);
-
+ std::lock_guard<std::mutex> its_lock(mutex_);
if (!packetizer_->empty()) {
- std::lock_guard<std::mutex> its_lock(mutex_);
queue_.push_back(packetizer_);
packetizer_ = std::make_shared<message_buffer_t>();
if (queue_.size() == 1) { // no writing in progress
@@ -170,12 +174,16 @@ bool client_endpoint_impl<Protocol>::flush() { template<typename Protocol>
void client_endpoint_impl<Protocol>::connect_cbk(
boost::system::error_code const &_error) {
+ if (_error == boost::asio::error::operation_aborted) {
+ // endpoint was stopped
+ shutdown_and_close_socket();
+ return;
+ }
std::shared_ptr<endpoint_host> its_host = this->host_.lock();
if (its_host) {
if (_error && _error != boost::asio::error::already_connected) {
if(socket_.is_open()) {
- boost::system::error_code its_error;
- socket_.close(its_error);
+ shutdown_and_close_socket();
}
connect_timer_.expires_from_now(
@@ -232,12 +240,19 @@ void client_endpoint_impl<Protocol>::send_cbk( }
} else if (_error == boost::asio::error::broken_pipe) {
is_connected_ = false;
- if (endpoint_impl<Protocol>::sending_blocked_) {
- std::lock_guard<std::mutex> its_lock(mutex_);
- queue_.clear();
+ if (endpoint_impl<Protocol>::sending_blocked_ || error_handler_) {
+ {
+ std::lock_guard<std::mutex> its_lock(mutex_);
+ queue_.clear();
+ }
+ if (error_handler_) {
+ std::lock_guard<std::mutex> its_lock(error_handler_mutex_);
+ error_handler_();
+ }
}
if (socket_.is_open()) {
boost::system::error_code its_error;
+ socket_.shutdown(Protocol::socket::shutdown_both, its_error);
socket_.close(its_error);
}
connect();
@@ -245,6 +260,9 @@ void client_endpoint_impl<Protocol>::send_cbk( || _error == boost::asio::error::bad_descriptor) {
was_not_connected_ = true;
connect();
+ } else if (_error == boost::asio::error::operation_aborted) {
+ // endpoint was stopped
+ shutdown_and_close_socket();
}
}
@@ -256,6 +274,22 @@ void client_endpoint_impl<Protocol>::flush_cbk( }
}
+template<typename Protocol>
+void client_endpoint_impl<Protocol>::register_error_callback(
+ endpoint_error_handler_t _callback) {
+ std::lock_guard<std::mutex> its_lock(error_handler_mutex_);
+ error_handler_ = _callback;
+}
+
+template<typename Protocol>
+void client_endpoint_impl<Protocol>::shutdown_and_close_socket() {
+ if (socket_.is_open()) {
+ boost::system::error_code its_error;
+ socket_.shutdown(Protocol::socket::shutdown_both, its_error);
+ socket_.close(its_error);
+ }
+}
+
// Instantiate template
#ifndef WIN32
template class client_endpoint_impl<boost::asio::local::stream_protocol>;
diff --git a/implementation/endpoints/src/local_client_endpoint_impl.cpp b/implementation/endpoints/src/local_client_endpoint_impl.cpp index 62dba82..7e633e0 100644 --- a/implementation/endpoints/src/local_client_endpoint_impl.cpp +++ b/implementation/endpoints/src/local_client_endpoint_impl.cpp @@ -40,6 +40,7 @@ void local_client_endpoint_impl::start() { sending_blocked_ = false;
boost::system::error_code its_error;
socket_.cancel(its_error);
+ socket_.shutdown(socket_type::shutdown_both, its_error);
socket_.close(its_error);
restart();
} else {
@@ -63,6 +64,20 @@ void local_client_endpoint_impl::connect() { }
void local_client_endpoint_impl::receive() {
+#ifndef WIN32
+ receive_buffer_t its_buffer(VSOMEIP_MAX_LOCAL_MESSAGE_SIZE , 0);
+ socket_.async_receive(
+ boost::asio::buffer(its_buffer),
+ std::bind(
+ &local_client_endpoint_impl::receive_cbk,
+ std::dynamic_pointer_cast<
+ local_client_endpoint_impl
+ >(shared_from_this()),
+ std::placeholders::_1,
+ std::placeholders::_2
+ )
+ );
+#endif
}
void local_client_endpoint_impl::send_queued() {
@@ -107,4 +122,19 @@ VSOMEIP_DEBUG << msg.str(); void local_client_endpoint_impl::send_magic_cookie() {
}
+void local_client_endpoint_impl::receive_cbk(
+ boost::system::error_code const &_error, std::size_t _bytes) {
+ (void)_bytes;
+ if (_error == boost::asio::error::operation_aborted) {
+ // endpoint was stopped
+ shutdown_and_close_socket();
+ } else if (_error == boost::asio::error::connection_reset
+ || _error == boost::asio::error::eof) {
+ VSOMEIP_TRACE << "local_client_endpoint: connection_reseted/EOF";
+ } else {
+ VSOMEIP_ERROR << "Local endpoint received message ("
+ << _error.message() << ")";
+ }
+}
+
} // namespace vsomeip
diff --git a/implementation/endpoints/src/local_server_endpoint_impl.cpp b/implementation/endpoints/src/local_server_endpoint_impl.cpp index e67a646..42ffc2b 100644 --- a/implementation/endpoints/src/local_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/local_server_endpoint_impl.cpp @@ -21,8 +21,18 @@ local_server_endpoint_impl::local_server_endpoint_impl( endpoint_type _local, boost::asio::io_service &_io, std::uint32_t _max_message_size) : local_server_endpoint_base_impl(_host, _local, _io, _max_message_size), - acceptor_(_io, _local), current_(nullptr) { + acceptor_(_io), current_(nullptr) { is_supporting_magic_cookies_ = false; + + boost::system::error_code ec; + acceptor_.open(_local.protocol(), ec); + boost::asio::detail::throw_error(ec, "acceptor open"); + acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "acceptor set_option"); + acceptor_.bind(_local, ec); + boost::asio::detail::throw_error(ec, "acceptor bind"); + acceptor_.listen(boost::asio::socket_base::max_connections, ec); + boost::asio::detail::throw_error(ec, "acceptor listen"); } local_server_endpoint_impl::~local_server_endpoint_impl() { @@ -33,19 +43,23 @@ bool local_server_endpoint_impl::is_local() const { } void local_server_endpoint_impl::start() { - current_ = connection::create(this, max_message_size_); - - acceptor_.async_accept( - current_->get_socket(), - std::bind( - &local_server_endpoint_impl::accept_cbk, - std::dynamic_pointer_cast< - local_server_endpoint_impl - >(shared_from_this()), - current_, - std::placeholders::_1 - ) - ); + if (acceptor_.is_open()) { + current_ = connection::create( + std::dynamic_pointer_cast<local_server_endpoint_impl>( + shared_from_this()), max_message_size_); + + acceptor_.async_accept( + current_->get_socket(), + std::bind( + &local_server_endpoint_impl::accept_cbk, + std::dynamic_pointer_cast< + local_server_endpoint_impl + >(shared_from_this()), + current_, + std::placeholders::_1 + ) + ); + } } void local_server_endpoint_impl::stop() { @@ -53,6 +67,16 @@ void local_server_endpoint_impl::stop() { boost::system::error_code its_error; acceptor_.close(its_error); } + { + std::lock_guard<std::mutex> its_lock(connections_mutex_); + for (const auto &c : connections_) { + if (c.second->get_socket().is_open()) { + boost::system::error_code its_error; + c.second->get_socket().cancel(its_error); + } + } + connections_.clear(); + } server_endpoint_impl::stop(); } @@ -68,6 +92,7 @@ bool local_server_endpoint_impl::send_to( void local_server_endpoint_impl::send_queued( queue_iterator_type _queue_iterator) { + std::lock_guard<std::mutex> its_lock(connections_mutex_); auto connection_iterator = connections_.find(_queue_iterator->first); if (connection_iterator != connections_.end()) connection_iterator->second->send_queued(_queue_iterator); @@ -81,13 +106,6 @@ void local_server_endpoint_impl::restart() { current_->start(); } -bool local_server_endpoint_impl::queue_message(const byte_t *_data, uint32_t _size) { - if (current_) { - return current_->queue_message(_data, _size); - } - return false; -} - local_server_endpoint_impl::endpoint_type local_server_endpoint_impl::get_remote() const { boost::system::error_code its_error; @@ -102,15 +120,14 @@ bool local_server_endpoint_impl::get_default_target( void local_server_endpoint_impl::remove_connection( local_server_endpoint_impl::connection *_connection) { - std::map< endpoint_type, connection::ptr >::iterator i - = connections_.end(); - for (i = connections_.begin(); i != connections_.end(); i++) { - if (i->second.get() == _connection) + std::lock_guard<std::mutex> its_lock(connections_mutex_); + for (auto it = connections_.begin(); it != connections_.end();) { + if (it->second.get() == _connection) { + it = connections_.erase(it); break; - } - - if (i != connections_.end()) { - connections_.erase(i); + } else { + ++it; + } } } @@ -122,23 +139,27 @@ void local_server_endpoint_impl::accept_cbk( boost::system::error_code its_error; endpoint_type remote = new_connection_socket.remote_endpoint(its_error); if(!its_error) { + std::lock_guard<std::mutex> its_lock(connections_mutex_); connections_[remote] = _connection; _connection->start(); } } - start(); + if (_error != boost::asio::error::bad_descriptor && + _error != boost::asio::error::operation_aborted) { + start(); + } } /////////////////////////////////////////////////////////////////////////////// // class local_service_impl::connection /////////////////////////////////////////////////////////////////////////////// -std::vector<byte_t> local_server_endpoint_impl::connection::queued_data_; - local_server_endpoint_impl::connection::connection( - local_server_endpoint_impl *_server, std::uint32_t _max_message_size) - : socket_(_server->service_), server_(_server), + std::weak_ptr<local_server_endpoint_impl> _server, + std::uint32_t _max_message_size) + : socket_(_server.lock()->service_), + server_(_server), max_message_size_(_max_message_size + 8), recv_buffer_(max_message_size_, 0), recv_buffer_size_(0) { @@ -146,7 +167,8 @@ local_server_endpoint_impl::connection::connection( local_server_endpoint_impl::connection::ptr local_server_endpoint_impl::connection::create( - local_server_endpoint_impl *_server, std::uint32_t _max_message_size) { + std::weak_ptr<local_server_endpoint_impl> _server, + std::uint32_t _max_message_size) { return ptr(new connection(_server, _max_message_size)); } @@ -156,20 +178,30 @@ local_server_endpoint_impl::connection::get_socket() { } void local_server_endpoint_impl::connection::start() { - if (recv_buffer_size_ == max_message_size_) { - // Overrun -> Reset buffer - recv_buffer_size_ = 0; + if (socket_.is_open()) { + if (recv_buffer_size_ == max_message_size_) { + // Overrun -> Reset buffer + recv_buffer_size_ = 0; + } + size_t buffer_size = max_message_size_ - recv_buffer_size_; + socket_.async_receive( + boost::asio::buffer(&recv_buffer_[recv_buffer_size_], buffer_size), + std::bind( + &local_server_endpoint_impl::connection::receive_cbk, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2 + ) + ); + } +} + +void local_server_endpoint_impl::connection::stop() { + if (socket_.is_open()) { + boost::system::error_code its_error; + socket_.shutdown(socket_.shutdown_both, its_error); + socket_.close(its_error); } - size_t buffer_size = max_message_size_ - recv_buffer_size_; - socket_.async_receive( - boost::asio::buffer(&recv_buffer_[recv_buffer_size_], buffer_size), - std::bind( - &local_server_endpoint_impl::connection::receive_cbk, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2 - ) - ); } void local_server_endpoint_impl::connection::send_queued( @@ -178,6 +210,12 @@ void local_server_endpoint_impl::connection::send_queued( // TODO: We currently do _not_ use the send method of the local server // endpoints. If we ever need it, we need to add the "start tag", "data", // "end tag" sequence here. + std::shared_ptr<local_server_endpoint_impl> its_server(server_.lock()); + if (!its_server) { + VSOMEIP_TRACE << "local_server_endpoint_impl::connection::send_queued " + " couldn't lock server_"; + return; + } message_buffer_ptr_t its_buffer = _queue_iterator->second.front(); #if 0 @@ -193,7 +231,7 @@ void local_server_endpoint_impl::connection::send_queued( boost::asio::buffer(*its_buffer), std::bind( &local_server_endpoint_base_impl::send_cbk, - server_->shared_from_this(), + its_server, _queue_iterator, std::placeholders::_1, std::placeholders::_2 @@ -201,27 +239,23 @@ void local_server_endpoint_impl::connection::send_queued( ); } -bool local_server_endpoint_impl::connection::queue_message(const byte_t *_data, uint32_t _size) { -#if 0 - std::stringstream msg; - msg << "lse::qm: "; - for (std::size_t i = 0; i < _size; i++) - msg << std::setw(2) << std::setfill('0') << std::hex - << (int)(_data)[i] << " "; - VSOMEIP_INFO << msg.str(); -#endif - queued_data_.resize(_size); - memcpy(&queued_data_[0], _data, _size); - return true; -} - void local_server_endpoint_impl::connection::send_magic_cookie() { } void local_server_endpoint_impl::connection::receive_cbk( boost::system::error_code const &_error, std::size_t _bytes) { - std::shared_ptr<endpoint_host> its_host = server_->host_.lock(); + if (_error == boost::asio::error::operation_aborted) { + stop(); + return; + } + std::shared_ptr<local_server_endpoint_impl> its_server(server_.lock()); + if (!its_server) { + VSOMEIP_TRACE << "local_server_endpoint_impl::connection::receive_cbk " + " couldn't lock server_"; + return; + } + std::shared_ptr<endpoint_host> its_host = its_server->host_.lock(); if (its_host) { std::size_t its_start; std::size_t its_end; @@ -269,13 +303,7 @@ void local_server_endpoint_impl::connection::receive_cbk( if (its_start != MESSAGE_IS_EMPTY && its_end + 3 < recv_buffer_size_ + its_iteration_gap) { its_host->on_message(&recv_buffer_[its_start], - uint32_t(its_end - its_start), server_); - - // If there was a queued message --> consume it now! - if (queued_data_.size() > 0) { - its_host->on_message(&queued_data_[0], static_cast<length_t>(queued_data_.size()), server_); - queued_data_.clear(); - } + uint32_t(its_end - its_start), its_server.get()); #if 0 std::stringstream local_msg; @@ -301,9 +329,14 @@ void local_server_endpoint_impl::connection::receive_cbk( } while (recv_buffer_size_ > 0 && its_start == FOUND_MESSAGE); } - if (_error == boost::asio::error::misc_errors::eof) { - server_->remove_connection(this); - } else { + if (_error == boost::asio::error::eof + || _error == boost::asio::error::connection_reset) { + { + std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); + stop(); + } + its_server->remove_connection(this); + } else if (_error != boost::asio::error::bad_descriptor) { start(); } } diff --git a/implementation/endpoints/src/tcp_client_endpoint_impl.cpp b/implementation/endpoints/src/tcp_client_endpoint_impl.cpp index d31a38b..97e5579 100644 --- a/implementation/endpoints/src/tcp_client_endpoint_impl.cpp +++ b/implementation/endpoints/src/tcp_client_endpoint_impl.cpp @@ -173,6 +173,11 @@ void tcp_client_endpoint_impl::send_magic_cookie(message_buffer_ptr_t &_buffer) void tcp_client_endpoint_impl::receive_cbk(
boost::system::error_code const &_error, std::size_t _bytes) {
+ if (_error == boost::asio::error::operation_aborted) {
+ // endpoint was stopped
+ shutdown_and_close_socket();
+ return;
+ }
#if 0
std::stringstream msg;
msg << "cei::rcb (" << _error.message() << "): ";
@@ -257,7 +262,11 @@ void tcp_client_endpoint_impl::receive_cbk( }
receive();
} else {
- if (socket_.is_open()) {
+ if (_error == boost::asio::error::connection_reset ||
+ _error == boost::asio::error::eof) {
+ VSOMEIP_TRACE << "tcp_client_endpoint: connection_reseted/EOF ~> close socket!";
+ shutdown_and_close_socket();
+ } else if (socket_.is_open()) {
receive();
}
}
diff --git a/implementation/endpoints/src/tcp_server_endpoint_impl.cpp b/implementation/endpoints/src/tcp_server_endpoint_impl.cpp index fc31850..f3035ef 100644 --- a/implementation/endpoints/src/tcp_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/tcp_server_endpoint_impl.cpp @@ -23,9 +23,19 @@ tcp_server_endpoint_impl::tcp_server_endpoint_impl( std::shared_ptr<endpoint_host> _host, endpoint_type _local, boost::asio::io_service &_io, std::uint32_t _max_message_size) : tcp_server_endpoint_base_impl(_host, _local, _io, _max_message_size), - acceptor_(_io, _local), + acceptor_(_io), current_(0) { is_supporting_magic_cookies_ = true; + + boost::system::error_code ec; + acceptor_.open(_local.protocol(), ec); + boost::asio::detail::throw_error(ec, "acceptor open"); + acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec); + boost::asio::detail::throw_error(ec, "acceptor set_option"); + acceptor_.bind(_local, ec); + boost::asio::detail::throw_error(ec, "acceptor bind"); + acceptor_.listen(boost::asio::socket_base::max_connections, ec); + boost::asio::detail::throw_error(ec, "acceptor listen"); } tcp_server_endpoint_impl::~tcp_server_endpoint_impl() { @@ -36,7 +46,9 @@ bool tcp_server_endpoint_impl::is_local() const { } void tcp_server_endpoint_impl::start() { - connection::ptr new_connection = connection::create(this, max_message_size_); + connection::ptr new_connection = connection::create( + std::dynamic_pointer_cast<tcp_server_endpoint_impl>( + shared_from_this()), max_message_size_); acceptor_.async_accept(new_connection->get_socket(), std::bind(&tcp_server_endpoint_impl::accept_cbk, @@ -46,13 +58,21 @@ void tcp_server_endpoint_impl::start() { } void tcp_server_endpoint_impl::stop() { - server_endpoint_impl::stop(); - for (auto& i : connections_) - i.second->stop(); if(acceptor_.is_open()) { boost::system::error_code its_error; acceptor_.close(its_error); } + { + std::lock_guard<std::mutex> its_lock(connections_mutex_); + for (const auto& c : connections_) { + if(c.second->get_socket().is_open()) { + boost::system::error_code its_error; + c.second->get_socket().cancel(its_error); + } + } + connections_.clear(); + } + server_endpoint_impl::stop(); } bool tcp_server_endpoint_impl::send_to( @@ -65,34 +85,38 @@ bool tcp_server_endpoint_impl::send_to( } void tcp_server_endpoint_impl::send_queued(queue_iterator_type _queue_iterator) { - auto connection_iterator = connections_.find(_queue_iterator->first); - if (connection_iterator != connections_.end()) { - connection_iterator->second->send_queued(_queue_iterator); - } else { - VSOMEIP_DEBUG << "Didn't find connection: " - << _queue_iterator->first.address().to_string() << ":" << std::dec - << static_cast<std::uint16_t>(_queue_iterator->first.port()) - << " dropping message."; - _queue_iterator->second.pop_front(); + connection::ptr its_connection; + { + std::lock_guard<std::mutex> its_lock(connections_mutex_); + auto connection_iterator = connections_.find(_queue_iterator->first); + if (connection_iterator != connections_.end()) { + its_connection = connection_iterator->second; + } else { + VSOMEIP_DEBUG << "Didn't find connection: " + << _queue_iterator->first.address().to_string() << ":" << std::dec + << static_cast<std::uint16_t>(_queue_iterator->first.port()) + << " dropping message."; + _queue_iterator->second.pop_front(); + } + } + if (its_connection) { + its_connection->send_queued(_queue_iterator); } } bool tcp_server_endpoint_impl::is_established(std::shared_ptr<endpoint_definition> _endpoint) { bool is_connected = false; endpoint_type endpoint(_endpoint->get_address(), _endpoint->get_port()); - auto connection_iterator = connections_.find(endpoint); - if (connection_iterator != connections_.end()) { -#if 0 - VSOMEIP_DEBUG << "tcp_server_endpoint_impl::is_established(): subscribers TCP connection for " - << endpoint.address().to_string() << ":" << std::dec - << static_cast<std::uint16_t>(endpoint.port()) - << " is established!" ; -#endif - is_connected = true; - } else { - VSOMEIP_DEBUG << "Didn't find TCP connection: Subscription rejected for: " - << endpoint.address().to_string() << ":" << std::dec - << static_cast<std::uint16_t>(endpoint.port()); + { + std::lock_guard<std::mutex> its_lock(connections_mutex_); + auto connection_iterator = connections_.find(endpoint); + if (connection_iterator != connections_.end()) { + is_connected = true; + } else { + VSOMEIP_DEBUG << "Didn't find TCP connection: Subscription " + << "rejected for: " << endpoint.address().to_string() << ":" + << std::dec << static_cast<std::uint16_t>(endpoint.port()); + } } return is_connected; } @@ -138,6 +162,19 @@ bool tcp_server_endpoint_impl::get_default_target(service_t, return false; } +void tcp_server_endpoint_impl::remove_connection( + tcp_server_endpoint_impl::connection *_connection) { + std::lock_guard<std::mutex> its_lock(connections_mutex_); + for (auto it = connections_.begin(); it != connections_.end();) { + if (it->second.get() == _connection) { + it = connections_.erase(it); + break; + } else { + ++it; + } + } +} + void tcp_server_endpoint_impl::accept_cbk(connection::ptr _connection, boost::system::error_code const &_error) { @@ -146,11 +183,13 @@ void tcp_server_endpoint_impl::accept_cbk(connection::ptr _connection, boost::system::error_code its_error; endpoint_type remote = new_connection_socket.remote_endpoint(its_error); if(!its_error) { + std::lock_guard<std::mutex> its_lock(connections_mutex_); connections_[remote] = _connection; _connection->start(); } } - if (_error != boost::asio::error::operation_aborted) { + if (_error != boost::asio::error::bad_descriptor + && _error != boost::asio::error::operation_aborted) { start(); } else { VSOMEIP_DEBUG << "Endpoint was stopped, don't starting again"; @@ -170,8 +209,9 @@ bool tcp_server_endpoint_impl::is_reliable() const { // class tcp_service_impl::connection /////////////////////////////////////////////////////////////////////////////// tcp_server_endpoint_impl::connection::connection( - tcp_server_endpoint_impl *_server, std::uint32_t _max_message_size) : - socket_(_server->service_), server_(_server), + std::weak_ptr<tcp_server_endpoint_impl> _server, + std::uint32_t _max_message_size) : + socket_(_server.lock()->service_), server_(_server), max_message_size_(_max_message_size), recv_buffer_(_max_message_size, 0), recv_buffer_size_(0) { @@ -179,7 +219,8 @@ tcp_server_endpoint_impl::connection::connection( tcp_server_endpoint_impl::connection::ptr tcp_server_endpoint_impl::connection::create( - tcp_server_endpoint_impl *_server, std::uint32_t _max_message_size) { + std::weak_ptr<tcp_server_endpoint_impl> _server, + std::uint32_t _max_message_size) { return ptr(new connection(_server, _max_message_size)); } @@ -211,25 +252,29 @@ void tcp_server_endpoint_impl::connection::receive() { void tcp_server_endpoint_impl::connection::stop() { std::lock_guard<std::mutex> its_lock(stop_mutex_); - if(socket_.is_open()) { - boost::system::error_code its_shutdown_error; - socket_.shutdown(socket_.shutdown_both, its_shutdown_error); - boost::system::error_code its_close_error; - socket_.close(its_close_error); + if (socket_.is_open()) { + boost::system::error_code its_error; + socket_.shutdown(socket_.shutdown_both, its_error); + socket_.close(its_error); } } void tcp_server_endpoint_impl::connection::send_queued( queue_iterator_type _queue_iterator) { + std::shared_ptr<tcp_server_endpoint_impl> its_server(server_.lock()); + if (!its_server) { + VSOMEIP_TRACE << "tcp_server_endpoint_impl::connection::send_queued " + " couldn't lock server_"; + return; + } message_buffer_ptr_t its_buffer = _queue_iterator->second.front(); - - if (server_->has_enabled_magic_cookies_) { + if (its_server->has_enabled_magic_cookies_) { send_magic_cookie(its_buffer); } boost::asio::async_write(socket_, boost::asio::buffer(*its_buffer), std::bind(&tcp_server_endpoint_base_impl::send_cbk, - server_->shared_from_this(), + its_server, _queue_iterator, std::placeholders::_1, std::placeholders::_2)); } @@ -251,6 +296,16 @@ bool tcp_server_endpoint_impl::connection::is_magic_cookie(size_t _offset) const void tcp_server_endpoint_impl::connection::receive_cbk( boost::system::error_code const &_error, std::size_t _bytes) { + if (_error == boost::asio::error::operation_aborted) { + stop(); + return; + } + std::shared_ptr<tcp_server_endpoint_impl> its_server(server_.lock()); + if (!its_server) { + VSOMEIP_ERROR << "tcp_server_endpoint_impl::connection::receive_cbk " + " couldn't lock server_"; + return; + } #if 0 std::stringstream msg; for (std::size_t i = 0; i < _bytes + recv_buffer_size_; ++i) @@ -258,7 +313,7 @@ void tcp_server_endpoint_impl::connection::receive_cbk( << (int) recv_buffer_[i] << " "; VSOMEIP_DEBUG<< msg.str(); #endif - std::shared_ptr<endpoint_host> its_host = server_->host_.lock(); + std::shared_ptr<endpoint_host> its_host = its_server->host_.lock(); if (its_host) { if (!_error && 0 < _bytes) { recv_buffer_size_ += _bytes; @@ -274,17 +329,17 @@ void tcp_server_endpoint_impl::connection::receive_cbk( if (has_full_message) { bool needs_forwarding(true); if (is_magic_cookie(its_iteration_gap)) { - server_->has_enabled_magic_cookies_ = true; + its_server->has_enabled_magic_cookies_ = true; } else { - if (server_->has_enabled_magic_cookies_) { + if (its_server->has_enabled_magic_cookies_) { uint32_t its_offset - = server_->find_magic_cookie(&recv_buffer_[its_iteration_gap], + = its_server->find_magic_cookie(&recv_buffer_[its_iteration_gap], recv_buffer_size_); if (its_offset < current_message_size) { VSOMEIP_ERROR << "Detected Magic Cookie within message data. Resyncing."; if (!is_magic_cookie(its_iteration_gap)) { its_host->on_error(&recv_buffer_[its_iteration_gap], - static_cast<length_t>(recv_buffer_size_), server_); + static_cast<length_t>(recv_buffer_size_), its_server.get()); } current_message_size = its_offset; needs_forwarding = false; @@ -306,50 +361,50 @@ void tcp_server_endpoint_impl::connection::receive_cbk( { std::lock_guard<std::mutex> its_lock(stop_mutex_); if (socket_.is_open()) { - server_->clients_mutex_.lock(); + its_server->clients_mutex_.lock(); boost::system::error_code its_error; endpoint_type its_endpoint(socket_.remote_endpoint(its_error)); if (!its_error) { - server_->clients_[its_client][its_session] = its_endpoint; + its_server->clients_[its_client][its_session] = its_endpoint; } - server_->clients_mutex_.unlock(); - server_->current_ = this; + its_server->clients_mutex_.unlock(); + its_server->current_ = this; } } } - if (!server_->has_enabled_magic_cookies_) { + if (!its_server->has_enabled_magic_cookies_) { its_host->on_message(&recv_buffer_[its_iteration_gap], - current_message_size, server_); + current_message_size, its_server.get()); } else { // Only call on_message without a magic cookie in front of the buffer! if (!is_magic_cookie(its_iteration_gap)) { its_host->on_message(&recv_buffer_[its_iteration_gap], - current_message_size, server_); + current_message_size, its_server.get()); } } } recv_buffer_size_ -= current_message_size; its_iteration_gap += current_message_size; - } else if (server_->has_enabled_magic_cookies_ && recv_buffer_size_ > 0){ + } else if (its_server->has_enabled_magic_cookies_ && recv_buffer_size_ > 0){ uint32_t its_offset = - server_->find_magic_cookie(&recv_buffer_[its_iteration_gap], + its_server->find_magic_cookie(&recv_buffer_[its_iteration_gap], recv_buffer_size_); if (its_offset < recv_buffer_size_) { VSOMEIP_ERROR << "Detected Magic Cookie within message data. Resyncing."; if (!is_magic_cookie(its_iteration_gap)) { its_host->on_error(&recv_buffer_[its_iteration_gap], - static_cast<length_t>(recv_buffer_size_), server_); + static_cast<length_t>(recv_buffer_size_), its_server.get()); } recv_buffer_size_ -= its_offset; its_iteration_gap += its_offset; has_full_message = true; // trigger next loop if (!is_magic_cookie(its_iteration_gap)) { its_host->on_error(&recv_buffer_[its_iteration_gap], - static_cast<length_t>(recv_buffer_size_), server_); + static_cast<length_t>(recv_buffer_size_), its_server.get()); } } } else if (current_message_size > max_message_size_) { - if (server_->has_enabled_magic_cookies_) { + if (its_server->has_enabled_magic_cookies_) { VSOMEIP_ERROR << "Received a TCP message which exceeds " << "maximum message size (" << std::dec << current_message_size @@ -376,21 +431,38 @@ void tcp_server_endpoint_impl::connection::receive_cbk( receive(); } } + if (_error == boost::asio::error::eof + || _error == boost::asio::error::connection_reset) { + { + std::lock_guard<std::mutex> its_lock(its_server->connections_mutex_); + stop(); + } + its_server->remove_connection(this); + } } client_t tcp_server_endpoint_impl::get_client(std::shared_ptr<endpoint_definition> _endpoint) { endpoint_type endpoint(_endpoint->get_address(), _endpoint->get_port()); - auto its_remote = connections_.find(endpoint); - if (its_remote != connections_.end()) { - return its_remote->second->get_client(endpoint); + { + std::lock_guard<std::mutex> its_lock(connections_mutex_); + auto its_remote = connections_.find(endpoint); + if (its_remote != connections_.end()) { + return its_remote->second->get_client(endpoint); + } } return 0; } client_t tcp_server_endpoint_impl::connection::get_client(endpoint_type _endpoint_type) { - std::lock_guard<std::mutex> its_lock(server_->clients_mutex_); - for (auto its_client : server_->clients_) { - for (auto its_session : server_->clients_[its_client.first]) { + std::shared_ptr<tcp_server_endpoint_impl> its_server(server_.lock()); + if (!its_server) { + VSOMEIP_TRACE << "tcp_server_endpoint_impl::connection::get_client " + " couldn't lock server_"; + return 0; + } + std::lock_guard<std::mutex> its_lock(its_server->clients_mutex_); + for (const auto its_client : its_server->clients_) { + for (const auto its_session : its_server->clients_[its_client.first]) { auto endpoint = its_session.second; if (endpoint == _endpoint_type) { // TODO: Check system byte order before convert! diff --git a/implementation/endpoints/src/udp_client_endpoint_impl.cpp b/implementation/endpoints/src/udp_client_endpoint_impl.cpp index a6029db..c92fff0 100644 --- a/implementation/endpoints/src/udp_client_endpoint_impl.cpp +++ b/implementation/endpoints/src/udp_client_endpoint_impl.cpp @@ -124,6 +124,11 @@ unsigned short udp_client_endpoint_impl::get_remote_port() const { void udp_client_endpoint_impl::receive_cbk(
boost::system::error_code const &_error, std::size_t _bytes) {
+ if (_error == boost::asio::error::operation_aborted) {
+ // endpoint was stopped
+ shutdown_and_close_socket();
+ return;
+ }
std::shared_ptr<endpoint_host> its_host = host_.lock();
if (!_error && 0 < _bytes && its_host) {
#if 0
@@ -147,7 +152,9 @@ void udp_client_endpoint_impl::receive_cbk( if (!_error) {
receive();
} else {
- if (socket_.is_open()) {
+ if (_error == boost::asio::error::connection_refused) {
+ shutdown_and_close_socket();
+ } else if (socket_.is_open()) {
receive();
}
}
diff --git a/implementation/endpoints/src/udp_server_endpoint_impl.cpp b/implementation/endpoints/src/udp_server_endpoint_impl.cpp index 8b3fd30..f1cd692 100644 --- a/implementation/endpoints/src/udp_server_endpoint_impl.cpp +++ b/implementation/endpoints/src/udp_server_endpoint_impl.cpp @@ -8,7 +8,6 @@ #include <boost/asio/ip/multicast.hpp> - #include "../include/endpoint_definition.hpp" #include "../include/endpoint_host.hpp" #include "../include/udp_server_endpoint_impl.hpp" @@ -40,6 +39,12 @@ udp_server_endpoint_impl::udp_server_endpoint_impl( = configuration::get()->get_unicast_address().to_v4(); boost::asio::ip::multicast::outbound_interface option(its_unicast_address); socket_.set_option(option); + } else if (_local.address().is_v6()) { + boost::asio::ip::address_v6 its_unicast_address + = configuration::get()->get_unicast_address().to_v6(); + boost::asio::ip::multicast::outbound_interface option( + static_cast<unsigned int>(its_unicast_address.scope_id())); + socket_.set_option(option); } socket_.bind(_local, ec); @@ -75,6 +80,7 @@ void udp_server_endpoint_impl::stop() { server_endpoint_impl::stop(); if (socket_.is_open()) { boost::system::error_code its_error; + socket_.shutdown(socket_type::shutdown_both, its_error); socket_.close(its_error); } } diff --git a/implementation/helper/boost/asio/detail/handler_type_requirements_ext.hpp b/implementation/helper/boost/asio/detail/handler_type_requirements_ext.hpp index d81fb42..3b66297 100644 --- a/implementation/helper/boost/asio/detail/handler_type_requirements_ext.hpp +++ b/implementation/helper/boost/asio/detail/handler_type_requirements_ext.hpp @@ -58,11 +58,15 @@ #if defined(__clang__)
# if defined(__apple_build_version__)
# if (__clang_major__ >= 7)
-# define BOOST_ASIO_UNUSED_TYPEDEF __attribute__((__unused__))
+# if !defined(BOOST_ASIO_UNUSED_TYPEDEF)
+# define BOOST_ASIO_UNUSED_TYPEDEF __attribute__((__unused__))
+# endif
# endif // (__clang_major__ >= 7)
# elif ((__clang_major__ == 3) && (__clang_minor__ >= 6)) \
|| (__clang_major__ > 3)
-# define BOOST_ASIO_UNUSED_TYPEDEF __attribute__((__unused__))
+# if !defined(BOOST_ASIO_UNUSED_TYPEDEF)
+# define BOOST_ASIO_UNUSED_TYPEDEF __attribute__((__unused__))
+# endif
# endif // ((__clang_major__ == 3) && (__clang_minor__ >= 6))
// || (__clang_major__ > 3)
#elif defined(__GNUC__)
diff --git a/implementation/logging/include/logger.hpp b/implementation/logging/include/logger.hpp index 7bbc00a..2d8cda4 100644 --- a/implementation/logging/include/logger.hpp +++ b/implementation/logging/include/logger.hpp @@ -19,14 +19,14 @@ namespace vsomeip {
-class VSOMEIP_EXPORT logger {
+class VSOMEIP_IMPORT_EXPORT logger {
public:
static std::shared_ptr<logger> get();
virtual ~logger() {
}
- virtual boost::log::sources::severity_logger<
+ virtual boost::log::sources::severity_logger_mt<
boost::log::trivial::severity_level> & get_internal() = 0;
};
diff --git a/implementation/logging/include/logger_impl.hpp b/implementation/logging/include/logger_impl.hpp index 3916874..9c7d0bf 100644 --- a/implementation/logging/include/logger_impl.hpp +++ b/implementation/logging/include/logger_impl.hpp @@ -37,7 +37,7 @@ public: logger_impl();
- boost::log::sources::severity_logger<
+ boost::log::sources::severity_logger_mt<
boost::log::trivial::severity_level> & get_internal();
private:
@@ -47,13 +47,14 @@ private: const std::string &_context_id);
private:
- boost::log::sources::severity_logger<
+ static boost::log::sources::severity_logger_mt<
boost::log::trivial::severity_level> logger_;
boost::log::trivial::severity_level loglevel_;
boost::shared_ptr<sink_t> console_sink_;
boost::shared_ptr<sink_t> file_sink_;
boost::shared_ptr<dlt_sink_t> dlt_sink_;
+ boost::log::core_ptr log_core_;
private:
void use_null_logger();
diff --git a/implementation/logging/src/logger_impl.cpp b/implementation/logging/src/logger_impl.cpp index 3934c1e..f7c1948 100644 --- a/implementation/logging/src/logger_impl.cpp +++ b/implementation/logging/src/logger_impl.cpp @@ -46,6 +46,9 @@ using namespace boost::log::trivial; namespace vsomeip {
+boost::log::sources::severity_logger_mt<
+ boost::log::trivial::severity_level> logger_impl::logger_;
+
std::shared_ptr<logger_impl> & logger_impl::get() {
static std::shared_ptr<logger_impl> the_logger__ = std::make_shared<
logger_impl>();
@@ -53,11 +56,12 @@ std::shared_ptr<logger_impl> & logger_impl::get() { }
logger_impl::logger_impl()
- : loglevel_(debug) {
+ : loglevel_(debug),
+ log_core_(logging::core::get()) {
logging::add_common_attributes();
}
-boost::log::sources::severity_logger<boost::log::trivial::severity_level> &
+boost::log::sources::severity_logger_mt<boost::log::trivial::severity_level> &
logger_impl::get_internal() {
return logger_;
}
diff --git a/implementation/message/include/payload_impl.hpp b/implementation/message/include/payload_impl.hpp index 66b3afa..b034918 100644 --- a/implementation/message/include/payload_impl.hpp +++ b/implementation/message/include/payload_impl.hpp @@ -32,6 +32,7 @@ public: VSOMEIP_EXPORT void set_data(const byte_t *_data, length_t _length);
VSOMEIP_EXPORT void set_data(const std::vector< byte_t > &_data);
+ VSOMEIP_EXPORT void set_data(std::vector< byte_t > &&_data);
VSOMEIP_EXPORT bool serialize(serializer *_to) const;
VSOMEIP_EXPORT bool deserialize(deserializer *_from);
diff --git a/implementation/message/include/serializer.hpp b/implementation/message/include/serializer.hpp index 7928a52..a61f9ef 100644 --- a/implementation/message/include/serializer.hpp +++ b/implementation/message/include/serializer.hpp @@ -15,7 +15,7 @@ namespace vsomeip { class serializable;
-class VSOMEIP_EXPORT serializer {
+class VSOMEIP_IMPORT_EXPORT serializer {
public:
serializer();
virtual ~serializer();
diff --git a/implementation/message/src/message_header_impl.cpp b/implementation/message/src/message_header_impl.cpp index 39446b7..3262c8d 100644 --- a/implementation/message/src/message_header_impl.cpp +++ b/implementation/message/src/message_header_impl.cpp @@ -48,12 +48,11 @@ bool message_header_impl::deserialize(deserializer *_from) { bool is_successful;
uint8_t tmp_message_type, tmp_return_code;
- uint32_t tmp_length;
is_successful = (0 != _from
&& _from->deserialize(service_)
&& _from->deserialize(method_)
- && _from->deserialize(tmp_length)
+ && _from->deserialize(length_)
&& _from->deserialize(client_)
&& _from->deserialize(session_)
&& _from->deserialize(protocol_version_)
@@ -64,7 +63,6 @@ bool message_header_impl::deserialize(deserializer *_from) { if (is_successful) {
type_ = static_cast< message_type_e >(tmp_message_type);
code_ = static_cast< return_code_e >(tmp_return_code);
- length_ = static_cast< length_t >(tmp_length - VSOMEIP_SOMEIP_HEADER_SIZE);
}
return is_successful;
diff --git a/implementation/message/src/message_impl.cpp b/implementation/message/src/message_impl.cpp index c19ae76..1d3d6bd 100644 --- a/implementation/message/src/message_impl.cpp +++ b/implementation/message/src/message_impl.cpp @@ -41,7 +41,7 @@ bool message_impl::deserialize(deserializer *_from) { payload_ = runtime::get()->create_payload();
bool is_successful = header_.deserialize(_from);
if (is_successful) {
- payload_->set_capacity(header_.length_);
+ payload_->set_capacity(header_.length_ - VSOMEIP_SOMEIP_HEADER_SIZE);
is_successful = payload_->deserialize(_from);
}
return is_successful;
diff --git a/implementation/message/src/payload_impl.cpp b/implementation/message/src/payload_impl.cpp index 3355788..663a7b8 100644 --- a/implementation/message/src/payload_impl.cpp +++ b/implementation/message/src/payload_impl.cpp @@ -64,6 +64,10 @@ void payload_impl::set_data(const std::vector< byte_t > &_data) { data_ = _data;
}
+void payload_impl::set_data(std::vector< byte_t > &&_data) {
+ data_ = std::move(_data);
+}
+
bool payload_impl::serialize(serializer *_to) const {
return (0 != _to && _to->serialize(data_.data(), uint32_t(data_.size())));
}
diff --git a/implementation/routing/include/event.hpp b/implementation/routing/include/event.hpp index 9033160..2a76ad8 100644 --- a/implementation/routing/include/event.hpp +++ b/implementation/routing/include/event.hpp @@ -13,9 +13,11 @@ #include <boost/asio/io_service.hpp> #include <boost/asio/ip/address.hpp> -#include <boost/asio/system_timer.hpp> +#include <boost/asio/steady_timer.hpp> #include <vsomeip/primitive_types.hpp> +#include <vsomeip/function_types.hpp> +#include <vsomeip/payload.hpp> namespace vsomeip { @@ -43,14 +45,17 @@ public: const std::shared_ptr<payload> get_payload() const; - void set_payload(std::shared_ptr<payload> _payload, const client_t _client); + void set_payload(const std::shared_ptr<payload> &_payload, + const client_t _client, bool _force = false); - void set_payload(std::shared_ptr<payload> _payload, - const std::shared_ptr<endpoint_definition> _target); + void set_payload(const std::shared_ptr<payload> &_payload, + const std::shared_ptr<endpoint_definition> _target, + bool _force = false); - void set_payload_dont_notify(std::shared_ptr<payload> _payload); + void set_payload_dont_notify(const std::shared_ptr<payload> &_payload); - void set_payload(std::shared_ptr<payload> _payload); + void set_payload(const std::shared_ptr<payload> &_payload, + bool _force = false); void unset_payload(bool _force = false); bool is_field() const; @@ -63,18 +68,20 @@ public: // SIP_RPC_357 void set_update_cycle(std::chrono::milliseconds &_cycle); + void set_change_resets_cycle(bool _change_resets_cycle); // SIP_RPC_358 void set_update_on_change(bool _is_on); - // SIP_RPC_359 (epsilon change) is not supported! + // SIP_RPC_359 (epsilon change) + void set_epsilon_change_function(const epsilon_change_func_t &_epsilon_change_func); const std::set<eventgroup_t> & get_eventgroups() const; void add_eventgroup(eventgroup_t _eventgroup); void set_eventgroups(const std::set<eventgroup_t> &_eventgroups); void notify_one(const std::shared_ptr<endpoint_definition> &_target); - void notify_one(client_t _client, bool _is_initial = false); + void notify_one(client_t _client); void add_ref(client_t _client, bool _is_provided); void remove_ref(client_t _client, bool _is_provided); @@ -91,17 +98,24 @@ private: void notify(); void notify(client_t _client, const std::shared_ptr<endpoint_definition> &_target); -private: - bool set_payload_helper(std::shared_ptr<payload> _payload); + void start_cycle(); + void stop_cycle(); + + bool compare(const std::shared_ptr<payload> &_lhs, const std::shared_ptr<payload> &_rhs) const; + + bool set_payload_helper(const std::shared_ptr<payload> &_payload, bool _force); + void reset_payload(const std::shared_ptr<payload> &_payload); +private: routing_manager *routing_; std::mutex mutex_; std::shared_ptr<message> message_; bool is_field_; - boost::asio::system_timer cycle_timer_; + boost::asio::steady_timer cycle_timer_; std::chrono::milliseconds cycle_; + bool change_resets_cycle_; bool is_updating_on_change_; @@ -115,6 +129,8 @@ private: bool is_shadow_; bool is_cache_placeholder_; + + epsilon_change_func_t epsilon_change_func_; }; } // namespace vsomeip diff --git a/implementation/routing/include/eventgroupinfo.hpp b/implementation/routing/include/eventgroupinfo.hpp index 7e832a1..fc3d321 100644 --- a/implementation/routing/include/eventgroupinfo.hpp +++ b/implementation/routing/include/eventgroupinfo.hpp @@ -25,7 +25,7 @@ class eventgroupinfo { public: struct target_t { std::shared_ptr<endpoint_definition> endpoint_; - std::chrono::high_resolution_clock::time_point expiration_; + std::chrono::steady_clock::time_point expiration_; bool operator==(const target_t &_other) const { return (endpoint_ == _other.endpoint_); @@ -47,19 +47,20 @@ public: uint16_t &_port) const; VSOMEIP_EXPORT void set_multicast(const boost::asio::ip::address &_address, uint16_t _port); + VSOMEIP_EXPORT bool is_sending_multicast() const; VSOMEIP_EXPORT const std::set<std::shared_ptr<event> > get_events() const; VSOMEIP_EXPORT void add_event(std::shared_ptr<event> _event); VSOMEIP_EXPORT void remove_event(std::shared_ptr<event> _event); VSOMEIP_EXPORT const std::list<target_t> get_targets() const; - VSOMEIP_EXPORT uint32_t get_unreliable_target_count(); + VSOMEIP_EXPORT uint32_t get_unreliable_target_count() const; VSOMEIP_EXPORT bool add_target(const target_t &_target); VSOMEIP_EXPORT bool add_target(const target_t &_target, const target_t &_subscriber); VSOMEIP_EXPORT bool update_target( const std::shared_ptr<endpoint_definition> &_target, - const std::chrono::high_resolution_clock::time_point &_expiration); + const std::chrono::steady_clock::time_point &_expiration); VSOMEIP_EXPORT bool remove_target( const std::shared_ptr<endpoint_definition> &_target); VSOMEIP_EXPORT void clear_targets(); @@ -68,6 +69,9 @@ public: VSOMEIP_EXPORT void clear_multicast_targets(); VSOMEIP_EXPORT const std::list<target_t> get_multicast_targets() const; + VSOMEIP_EXPORT uint8_t get_threshold() const; + VSOMEIP_EXPORT void set_threshold(uint8_t _threshold); + private: major_version_t major_; ttl_t ttl_; @@ -78,6 +82,8 @@ private: std::set<std::shared_ptr<event> > events_; std::list<target_t> targets_; std::list<target_t> multicast_targets_; + + uint8_t threshold_; }; } // namespace vsomeip diff --git a/implementation/routing/include/routing_manager.hpp b/implementation/routing/include/routing_manager.hpp index ee18957..dcbdd5a 100644 --- a/implementation/routing/include/routing_manager.hpp +++ b/implementation/routing/include/routing_manager.hpp @@ -12,6 +12,7 @@ #include <boost/asio/io_service.hpp> +#include <vsomeip/function_types.hpp> #include <vsomeip/message.hpp> #include <vsomeip/handler.hpp> @@ -35,12 +36,13 @@ public: virtual void start() = 0; virtual void stop() = 0; - virtual void offer_service(client_t _client, service_t _service, + virtual bool offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) = 0; virtual void stop_offer_service(client_t _client, service_t _service, - instance_t _instance, major_version_t _major, minor_version_t _minor) = 0; + instance_t _instance, major_version_t _major, + minor_version_t _minor) = 0; virtual void request_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, @@ -61,7 +63,7 @@ public: bool _flush) = 0; virtual bool send(client_t _client, const byte_t *_data, uint32_t _size, - instance_t _instance, bool _flush, bool _reliable, bool _initial) = 0; + instance_t _instance, bool _flush, bool _reliable) = 0; virtual bool send_to(const std::shared_ptr<endpoint_definition> &_target, std::shared_ptr<message>) = 0; @@ -69,13 +71,16 @@ public: virtual bool send_to(const std::shared_ptr<endpoint_definition> &_target, const byte_t *_data, uint32_t _size) = 0; - virtual void register_event(client_t _client, service_t _service, instance_t _instance, - event_t _event, const std::set<eventgroup_t> &_eventgroups, - bool _is_field, bool _is_provided, bool _is_shadow = false, + virtual void register_event(client_t _client, service_t _service, + instance_t _instance, event_t _event, + const std::set<eventgroup_t> &_eventgroups, bool _is_field, + std::chrono::milliseconds _cycle, bool _change_resets_cycle, + epsilon_change_func_t _epsilon_change_func, + bool _is_provided, bool _is_shadow = false, bool _is_cache_placeholder = false) = 0; - virtual void unregister_event(client_t _client, service_t _service, instance_t _instance, - event_t _event, bool _is_provided) = 0; + virtual void unregister_event(client_t _client, service_t _service, + instance_t _instance, event_t _event, bool _is_provided) = 0; virtual std::shared_ptr<event> get_event(service_t _service, instance_t _instance, event_t _event) const = 0; @@ -84,13 +89,15 @@ public: instance_t _instance, eventgroup_t _eventgroup) const = 0; virtual void notify(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload) = 0; + event_t _event, std::shared_ptr<payload> _payload, + bool _force) = 0; virtual void notify_one(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload, client_t _client) = 0; + event_t _event, std::shared_ptr<payload> _payload, + client_t _client, bool _force) = 0; - virtual void on_identify_response(client_t _client, service_t _service, instance_t _instance, - bool _reliable) = 0; + virtual void on_identify_response(client_t _client, service_t _service, + instance_t _instance, bool _reliable) = 0; }; } // namespace vsomeip diff --git a/implementation/routing/include/routing_manager_base.hpp b/implementation/routing/include/routing_manager_base.hpp index 1d69f6f..a7c127c 100644 --- a/implementation/routing/include/routing_manager_base.hpp +++ b/implementation/routing/include/routing_manager_base.hpp @@ -48,7 +48,7 @@ public: virtual void init(); - virtual void offer_service(client_t _client, service_t _service, + virtual bool offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor); @@ -63,9 +63,10 @@ public: instance_t _instance); virtual void register_event(client_t _client, service_t _service, instance_t _instance, - event_t _event, const std::set<eventgroup_t> &_eventgroups, - bool _is_field, bool _is_provided, bool _is_shadow = false, - bool _is_cache_placeholder = false); + event_t _event, const std::set<eventgroup_t> &_eventgroups, bool _is_field, + std::chrono::milliseconds _cycle, bool _change_resets_cycle, + epsilon_change_func_t _epsilon_change_func, + bool _is_provided, bool _is_shadow = false, bool _is_cache_placeholder = false); virtual void unregister_event(client_t _client, service_t _service, instance_t _instance, event_t _event, bool _is_provided); @@ -84,19 +85,18 @@ public: virtual void unsubscribe(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup); - virtual void notify(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload); + virtual void notify(service_t _service, instance_t _instance, + event_t _event, std::shared_ptr<payload> _payload, bool _force); - virtual void notify_one(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload, client_t _client); + virtual void notify_one(service_t _service, instance_t _instance, + event_t _event, std::shared_ptr<payload> _payload, + client_t _client, bool _force); virtual bool send(client_t _client, std::shared_ptr<message> _message, bool _flush); virtual bool send(client_t _client, const byte_t *_data, uint32_t _size, - instance_t _instance, bool _flush, bool _reliable, bool _initial) = 0; - - virtual bool queue_message(const byte_t *_data, uint32_t _size) const = 0; + instance_t _instance, bool _flush, bool _reliable) = 0; // Endpoint host ~> will be implemented by routing_manager_impl/_proxy/ virtual void on_connect(std::shared_ptr<endpoint> _endpoint) = 0; @@ -107,6 +107,8 @@ public: virtual void on_error(const byte_t *_data, length_t _length, endpoint *_receiver) = 0; + virtual void on_clientendpoint_error(client_t _client); + protected: std::shared_ptr<serviceinfo> find_service(service_t _service, instance_t _instance) const; std::shared_ptr<serviceinfo> create_service_info(service_t _service, @@ -138,15 +140,14 @@ protected: void remove_eventgroup_info(service_t _service, instance_t _instance, eventgroup_t _eventgroup); - void send_local_notification(client_t _client, + bool send_local_notification(client_t _client, const byte_t *_data, uint32_t _size, instance_t _instance, - bool _flush = true, bool _reliable = false, bool _inital = false); + bool _flush = true, bool _reliable = false); bool send_local( std::shared_ptr<endpoint> &_target, client_t _client, const byte_t *_data, uint32_t _size, instance_t _instance, - bool _flush, bool _reliable, uint8_t _command, bool _queue_message = false, - bool _initial = false) const; + bool _flush, bool _reliable, uint8_t _command) const; bool insert_subscription(service_t _service, instance_t _instance, eventgroup_t _eventgroup, client_t _client); diff --git a/implementation/routing/include/routing_manager_impl.hpp b/implementation/routing/include/routing_manager_impl.hpp index 13936a4..b08f2cd 100644 --- a/implementation/routing/include/routing_manager_impl.hpp +++ b/implementation/routing/include/routing_manager_impl.hpp @@ -58,7 +58,7 @@ public: void start(); void stop(); - void offer_service(client_t _client, service_t _service, + bool offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor); @@ -82,7 +82,7 @@ public: bool send(client_t _client, std::shared_ptr<message> _message, bool _flush); bool send(client_t _client, const byte_t *_data, uint32_t _size, - instance_t _instance, bool _flush, bool _reliable, bool _initial = false); + instance_t _instance, bool _flush, bool _reliable); bool send_to(const std::shared_ptr<endpoint_definition> &_target, std::shared_ptr<message> _message); @@ -103,10 +103,11 @@ public: bool _is_provided); void notify(service_t _service, instance_t _instance, event_t _event, - std::shared_ptr<payload> _payload); + std::shared_ptr<payload> _payload, bool _force); void notify_one(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload, client_t _client); + event_t _event, std::shared_ptr<payload> _payload, + client_t _client, bool _force); void on_subscribe_nack(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup); @@ -117,15 +118,15 @@ public: void on_identify_response(client_t _client, service_t _service, instance_t _instance, bool _reliable); - bool queue_message(const byte_t *_data, uint32_t _size) const; - // interface to stub std::shared_ptr<endpoint> find_local(client_t _client); std::shared_ptr<endpoint> find_or_create_local(client_t _client); void remove_local(client_t _client); - void on_stop_offer_service(service_t _service, instance_t _instance, + void on_stop_offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor); + void on_pong(client_t _client); + // interface "endpoint_host" std::shared_ptr<endpoint> find_or_create_remote_client(service_t _service, instance_t _instance, @@ -151,7 +152,7 @@ public: std::shared_ptr<endpoint> create_service_discovery_endpoint(const std::string &_address, uint16_t _port, bool _reliable); void init_routing_info(); - void add_routing_info(service_t _service, instance_t _instance, + std::chrono::milliseconds add_routing_info(service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor, ttl_t _ttl, const boost::asio::ip::address &_reliable_address, uint16_t _reliable_port, @@ -165,11 +166,11 @@ public: eventgroup_t _eventgroup, std::shared_ptr<endpoint_definition> _subscriber, std::shared_ptr<endpoint_definition> _target, - const std::chrono::high_resolution_clock::time_point &_expiration); + const std::chrono::steady_clock::time_point &_expiration); bool on_subscribe_accepted(service_t _service, instance_t _instance, eventgroup_t _eventgroup, std::shared_ptr<endpoint_definition> _target, - const std::chrono::high_resolution_clock::time_point &_expiration); + const std::chrono::steady_clock::time_point &_expiration); void on_unsubscribe(service_t _service, instance_t _instance, eventgroup_t _eventgroup, std::shared_ptr<endpoint_definition> _target); @@ -179,11 +180,14 @@ public: void expire_subscriptions(const boost::asio::ip::address &_address); void expire_services(const boost::asio::ip::address &_address); - std::chrono::high_resolution_clock::time_point expire_subscriptions(); + std::chrono::steady_clock::time_point expire_subscriptions(); bool has_identified(client_t _client, service_t _service, instance_t _instance, bool _reliable); + void on_clientendpoint_error(client_t _client); + void confirm_pending_offers(client_t _client); + private: bool deliver_message(const byte_t *_data, length_t _length, instance_t _instance, bool _reliable); @@ -197,7 +201,7 @@ private: std::shared_ptr<endpoint> create_client_endpoint( const boost::asio::ip::address &_address, - uint16_t _local_port, uint16_t _remote_port, + uint16_t _local_port, uint16_t _remote_port, bool _reliable, client_t _client, bool _start); std::shared_ptr<endpoint> create_server_endpoint(uint16_t _port, @@ -223,6 +227,9 @@ private: void stop_and_delete_client_endpoint(std::shared_ptr<endpoint> _endpoint); void clear_multicast_endpoints(service_t _service, instance_t _instance); + bool is_identifying(client_t _client, service_t _service, + instance_t _instance, bool _reliable); + private: return_code_e check_error(const byte_t *_data, length_t _size, instance_t _instance); @@ -244,6 +251,23 @@ private: client_t _client, const std::shared_ptr<endpoint_definition> &_target); + void log_version_timer_cbk(boost::system::error_code const & _error); + + void clear_remote_service_info(service_t _service, instance_t _instance, bool _reliable); + + bool handle_local_offer_service(client_t _client, service_t _service, + instance_t _instance, major_version_t _major,minor_version_t _minor); + + void remove_specific_client_endpoint(client_t _client, service_t _service, instance_t _instance, bool _reliable); + + void clear_identified_clients( service_t _service, instance_t _instance); + + void clear_identifying_clients( service_t _service, instance_t _instance); + + void remove_identified_client(service_t _service, instance_t _instance, client_t _client); + + void remove_identifying_client(service_t _service, instance_t _instance, client_t _client); + std::shared_ptr<routing_manager_stub> stub_; std::shared_ptr<sd::service_discovery> discovery_; @@ -272,17 +296,36 @@ private: std::mutex identified_clients_mutex_; std::mutex requested_services_mutex_; + std::mutex remote_subscribers_mutex_; std::map<service_t, std::map<instance_t, std::map<client_t, std::set<std::shared_ptr<endpoint_definition>>>>> remote_subscribers_; std::mutex specific_endpoint_clients_mutex_; std::map<service_t, std::map<instance_t, std::unordered_set<client_t>>>specific_endpoint_clients_; std::map<service_t, std::map<instance_t, - std::map<bool, std::unordered_set<client_t> > > >identified_clients_; + std::map<bool, std::unordered_set<client_t> > > > identified_clients_; + std::map<service_t, std::map<instance_t, + std::map<bool, std::unordered_set<client_t> > > > identifying_clients_; std::shared_ptr<serviceinfo> sd_info_; std::map<bool, std::set<uint16_t>> used_client_ports_; + + boost::asio::steady_timer version_log_timer_; + +#ifndef WITHOUT_SYSTEMD + boost::asio::steady_timer watchdog_timer_; + void watchdog_cbk(boost::system::error_code const &_error); +#endif + + std::mutex pending_offers_mutex_; + // map to store pending offers. + // 1st client id in tuple: client id of new offering application + // 2nd client id in tuple: client id of previously/stored offering application + std::map<service_t, + std::map<instance_t, + std::tuple<major_version_t, minor_version_t, + client_t, client_t>>> pending_offers_; }; } // namespace vsomeip diff --git a/implementation/routing/include/routing_manager_proxy.hpp b/implementation/routing/include/routing_manager_proxy.hpp index d2f61d7..bea1c24 100644 --- a/implementation/routing/include/routing_manager_proxy.hpp +++ b/implementation/routing/include/routing_manager_proxy.hpp @@ -10,6 +10,7 @@ #include <mutex> #include <boost/asio/io_service.hpp> +#include <boost/asio/steady_timer.hpp> #include "routing_manager_base.hpp" #include <vsomeip/enumeration_types.hpp> @@ -30,7 +31,7 @@ public: void start(); void stop(); - void offer_service(client_t _client, service_t _service, + bool offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor); @@ -52,8 +53,7 @@ public: eventgroup_t _eventgroup); bool send(client_t _client, const byte_t *_data, uint32_t _size, - instance_t _instance, bool _flush = true, bool _reliable = false, - bool _initial = false); + instance_t _instance, bool _flush = true, bool _reliable = false); bool send_to(const std::shared_ptr<endpoint_definition> &_target, std::shared_ptr<message> _message); @@ -63,15 +63,17 @@ public: void register_event(client_t _client, service_t _service, instance_t _instance, event_t _event, - const std::set<eventgroup_t> &_eventgroups, - bool _is_field, bool _is_provided, bool _is_shadow, bool _is_cache_placeholder); + const std::set<eventgroup_t> &_eventgroups, bool _is_field, + std::chrono::milliseconds _cycle, bool _change_resets_cycle, + epsilon_change_func_t _epsilon_change_func, + bool _is_provided, bool _is_shadow, bool _is_cache_placeholder); void unregister_event(client_t _client, service_t _service, instance_t _instance, event_t _event, bool _is_provided); void notify(service_t _service, instance_t _instance, event_t _event, - std::shared_ptr<payload> _payload); + std::shared_ptr<payload> _payload, bool _force); void on_connect(std::shared_ptr<endpoint> _endpoint); void on_disconnect(std::shared_ptr<endpoint> _endpoint); @@ -85,8 +87,6 @@ public: void on_identify_response(client_t _client, service_t _service, instance_t _instance, bool _reliable); - bool queue_message(const byte_t *_data, uint32_t _size) const; - private: void register_application(); void deregister_application(); @@ -131,10 +131,27 @@ private: void on_stop_offer_service(service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor); + void send_pending_commands(); + + void init_receiver(); + + void notify_remote_initally(service_t _service, instance_t _instance, + eventgroup_t _eventgroup); + + uint32_t get_remote_subscriber_count(service_t _service, instance_t _instance, + eventgroup_t _eventgroup, bool _increment); + + void register_application_timeout_cbk(boost::system::error_code const &_error); private: + enum class inner_state_type_e : std::uint8_t { + ST_REGISTERED = 0x0, + ST_DEREGISTERED = 0x1, + ST_REGISTERING = 0x2 + }; + bool is_connected_; bool is_started_; - state_type_e state_; + inner_state_type_e state_; std::shared_ptr<endpoint> sender_; // --> stub std::shared_ptr<endpoint> receiver_; // --> from everybody @@ -202,7 +219,16 @@ private: std::mutex send_mutex_; std::mutex deserialize_mutex_; - std::mutex pending_mutex_; + + std::mutex state_mutex_; + std::condition_variable state_condition_; + + std::map<service_t, + std::map<instance_t, std::map<eventgroup_t, uint32_t > > > remote_subscriber_count_; + + mutable std::recursive_mutex sender_mutex_; + + boost::asio::steady_timer register_application_timer_; }; } // namespace vsomeip diff --git a/implementation/routing/include/routing_manager_stub.hpp b/implementation/routing/include/routing_manager_stub.hpp index 27b8ec5..6255cda 100644 --- a/implementation/routing/include/routing_manager_stub.hpp +++ b/implementation/routing/include/routing_manager_stub.hpp @@ -15,7 +15,7 @@ #include <thread> #include <boost/asio/io_service.hpp> -#include <boost/asio/system_timer.hpp> +#include <boost/asio/steady_timer.hpp> #include "../../endpoints/include/endpoint_host.hpp" @@ -48,8 +48,6 @@ public: void on_stop_offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor); - bool queue_message(const byte_t *_data, uint32_t _size); - void send_subscribe(std::shared_ptr<vsomeip::endpoint> _target, client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup, @@ -57,7 +55,8 @@ public: void send_unsubscribe(std::shared_ptr<vsomeip::endpoint> _target, client_t _client, service_t _service, - instance_t _instance, eventgroup_t _eventgroup); + instance_t _instance, eventgroup_t _eventgroup, + bool _is_remote_subscriber); void send_subscribe_nack(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup); @@ -65,8 +64,15 @@ public: void send_subscribe_ack(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup); + bool contained_in_routing_info(client_t _client, service_t _service, + instance_t _instance, major_version_t _major, + minor_version_t _minor) const; + + void create_local_receiver(); + bool send_ping(client_t _client); + void deregister_erroneous_client(client_t _client); private: - void broadcast(std::vector<byte_t> &_command) const; + void broadcast(const std::vector<byte_t> &_command) const; void on_register_application(client_t _client); void on_deregister_application(client_t _client); @@ -81,11 +87,14 @@ private: void send_application_lost(std::list<client_t> &_lost); void client_registration_func(void); + void init_routing_endpoint(); + void on_ping_timer_expired(boost::system::error_code const &_error); + void remove_from_pinged_clients(client_t _client); private: routing_manager_stub_host *host_; boost::asio::io_service &io_; - boost::asio::system_timer watchdog_timer_; + boost::asio::steady_timer watchdog_timer_; std::string endpoint_path_; std::string local_receiver_path_; @@ -103,7 +112,18 @@ private: std::shared_ptr<std::thread> client_registration_thread_; std::mutex client_registration_mutex_; std::condition_variable client_registration_condition_; - std::map<client_t, std::vector<bool>> pending_client_registrations_; + + enum class registration_type_e : std::uint8_t { + REGISTER = 0x1, + DEREGISTER = 0x2, + DEREGISTER_ERROR_CASE = 0x3 + }; + std::map<client_t, std::vector<registration_type_e>> pending_client_registrations_; + static const std::vector<byte_t> its_ping_; + const std::chrono::milliseconds configured_watchdog_timeout_; + boost::asio::steady_timer pinged_clients_timer_; + std::mutex pinged_clients_mutex_; + std::map<client_t, boost::asio::steady_timer::time_point> pinged_clients_; }; } // namespace vsomeip diff --git a/implementation/routing/include/routing_manager_stub_host.hpp b/implementation/routing/include/routing_manager_stub_host.hpp index e216b5a..0d1decb 100644 --- a/implementation/routing/include/routing_manager_stub_host.hpp +++ b/implementation/routing/include/routing_manager_stub_host.hpp @@ -16,7 +16,7 @@ public: virtual ~routing_manager_stub_host() { } - virtual void offer_service(client_t _client, service_t _service, + virtual bool offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) = 0; @@ -59,7 +59,7 @@ public: service_t _service, instance_t _instance, const byte_t *_data, length_t _size, bool _notify_one = false) = 0; - virtual void on_stop_offer_service(service_t _service, + virtual void on_stop_offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) = 0; @@ -74,6 +74,10 @@ public: virtual void on_identify_response(client_t _client, service_t _service, instance_t _instance, bool _reliable) = 0; + + virtual void on_pong(client_t _client) = 0; + virtual void on_clientendpoint_error(client_t _client) = 0; + virtual void confirm_pending_offers(client_t _client) = 0; }; } // namespace vsomeip diff --git a/implementation/routing/src/event.cpp b/implementation/routing/src/event.cpp index d536597..c077844 100644 --- a/implementation/routing/src/event.cpp +++ b/implementation/routing/src/event.cpp @@ -21,11 +21,15 @@ event::event(routing_manager *_routing, bool _is_shadow) : message_(runtime::get()->create_notification()), is_field_(false), cycle_timer_(_routing->get_io()), + cycle_(std::chrono::milliseconds::zero()), + change_resets_cycle_(false), is_updating_on_change_(true), is_set_(false), is_provided_(false), is_shadow_(_is_shadow), - is_cache_placeholder_(false) { + is_cache_placeholder_(false), + epsilon_change_func_(std::bind(&event::compare, this, + std::placeholders::_1, std::placeholders::_2)) { } service_t event::get_service() const { @@ -84,33 +88,29 @@ const std::shared_ptr<payload> event::get_payload() const { return (message_->get_payload()); } -void event::set_payload_dont_notify(std::shared_ptr<payload> _payload) { +void event::set_payload_dont_notify(const std::shared_ptr<payload> &_payload) { if(is_cache_placeholder_) { - std::shared_ptr<payload> its_new_payload - = runtime::get()->create_payload( - _payload->get_data(), _payload->get_length()); - message_->set_payload(its_new_payload); + reset_payload(_payload); is_set_ = true; } else { - if (set_payload_helper(_payload)) { - std::shared_ptr<payload> its_new_payload - = runtime::get()->create_payload( - _payload->get_data(), _payload->get_length()); - message_->set_payload(its_new_payload); + if (set_payload_helper(_payload, false)) { + reset_payload(_payload); } } } -void event::set_payload(std::shared_ptr<payload> _payload) { +void event::set_payload(const std::shared_ptr<payload> &_payload, bool _force) { if (is_provided_) { - if (set_payload_helper(_payload)) { - std::shared_ptr<payload> its_new_payload - = runtime::get()->create_payload( - _payload->get_data(), _payload->get_length()); - - message_->set_payload(its_new_payload); + if (set_payload_helper(_payload, _force)) { + reset_payload(_payload); if (is_updating_on_change_) { + if (change_resets_cycle_) + stop_cycle(); + notify(); + + if (change_resets_cycle_) + start_cycle(); } } } else { @@ -119,16 +119,14 @@ void event::set_payload(std::shared_ptr<payload> _payload) { } } -void event::set_payload(std::shared_ptr<payload> _payload, client_t _client) { +void event::set_payload(const std::shared_ptr<payload> &_payload, client_t _client, + bool _force) { if (is_provided_) { - set_payload_helper(_payload); - std::shared_ptr<payload> its_new_payload - = runtime::get()->create_payload( - _payload->get_data(), _payload->get_length()); - - message_->set_payload(its_new_payload); - if (is_updating_on_change_) { - notify_one(_client); + if (set_payload_helper(_payload, _force)) { + reset_payload(_payload); + if (is_updating_on_change_) { + notify_one(_client); + } } } else { VSOMEIP_DEBUG << "Can't set payload for event " << std::hex @@ -136,15 +134,12 @@ void event::set_payload(std::shared_ptr<payload> _payload, client_t _client) { } } -void event::set_payload(std::shared_ptr<payload> _payload, - const std::shared_ptr<endpoint_definition> _target) { +void event::set_payload(const std::shared_ptr<payload> &_payload, + const std::shared_ptr<endpoint_definition> _target, + bool _force) { if (is_provided_) { - if (set_payload_helper(_payload)) { - std::shared_ptr<payload> its_new_payload - = runtime::get()->create_payload( - _payload->get_data(), _payload->get_length()); - - message_->set_payload(its_new_payload); + if (set_payload_helper(_payload, _force)) { + reset_payload(_payload); if (is_updating_on_change_) { notify_one(_target); } @@ -156,39 +151,42 @@ void event::set_payload(std::shared_ptr<payload> _payload, } void event::unset_payload(bool _force) { - if(_force) { + if (_force) { is_set_ = false; + stop_cycle(); message_->set_payload(std::make_shared<payload_impl>()); } else { if (is_provided_) { is_set_ = false; + stop_cycle(); message_->set_payload(std::make_shared<payload_impl>()); } } } -void event::set_update_on_change(bool _is_active) { - if (is_provided_) { - is_updating_on_change_ = _is_active; - } -} - void event::set_update_cycle(std::chrono::milliseconds &_cycle) { if (is_provided_) { + stop_cycle(); cycle_ = _cycle; + start_cycle(); + } +} - cycle_timer_.cancel(); +void event::set_change_resets_cycle(bool _change_resets_cycle) { + change_resets_cycle_ = _change_resets_cycle; +} - if (std::chrono::milliseconds::zero() != _cycle) { - cycle_timer_.expires_from_now(cycle_); - std::function<void(boost::system::error_code const &)> its_handler = - std::bind(&event::update_cbk, shared_from_this(), - std::placeholders::_1); - cycle_timer_.async_wait(its_handler); - } +void event::set_update_on_change(bool _is_active) { + if (is_provided_) { + is_updating_on_change_ = _is_active; } } +void event::set_epsilon_change_function(const epsilon_change_func_t &_epsilon_change_func) { + if (_epsilon_change_func) + epsilon_change_func_ = _epsilon_change_func; +} + const std::set<eventgroup_t> & event::get_eventgroups() const { return (eventgroups_); } @@ -230,42 +228,36 @@ void event::notify_one(const std::shared_ptr<endpoint_definition> &_target) { } } -void event::notify_one(client_t _client, bool _is_initial) { +void event::notify_one(client_t _client) { if (is_set_) { - const bool old_initial_value(message_->is_initial()); - if(_is_initial) { - message_->set_initial(true); - } routing_->send(_client, message_, true); - if(_is_initial) { - message_->set_initial(old_initial_value); - } } else { VSOMEIP_DEBUG << "Notify one event " << std::hex << message_->get_method() << " to client " << _client << " failed. Event payload not set!"; } } -bool event::set_payload_helper(std::shared_ptr<payload> _payload) { +bool event::set_payload_helper(const std::shared_ptr<payload> &_payload, bool _force) { std::shared_ptr<payload> its_payload = message_->get_payload(); bool is_change(!is_field_); if (is_field_) { - is_change = (its_payload->get_length() != _payload->get_length()); - if (!is_change) { - std::size_t its_pos = 0; - const byte_t *its_old_data = its_payload->get_data(); - const byte_t *its_new_data = _payload->get_data(); - while (!is_change && its_pos < its_payload->get_length()) { - is_change = (*its_old_data++ != *its_new_data++); - its_pos++; - } - } + is_change = _force || epsilon_change_func_(its_payload, _payload); } - is_set_ = true; - return is_change; } +void event::reset_payload(const std::shared_ptr<payload> &_payload) { + std::shared_ptr<payload> its_new_payload + = runtime::get()->create_payload( + _payload->get_data(), _payload->get_length()); + message_->set_payload(its_new_payload); + + if (!is_set_) + start_cycle(); + + is_set_ = true; +} + void event::add_ref(client_t _client, bool _is_provided) { auto its_client = refs_.find(_client); if (its_client == refs_.end()) { @@ -316,4 +308,36 @@ void event::set_cache_placeholder(bool _is_cache_place_holder) { is_cache_placeholder_ = _is_cache_place_holder; } -} // namespace vsomeip +void event::start_cycle() { + if (std::chrono::milliseconds::zero() != cycle_) { + cycle_timer_.expires_from_now(cycle_); + std::function<void(boost::system::error_code const &)> its_handler = + std::bind(&event::update_cbk, shared_from_this(), + std::placeholders::_1); + cycle_timer_.async_wait(its_handler); + } +} + +void event::stop_cycle() { + if (std::chrono::milliseconds::zero() != cycle_) { + boost::system::error_code ec; + cycle_timer_.cancel(ec); + } +} + +bool event::compare(const std::shared_ptr<payload> &_lhs, + const std::shared_ptr<payload> &_rhs) const { + bool is_change = (_lhs->get_length() != _rhs->get_length()); + if (!is_change) { + std::size_t its_pos = 0; + const byte_t *its_old_data = _lhs->get_data(); + const byte_t *its_new_data = _rhs->get_data(); + while (!is_change && its_pos < _lhs->get_length()) { + is_change = (*its_old_data++ != *its_new_data++); + its_pos++; + } + } + return is_change; +} + +} // namespace vsomeip diff --git a/implementation/routing/src/eventgroupinfo.cpp b/implementation/routing/src/eventgroupinfo.cpp index 1380969..fd63c4d 100644 --- a/implementation/routing/src/eventgroupinfo.cpp +++ b/implementation/routing/src/eventgroupinfo.cpp @@ -44,6 +44,12 @@ bool eventgroupinfo::is_multicast() const { return address_.is_multicast(); } +bool eventgroupinfo::is_sending_multicast() const { + return (is_multicast() && + threshold_ != 0 && + get_unreliable_target_count() >= threshold_); +} + bool eventgroupinfo::get_multicast(boost::asio::ip::address &_address, uint16_t &_port) const { @@ -77,7 +83,7 @@ const std::list<eventgroupinfo::target_t> eventgroupinfo::get_targets() const { return targets_; } -uint32_t eventgroupinfo::get_unreliable_target_count(){ +uint32_t eventgroupinfo::get_unreliable_target_count() const { uint32_t _count(0); for (auto i = targets_.begin(); i != targets_.end(); i++) { if (!i->endpoint_->is_reliable()) { @@ -114,8 +120,6 @@ bool eventgroupinfo::add_target(const eventgroupinfo::target_t &_target, const e std::size_t its_size = targets_.size(); if (std::find(targets_.begin(), targets_.end(), _subscriber) == targets_.end()) { targets_.push_back(_subscriber); - } - if (its_size != targets_.size()) { add_multicast_target(_target); } return (its_size != targets_.size()); @@ -123,12 +127,13 @@ bool eventgroupinfo::add_target(const eventgroupinfo::target_t &_target, const e bool eventgroupinfo::update_target( const std::shared_ptr<endpoint_definition> &_target, - const std::chrono::high_resolution_clock::time_point &_expiration) { + const std::chrono::steady_clock::time_point &_expiration) { for (auto i = targets_.begin(); i != targets_.end(); i++) { - if (i->endpoint_ == _target) { - i->expiration_ = _expiration; - return true; - } + if (i->endpoint_->get_address() == _target->get_address() && + i->endpoint_->get_port() == _target->get_port()) { + i->expiration_ = _expiration; + return true; + } } return false; } @@ -151,4 +156,12 @@ void eventgroupinfo::clear_targets() { targets_.clear(); } +uint8_t eventgroupinfo::get_threshold() const { + return threshold_; +} + +void eventgroupinfo::set_threshold(uint8_t _threshold) { + threshold_ = _threshold; +} + } // namespace vsomeip diff --git a/implementation/routing/src/routing_manager_base.cpp b/implementation/routing/src/routing_manager_base.cpp index 1a04a20..e146d65 100644 --- a/implementation/routing/src/routing_manager_base.cpp +++ b/implementation/routing/src/routing_manager_base.cpp @@ -11,6 +11,7 @@ #include "../../logging/include/logger.hpp" #include "../../endpoints/include/local_client_endpoint_impl.hpp" #include "../../endpoints/include/local_server_endpoint_impl.hpp" +#include "../include/routing_manager_impl.hpp" namespace vsomeip { @@ -42,7 +43,7 @@ void routing_manager_base::init() { serializer_->create_data(configuration_->get_max_message_size_local()); } -void routing_manager_base::offer_service(client_t _client, service_t _service, +bool routing_manager_base::offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) { (void)_client; @@ -50,16 +51,20 @@ void routing_manager_base::offer_service(client_t _client, service_t _service, // Remote route (incoming only) auto its_info = find_service(_service, _instance); if (its_info) { - if (its_info->get_major() == _major + if (!its_info->is_local()) { + return false; + } else if (its_info->get_major() == _major && its_info->get_minor() == _minor) { its_info->set_ttl(DEFAULT_TTL); } else { host_->on_error(error_code_e::SERVICE_PROPERTY_MISMATCH); + return false; } } else { its_info = create_service_info(_service, _instance, _major, _minor, DEFAULT_TTL, true); } + return true; } void routing_manager_base::stop_offer_service(client_t _client, service_t _service, @@ -118,9 +123,10 @@ void routing_manager_base::release_service(client_t _client, service_t _service, } void routing_manager_base::register_event(client_t _client, service_t _service, instance_t _instance, - event_t _event, const std::set<eventgroup_t> &_eventgroups, - bool _is_field, bool _is_provided, bool _is_shadow, - bool _is_cache_placeholder) { + event_t _event, const std::set<eventgroup_t> &_eventgroups, bool _is_field, + std::chrono::milliseconds _cycle, bool _change_resets_cycle, + epsilon_change_func_t _epsilon_change_func, + bool _is_provided, bool _is_shadow, bool _is_cache_placeholder) { std::shared_ptr<event> its_event = find_event(_service, _instance, _event); if (its_event) { if(!its_event->is_cache_placeholder()) { @@ -163,6 +169,9 @@ void routing_manager_base::register_event(client_t _client, service_t _service, its_event->set_eventgroups(_eventgroups); } + its_event->set_epsilon_change_function(_epsilon_change_func); + its_event->set_change_resets_cycle(_change_resets_cycle); + its_event->set_update_cycle(_cycle); } } else { its_event = std::make_shared<event>(this, _is_shadow); @@ -184,6 +193,10 @@ void routing_manager_base::register_event(client_t _client, service_t _service, } else { its_event->set_eventgroups(_eventgroups); } + + its_event->set_epsilon_change_function(_epsilon_change_func); + its_event->set_change_resets_cycle(_change_resets_cycle); + its_event->set_update_cycle(_cycle); } if(!_is_cache_placeholder) { @@ -312,10 +325,10 @@ void routing_manager_base::unsubscribe(client_t _client, service_t _service, } void routing_manager_base::notify(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload) { + event_t _event, std::shared_ptr<payload> _payload, bool _force) { std::shared_ptr<event> its_event = find_event(_service, _instance, _event); if (its_event) { - its_event->set_payload(_payload); + its_event->set_payload(_payload, _force); } else { VSOMEIP_WARNING << "Attempt to update the undefined event/field [" << std::hex << _service << "." << _instance << "." << _event @@ -324,7 +337,8 @@ void routing_manager_base::notify(service_t _service, instance_t _instance, } void routing_manager_base::notify_one(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload, client_t _client) { + event_t _event, std::shared_ptr<payload> _payload, + client_t _client, bool _force) { std::shared_ptr<event> its_event = find_event(_service, _instance, _event); if (its_event) { // Event is valid for service/instance @@ -340,7 +354,7 @@ void routing_manager_base::notify_one(service_t _service, instance_t _instance, } } if (found_eventgroup) { - its_event->set_payload(_payload, _client); + its_event->set_payload(_payload, _client, _force); } } else { VSOMEIP_WARNING << "Attempt to update the undefined event/field [" @@ -360,7 +374,7 @@ bool routing_manager_base::send(client_t its_client, if (serializer_->serialize(_message.get())) { is_sent = send(its_client, serializer_->get_data(), serializer_->get_size(), _message->get_instance(), - _flush, _message->is_reliable(), _message->is_initial()); + _flush, _message->is_reliable()); serializer_->reset(); } else { VSOMEIP_ERROR << "Failed to serialize message. Check message size!"; @@ -399,6 +413,9 @@ void routing_manager_base::clear_service_info(service_t _service, instance_t _in if (!its_info) { return; } + + std::lock_guard<std::mutex> its_lock(services_mutex_); + // Clear service_info and service_group std::shared_ptr<endpoint> its_empty_endpoint; if (!its_info->get_endpoint(!_reliable)) { @@ -467,7 +484,7 @@ std::shared_ptr<endpoint> routing_manager_base::create_local(client_t _client) { VSOMEIP_DEBUG << "Client [" << std::hex << get_client() << "] is connecting to [" << std::hex << _client << "] at " << its_path.str(); #endif - std::shared_ptr<endpoint> its_endpoint = std::make_shared< + std::shared_ptr<local_client_endpoint_impl> its_endpoint = std::make_shared< local_client_endpoint_impl>(shared_from_this(), #ifdef WIN32 boost::asio::ip::tcp::endpoint(address, port) @@ -482,7 +499,16 @@ std::shared_ptr<endpoint> routing_manager_base::create_local(client_t _client) { // with VSOMEIP_ROUTING_CLIENT as parameter // as it indicates remote communication! - local_endpoints_[_client] = its_endpoint; + local_endpoints_[_client] = std::static_pointer_cast<endpoint>(its_endpoint); + if (routing_manager_impl *rm_impl = dynamic_cast<routing_manager_impl*>(this)) { + its_endpoint->register_error_callback( + std::bind(&routing_manager_impl::on_clientendpoint_error, + rm_impl, _client)); + } else { + its_endpoint->register_error_callback( + std::bind(&routing_manager_base::on_clientendpoint_error, this, + _client)); + } } return (its_endpoint); @@ -637,6 +663,8 @@ std::shared_ptr<eventgroupinfo> routing_manager_base::find_eventgroup( } its_info->set_major(its_service_info->get_major()); its_info->set_ttl(its_service_info->get_ttl()); + its_info->set_threshold(configuration_->get_threshold( + _service, _instance, _eventgroup)); } } } @@ -673,9 +701,13 @@ std::set<client_t> routing_manager_base::find_local_clients(service_t _service, return (its_clients); } -void routing_manager_base::send_local_notification(client_t _client, +bool routing_manager_base::send_local_notification(client_t _client, const byte_t *_data, uint32_t _size, instance_t _instance, - bool _flush, bool _reliable, bool _initial) { + bool _flush, bool _reliable) { +#ifdef USE_DLT + bool has_local(false); +#endif + bool has_remote(false); method_t its_method = VSOMEIP_BYTES_TO_WORD(_data[VSOMEIP_METHOD_POS_MIN], _data[VSOMEIP_METHOD_POS_MAX]); service_t its_service = VSOMEIP_BYTES_TO_WORD( @@ -688,52 +720,46 @@ void routing_manager_base::send_local_notification(client_t _client, // local auto its_local_clients = find_local_clients(its_service, _instance, its_group); for (auto its_local_client : its_local_clients) { - // If we also want to receive the message, send it to the routing manager - // We cannot call deliver_message in this case as this would end in receiving - // an answer before the call to send has finished. - if (its_local_client == host_->get_client()) { - uint8_t *local_data = const_cast<uint8_t *>(_data); - local_data[VSOMEIP_CLIENT_POS_MIN] = VSOMEIP_WORD_BYTE1(its_local_client); - local_data[VSOMEIP_CLIENT_POS_MAX] = VSOMEIP_WORD_BYTE0(its_local_client); - std::shared_ptr<endpoint> its_local_target = find_local(get_client()); - if (its_local_target) { - send_local(its_local_target, _client, _data, _size, - _instance, _flush, _reliable, VSOMEIP_SEND, true, _initial); - } - std::memset(&local_data[VSOMEIP_CLIENT_POS_MIN], 0, 2); - } else { - std::shared_ptr<endpoint> its_local_target = find_local(its_local_client); - if (its_local_target) { - send_local(its_local_target, _client, _data, _size, - _instance, _flush, _reliable, VSOMEIP_SEND, false, _initial); - } + if (its_local_client == VSOMEIP_ROUTING_CLIENT) { + has_remote = true; + continue; + } +#ifdef USE_DLT + else { + has_local = true; + } +#endif + std::shared_ptr<endpoint> its_local_target = find_local(its_local_client); + if (its_local_target) { + send_local(its_local_target, _client, _data, _size, + _instance, _flush, _reliable, VSOMEIP_SEND); } } } } +#ifdef USE_DLT + // Trace the message if a local client but will _not_ be forwarded to the routing manager + if (has_local && !has_remote) { + uint16_t its_data_size + = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); + + tc::trace_header its_header; + if (its_header.prepare(nullptr, true)) + tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, + _data, its_data_size); + } +#endif + return has_remote; } bool routing_manager_base::send_local( std::shared_ptr<endpoint>& _target, client_t _client, - const byte_t *_data, uint32_t _size, - instance_t _instance, - bool _flush, bool _reliable, uint8_t _command, bool _queue_message, bool _initial) const { - - std::lock_guard<std::mutex> its_lock(local_endpoint_mutex_); - -#ifdef USE_DLT - uint16_t its_data_size - = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - - tc::trace_header its_header; - if (its_header.prepare(_target, true)) - tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, - _data, its_data_size); -#endif + const byte_t *_data, uint32_t _size, instance_t _instance, + bool _flush, bool _reliable, uint8_t _command) const { std::vector<byte_t> its_command( VSOMEIP_COMMAND_HEADER_SIZE + _size + sizeof(instance_t) - + sizeof(bool) + sizeof(bool) + sizeof(bool)); + + sizeof(bool) + sizeof(bool)); its_command[VSOMEIP_COMMAND_TYPE_POS] = _command; std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &_client, sizeof(client_t)); @@ -747,15 +773,8 @@ bool routing_manager_base::send_local( + sizeof(instance_t)], &_flush, sizeof(bool)); std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + _size + sizeof(instance_t) + sizeof(bool)], &_reliable, sizeof(bool)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + _size - + sizeof(instance_t) + sizeof(bool) + sizeof(bool)], - &_initial, sizeof(bool)); - if (_queue_message) { - return queue_message(&its_command[0], uint32_t(its_command.size())); - } else { - return _target->send(&its_command[0], uint32_t(its_command.size())); - } + return _target->send(&its_command[0], uint32_t(its_command.size())); } bool routing_manager_base::insert_subscription( @@ -779,4 +798,10 @@ bool routing_manager_base::insert_subscription( return true; } +void routing_manager_base::on_clientendpoint_error(client_t _client) { + VSOMEIP_ERROR << "Client endpoint error for application: " << std::hex + << std::setfill('0') << std::setw(4) << _client; + remove_local(_client); +} + } // namespace vsomeip diff --git a/implementation/routing/src/routing_manager_impl.cpp b/implementation/routing/src/routing_manager_impl.cpp index 3c98334..99c8c7d 100644 --- a/implementation/routing/src/routing_manager_impl.cpp +++ b/implementation/routing/src/routing_manager_impl.cpp @@ -7,6 +7,11 @@ #include <iomanip> #include <memory> #include <sstream> +#include <forward_list> + +#ifndef WITHOUT_SYSTEMD +#include <systemd/sd-daemon.h> +#endif #include <vsomeip/constants.hpp> #include <vsomeip/message.hpp> @@ -41,7 +46,12 @@ namespace vsomeip { routing_manager_impl::routing_manager_impl(routing_manager_host *_host) : - routing_manager_base(_host) { + routing_manager_base(_host), + version_log_timer_(_host->get_io()) +#ifndef WITHOUT_SYSTEMD + , watchdog_timer_(_host->get_io()) +#endif +{ } routing_manager_impl::~routing_manager_impl() { @@ -88,9 +98,29 @@ void routing_manager_impl::start() { discovery_->start(); host_->on_state(state_type_e::ST_REGISTERED); + + if (configuration_->log_version()) { + version_log_timer_.expires_from_now( + std::chrono::seconds(0)); + version_log_timer_.async_wait(std::bind(&routing_manager_impl::log_version_timer_cbk, + this, std::placeholders::_1)); + } + +#ifndef WITHOUT_SYSTEMD + watchdog_timer_.expires_from_now(std::chrono::seconds(0)); + watchdog_timer_.async_wait(std::bind(&routing_manager_impl::watchdog_cbk, + this, std::placeholders::_1)); +#endif } void routing_manager_impl::stop() { + version_log_timer_.cancel(); + +#ifndef WITHOUT_SYSTEMD + watchdog_timer_.cancel(); + sd_notify(0, "STOPPING=1"); +#endif + host_->on_state(state_type_e::ST_DEREGISTERED); if (discovery_) @@ -104,16 +134,20 @@ void routing_manager_impl::stop() { } } -void routing_manager_impl::offer_service(client_t _client, service_t _service, +bool routing_manager_impl::offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) { std::shared_ptr<serviceinfo> its_info = find_service(_service, _instance); - { - std::lock_guard<std::mutex> its_lock(local_services_mutex_); - local_services_[_service][_instance] = std::make_tuple(_major, _minor, _client); + VSOMEIP_DEBUG << "OFFER(" + << std::hex << std::setw(4) << std::setfill('0') << _client <<"): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance + << ":" << std::dec << int(_major) << "." << std::dec << _minor << "]"; + + if(!handle_local_offer_service(_client, _service, _instance, _major, _minor)) { + return false; } - routing_manager_base::offer_service(_client, _service, _instance, _major, _minor); init_service_info(_service, _instance, true); { @@ -136,20 +170,35 @@ void routing_manager_impl::offer_service(client_t _client, service_t _service, stub_->on_offer_service(_client, _service, _instance, _major, _minor); host_->on_availability(_service, _instance, true, _major, _minor); + return true; } void routing_manager_impl::stop_offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) { + + VSOMEIP_DEBUG << "STOP OFFER(" + << std::hex << std::setw(4) << std::setfill('0') << _client <<"): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance + << ":" << std::dec << int(_major) << "." << _minor << "]"; + routing_manager_base::stop_offer_service(_client, _service, _instance, _major, _minor); - on_stop_offer_service(_service, _instance, _major, _minor); + on_stop_offer_service(_client, _service, _instance, _major, _minor); stub_->on_stop_offer_service(_client, _service, _instance, _major, _minor); + host_->on_availability(_service, _instance, false, _major, _minor); } void routing_manager_impl::request_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor, bool _use_exclusive_proxy) { + VSOMEIP_DEBUG << "REQUEST(" + << std::hex << std::setw(4) << std::setfill('0') << _client << "): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance << ":" + << std::dec << int(_major) << "." << std::dec << _minor << "]"; + routing_manager_base::request_service(_client, _service, _instance, _major, _minor, _use_exclusive_proxy); @@ -159,10 +208,18 @@ void routing_manager_impl::request_service(client_t _client, service_t _service, std::lock_guard<std::mutex> ist_lock(requested_services_mutex_); requested_services_[_client][_service][_instance].insert({ _major, _minor }); } - // Unknown service instance ~> tell SD to find it! if (discovery_) { - discovery_->request_service(_service, _instance, _major, _minor, - DEFAULT_TTL); + if (!configuration_->is_local_service(_service, _instance)) { + // Non local service instance ~> tell SD to find it! + discovery_->request_service(_service, _instance, _major, _minor, + DEFAULT_TTL); + } else { + VSOMEIP_DEBUG << std::hex + << "Avoid trigger SD find-service message" + << " for local service/instance/major/minor: " + << _service << "/" << _instance << std::dec + << "/" << (uint32_t)_major << "/" << _minor; + } } } else { if ((_major == its_info->get_major() @@ -171,6 +228,11 @@ void routing_manager_impl::request_service(client_t _client, service_t _service, || DEFAULT_MINOR == its_info->get_minor() || _minor == ANY_MINOR)) { if(!its_info->is_local()) { + { + std::lock_guard<std::mutex> ist_lock(requested_services_mutex_); + requested_services_[_client][_service][_instance].insert({ _major, _minor }); + its_info->add_client(_client); + } find_or_create_remote_client(_service, _instance, true, VSOMEIP_ROUTING_CLIENT); if (_use_exclusive_proxy) { std::shared_ptr<endpoint> its_endpoint = its_info->get_endpoint(true); @@ -186,17 +248,53 @@ void routing_manager_impl::request_service(client_t _client, service_t _service, std::lock_guard<std::mutex> its_lock(specific_endpoint_clients_mutex_); specific_endpoint_clients_[_service][_instance].insert(_client); } + + if (_client == get_client()) { + stub_->create_local_receiver(); + } } void routing_manager_impl::release_service(client_t _client, service_t _service, instance_t _instance) { + + VSOMEIP_DEBUG << "RELEASE(" + << std::hex << std::setw(4) << std::setfill('0') << _client << "): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance << "]"; + routing_manager_base::release_service(_client, _service, _instance); + + { + std::lock_guard<std::mutex> its_lock(requested_services_mutex_); + auto its_client = requested_services_.find(_client); + if (its_client != requested_services_.end()) { + auto its_service = its_client->second.find(_service); + if (its_service != its_client->second.end()) { + auto its_instance = its_service->second.find(_instance); + if (its_instance != its_service->second.end()) { + its_service->second.erase(_instance); + } + } + } + } + std::shared_ptr<serviceinfo> its_info(find_service(_service, _instance)); - if(its_info) { + if(its_info && !its_info->is_local()) { if(!its_info->get_requesters_size()) { if(discovery_) { discovery_->release_service(_service, _instance); } + clear_client_endpoints(_service, _instance, true); + clear_client_endpoints(_service, _instance, false); + clear_service_info(_service, _instance, true); + clear_service_info(_service, _instance, false); + clear_identified_clients(_service, _instance); + clear_identifying_clients( _service, _instance); + } else { + remove_identified_client(_service, _instance, _client); + remove_identifying_client(_service, _instance, _client); + remove_specific_client_endpoint(_service, _instance, _client, true); + remove_specific_client_endpoint(_service, _instance, _client, false); } } else { if(discovery_) { @@ -208,45 +306,71 @@ void routing_manager_impl::release_service(client_t _client, service_t _service, void routing_manager_impl::subscribe(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup, major_version_t _major, subscription_type_e _subscription_type) { + + VSOMEIP_DEBUG << "SUBSCRIBE(" + << std::hex << std::setw(4) << std::setfill('0') << _client <<"): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance << "." + << std::hex << std::setw(4) << std::setfill('0') << _eventgroup << ":" + << std::dec << (uint16_t)_major << "]"; + if (get_client() == find_local_client(_service, _instance)) { - if (!host_->on_subscription(_service, _instance, _eventgroup, _client, true)) { - on_subscribe_nack(_client, _service, _instance, _eventgroup); + bool subscription_accepted = host_->on_subscription(_service, _instance, _eventgroup, _client, true); + (void) find_or_create_local(_client); + if (!subscription_accepted) { + stub_->send_subscribe_nack(_client, _service, _instance, _eventgroup); VSOMEIP_INFO << "Subscription request from client: 0x" << std::hex << _client << std::dec << " for eventgroup: 0x" << _eventgroup << " rejected from application handler."; return; } else { - on_subscribe_ack(_client, _service, _instance, _eventgroup); + stub_->send_subscribe_ack(_client, _service, _instance, _eventgroup); } routing_manager_base::subscribe(_client, _service, _instance, _eventgroup, _major, _subscription_type); } else { if (discovery_) { - bool inserted = insert_subscription(_service, _instance, _eventgroup, _client); - if (inserted) { - if (0 == find_local_client(_service, _instance)) { - client_t subscriber = VSOMEIP_ROUTING_CLIENT; - // subscriber != VSOMEIP_ROUTING_CLIENT implies to use its own endpoint - { - std::lock_guard<std::mutex> its_lock(specific_endpoint_clients_mutex_); - auto found_service = specific_endpoint_clients_.find(_service); - if (found_service != specific_endpoint_clients_.end()) { - auto found_instance = found_service->second.find(_instance); - if (found_instance != found_service->second.end()) { - auto found_client = found_instance->second.find(_client); - if(found_client != found_instance->second.end()) { - subscriber = _client; - if (supports_selective(_service, _instance)) { - identify_for_subscribe(_client, _service, _instance, _major); - } else { - VSOMEIP_INFO << "Subcribe to legacy selective service: " << std::hex - << _service << ":" << _instance << "."; - } + client_t subscriber = VSOMEIP_ROUTING_CLIENT; + if (0 == find_local_client(_service, _instance)) { + // subscriber != VSOMEIP_ROUTING_CLIENT implies to use its own endpoint + bool identify(false); + { + std::lock_guard<std::mutex> its_lock(specific_endpoint_clients_mutex_); + auto found_service = specific_endpoint_clients_.find(_service); + if (found_service != specific_endpoint_clients_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + auto found_client = found_instance->second.find(_client); + if(found_client != found_instance->second.end()) { + subscriber = _client; + if (supports_selective(_service, _instance)) { + identify = true; + } else { + VSOMEIP_INFO << "Subcribe to legacy selective service: " << std::hex + << _service << ":" << _instance << "."; } } } } + } + if(identify) { + identify_for_subscribe(_client, _service, _instance, _major); + } + } + bool inserted = insert_subscription(_service, _instance, _eventgroup, _client); + if (inserted) { + if (0 == find_local_client(_service, _instance)) { + static const ttl_t configured_ttl(configuration_->get_sd_ttl()); discovery_->subscribe(_service, _instance, _eventgroup, - _major, DEFAULT_TTL, subscriber, _subscription_type); + _major, configured_ttl, subscriber, _subscription_type); + auto its_eventgroup = find_eventgroup(_service, _instance, _eventgroup); + if (its_eventgroup) { + std::set<std::shared_ptr<event> > its_events + = its_eventgroup->get_events(); + for (auto e : its_events) { + if (e->is_field()) + e->notify_one(_client); + } + } } else { stub_->send_subscribe(routing_manager_base::find_local(_service, _instance), _client, _service, _instance, _eventgroup, _major, false); @@ -260,6 +384,14 @@ void routing_manager_impl::subscribe(client_t _client, service_t _service, void routing_manager_impl::unsubscribe(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup) { + + VSOMEIP_DEBUG << "UNSUBSCRIBE(" + << std::hex << std::setw(4) << std::setfill('0') << _client << "): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance << "." + << std::hex << std::setw(4) << std::setfill('0') << _eventgroup << "]"; + + bool last_subscriber_removed(false); if (discovery_) { { std::lock_guard<std::mutex> its_lock(eventgroup_clients_mutex_); @@ -272,6 +404,7 @@ void routing_manager_impl::unsubscribe(client_t _client, service_t _service, if (found_eventgroup != found_instance->second.end()) { found_eventgroup->second.erase(_client); if (0 == found_eventgroup->second.size()) { + last_subscriber_removed = true; eventgroup_clients_.erase(_eventgroup); } } @@ -295,10 +428,11 @@ void routing_manager_impl::unsubscribe(client_t _client, service_t _service, } } } - discovery_->unsubscribe(_service, _instance, _eventgroup, subscriber); + if( last_subscriber_removed ) + discovery_->unsubscribe(_service, _instance, _eventgroup, subscriber); } else { stub_->send_unsubscribe(routing_manager_base::find_local(_service, _instance), - _client, _service, _instance, _eventgroup); + _client, _service, _instance, _eventgroup, false); } clear_multicast_endpoints(_service, _instance); } else { @@ -308,12 +442,12 @@ void routing_manager_impl::unsubscribe(client_t _client, service_t _service, bool routing_manager_impl::send(client_t _client, std::shared_ptr<message> _message, bool _flush) { - return routing_manager_base::send(_client, _message, _flush); + return routing_manager_base::send(_client, _message, _flush); } bool routing_manager_impl::send(client_t _client, const byte_t *_data, length_t _size, instance_t _instance, - bool _flush, bool _reliable, bool _initial) { + bool _flush, bool _reliable) { bool is_sent(false); std::shared_ptr<endpoint> its_target; @@ -345,7 +479,7 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, } if (its_target) { - is_sent = send_local(its_target, _client, _data, _size, _instance, _flush, _reliable, VSOMEIP_SEND, false, _initial); + is_sent = send_local(its_target, _client, _data, _size, _instance, _flush, _reliable, VSOMEIP_SEND); } else { // Check whether hosting application should get the message // If not, check routes to external @@ -394,8 +528,10 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, _data[VSOMEIP_METHOD_POS_MAX]); std::shared_ptr<event> its_event = find_event(its_service, _instance, its_method); if (its_event) { +#ifdef USE_DLT + bool has_sent(false); +#endif std::vector< byte_t > its_data; - for (auto its_group : its_event->get_eventgroups()) { // we need both endpoints as clients can subscribe to events via TCP and UDP std::shared_ptr<endpoint> its_unreliable_target = its_info->get_endpoint(false); @@ -404,45 +540,44 @@ bool routing_manager_impl::send(client_t _client, const byte_t *_data, // remote auto its_eventgroup = find_eventgroup(its_service, _instance, its_group); if (its_eventgroup) { - //Unicast targets + // Unicast targets for (auto its_remote : its_eventgroup->get_targets()) { if(its_remote.endpoint_->is_reliable() && its_reliable_target) { -#ifdef USE_DLT - uint16_t its_data_size - = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - - tc::trace_header its_header; - if (its_header.prepare(its_reliable_target, true)) - tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, - _data, its_data_size); -#endif its_reliable_target->send_to(its_remote.endpoint_, _data, _size); - } else if(its_unreliable_target && !its_eventgroup->is_multicast()) { -#ifdef USE_DLT - uint16_t its_data_size - = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); - - tc::trace_header its_header; - if (its_header.prepare(its_reliable_target, true)) - tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, - _data, its_data_size); -#endif + } else if (its_unreliable_target && !its_eventgroup->is_sending_multicast()) { its_unreliable_target->send_to(its_remote.endpoint_, _data, _size); } +#ifdef USE_DLT + has_sent = true; +#endif } - //send to multicast targets if subscribers are still interested - if(its_eventgroup->is_multicast() - && its_eventgroup->get_unreliable_target_count() > 0 ) { + // Send to multicast targets if subscribers are still interested + if (its_eventgroup->is_sending_multicast()) { for (auto its_multicast_target : its_eventgroup->get_multicast_targets()) { its_unreliable_target->send_to(its_multicast_target.endpoint_, _data, _size); +#ifdef USE_DLT + has_sent = true; +#endif } } +#ifdef USE_DLT + if (has_sent) { + uint16_t its_data_size + = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); + + tc::trace_header its_header; + if (its_header.prepare(nullptr, true)) + tc_->trace(its_header.data_, VSOMEIP_TRACE_HEADER_SIZE, + _data, its_data_size); + } +#endif } } } } } else { - its_target = is_service_discovery ? sd_info_->get_endpoint(false) : its_info->get_endpoint(_reliable); + its_target = is_service_discovery ? + (sd_info_ ? sd_info_->get_endpoint(false) : nullptr) : its_info->get_endpoint(_reliable); if (its_target) { #ifdef USE_DLT uint16_t its_data_size @@ -496,7 +631,6 @@ bool routing_manager_impl::send_to( std::shared_ptr<endpoint> its_endpoint = find_server_endpoint( _target->get_remote_port(), _target->is_reliable()); - if (its_endpoint) { #ifdef USE_DLT uint16_t its_data_size @@ -538,7 +672,10 @@ void routing_manager_impl::register_shadow_event(client_t _client, event_t _event, const std::set<eventgroup_t> &_eventgroups, bool _is_field, bool _is_provided) { routing_manager_base::register_event(_client, _service, _instance, - _event, _eventgroups, _is_field, _is_provided, true); + _event, _eventgroups, _is_field, + std::chrono::milliseconds::zero(), false, + nullptr, + _is_provided, true); } void routing_manager_impl::unregister_shadow_event(client_t _client, @@ -550,10 +687,10 @@ void routing_manager_impl::unregister_shadow_event(client_t _client, void routing_manager_impl::notify( service_t _service, instance_t _instance, event_t _event, - std::shared_ptr<payload> _payload) { + std::shared_ptr<payload> _payload, bool _force) { std::shared_ptr<event> its_event = find_event(_service, _instance, _event); if (its_event) { - its_event->set_payload(_payload); + its_event->set_payload(_payload, _force); } else { VSOMEIP_WARNING << "Attempt to update the undefined event/field [" << std::hex << _service << "." << _instance << "." << _event @@ -562,47 +699,49 @@ void routing_manager_impl::notify( } void routing_manager_impl::notify_one(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload, client_t _client) { - if (find_local(_client)) { - routing_manager_base::notify_one(_service, _instance, _event, _payload, - _client); - } else { - std::shared_ptr<event> its_event = find_event(_service, _instance, _event); - if (its_event) { - // Event is valid for service/instance - bool found_eventgroup(false); - // Iterate over all groups of the event to ensure at least - // one valid eventgroup for service/instance exists. - for (auto its_group : its_event->get_eventgroups()) { - auto its_eventgroup = find_eventgroup(_service, _instance, its_group); - if (its_eventgroup) { - // Eventgroup is valid for service/instance - found_eventgroup = true; - break; - } - } - if (found_eventgroup) { - // Now set event's payload! - // Either with endpoint_definition (remote) or with client (local). - auto its_service = remote_subscribers_.find(_service); - if (its_service != remote_subscribers_.end()) { - auto its_instance = its_service->second.find(_instance); - if (its_instance != its_service->second.end()) { - auto its_subscriber = its_instance->second.find(_client); - if (its_subscriber != its_instance->second.end()) { - for (auto its_target : its_subscriber->second) { - its_event->set_payload(_payload, its_target); - } - } - } - } - } - } else { - VSOMEIP_WARNING << "Attempt to update the undefined event/field [" - << std::hex << _service << "." << _instance << "." << _event - << "]"; - } - } + event_t _event, std::shared_ptr<payload> _payload, client_t _client, + bool _force) { + if (find_local(_client)) { + routing_manager_base::notify_one(_service, _instance, _event, _payload, + _client, _force); + } else { + std::shared_ptr<event> its_event = find_event(_service, _instance, _event); + if (its_event) { + // Event is valid for service/instance + bool found_eventgroup(false); + // Iterate over all groups of the event to ensure at least + // one valid eventgroup for service/instance exists. + for (auto its_group : its_event->get_eventgroups()) { + auto its_eventgroup = find_eventgroup(_service, _instance, its_group); + if (its_eventgroup) { + // Eventgroup is valid for service/instance + found_eventgroup = true; + break; + } + } + if (found_eventgroup) { + // Now set event's payload! + // Either with endpoint_definition (remote) or with client (local). + std::lock_guard<std::mutex> its_lock(remote_subscribers_mutex_); + auto its_service = remote_subscribers_.find(_service); + if (its_service != remote_subscribers_.end()) { + auto its_instance = its_service->second.find(_instance); + if (its_instance != its_service->second.end()) { + auto its_subscriber = its_instance->second.find(_client); + if (its_subscriber != its_instance->second.end()) { + for (auto its_target : its_subscriber->second) { + its_event->set_payload(_payload, its_target, _force); + } + } + } + } + } + } else { + VSOMEIP_WARNING << "Attempt to update the undefined event/field [" + << std::hex << _service << "." << _instance << "." << _event + << "]"; + } + } } void routing_manager_impl::on_error(const byte_t *_data, length_t _length, endpoint *_receiver) { @@ -617,7 +756,7 @@ void routing_manager_impl::on_error(const byte_t *_data, length_t _length, endpo } void routing_manager_impl::release_port(uint16_t _port, bool _reliable) { - used_client_ports_[_reliable].erase(_port); + used_client_ports_[_reliable].erase(_port); } void routing_manager_impl::on_message(const byte_t *_data, length_t _size, @@ -652,6 +791,13 @@ void routing_manager_impl::on_message(const byte_t *_data, length_t _size, } } else { instance_t its_instance = find_instance(its_service, _receiver); + //Ignore messages with invalid message type + if(_size >= VSOMEIP_MESSAGE_TYPE_POS) { + if(!utility::is_valid_message_type(static_cast<message_type_e>(_data[VSOMEIP_MESSAGE_TYPE_POS]))) { + VSOMEIP_ERROR << "Ignored SomeIP message with invalid message type."; + return; + } + } return_code_e return_code = check_error(_data, _size, its_instance); if(!(_size >= VSOMEIP_MESSAGE_TYPE_POS && utility::is_request_no_return(_data[VSOMEIP_MESSAGE_TYPE_POS]))) { if (return_code != return_code_e::E_OK && return_code != return_code_e::E_NOT_OK) { @@ -736,46 +882,78 @@ void routing_manager_impl::on_notification(client_t _client, &_data[VSOMEIP_PAYLOAD_POS], its_length); - if (!_notify_one) { - its_event->set_payload(its_payload); + if (_notify_one) { + notify_one(_service, _instance, its_event->get_event(), its_payload, _client, true); } else { - notify_one(_service, _instance, its_event->get_event(), its_payload, _client); + if (its_event->is_field()) { + if (its_event->is_set()) { + its_event->set_payload(its_payload); + } else { + // Set payload first time ~> notify all remote subscriber per unicast (inital field) + for (auto its_group : its_event->get_eventgroups()) { + auto its_eventgroup = find_eventgroup(_service, _instance, its_group); + if (its_eventgroup) { + //Unicast targets + for (auto its_remote : its_eventgroup->get_targets()) { + its_event->set_payload(its_payload, its_remote.endpoint_, true); + } + } + } + } + } else { + its_event->set_payload(its_payload); + } } } } void routing_manager_impl::on_connect(std::shared_ptr<endpoint> _endpoint) { - // Is called when endpoint->connect succeded! - for (auto &its_service : remote_services_) { - for (auto &its_instance : its_service.second) { - for (auto &its_client : its_instance.second) { - if (its_client.first == VSOMEIP_ROUTING_CLIENT || - its_client.first == get_client()) { - auto found_endpoint = its_client.second.find(false); - if (found_endpoint != its_client.second.end()) { - if (found_endpoint->second.get() == _endpoint.get()) { - std::shared_ptr<serviceinfo> its_info(find_service(its_service.first, its_instance.first)); - if(!its_info) { - return; + // Is called when endpoint->connect succeeded! + struct service_info { + service_t service_id_; + instance_t instance_id_; + major_version_t major_; + minor_version_t minor_; + bool reliable_; + std::shared_ptr<endpoint> endpoint_; + }; + std::forward_list<struct service_info> services_to_report_; + { + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); + for (auto &its_service : remote_services_) { + for (auto &its_instance : its_service.second) { + for (auto &its_client : its_instance.second) { + if (its_client.first == VSOMEIP_ROUTING_CLIENT || + its_client.first == get_client()) { + auto found_endpoint = its_client.second.find(false); + if (found_endpoint != its_client.second.end()) { + if (found_endpoint->second.get() == _endpoint.get()) { + std::shared_ptr<serviceinfo> its_info(find_service(its_service.first, its_instance.first)); + if (!its_info) { + return; + } + services_to_report_.push_front( + { its_service.first, + its_instance.first, + its_info->get_major(), + its_info->get_minor(), + false, nullptr }); } - host_->on_availability(its_service.first, its_instance.first, - true, its_info->get_major(), its_info->get_minor()); } - } - found_endpoint = its_client.second.find(true); - if (found_endpoint != its_client.second.end()) { - if (found_endpoint->second.get() == _endpoint.get()) { - std::shared_ptr<serviceinfo> its_info(find_service(its_service.first, its_instance.first)); - if(!its_info){ - return; - } - host_->on_availability(its_service.first, its_instance.first, - true, its_info->get_major(), its_info->get_minor()); - stub_->on_offer_service(VSOMEIP_ROUTING_CLIENT, - its_service.first, its_instance.first, its_info->get_major(), its_info->get_minor()); - if(discovery_) { - discovery_->on_reliable_endpoint_connected(its_service.first, its_instance.first, _endpoint); + found_endpoint = its_client.second.find(true); + if (found_endpoint != its_client.second.end()) { + if (found_endpoint->second.get() == _endpoint.get()) { + std::shared_ptr<serviceinfo> its_info(find_service(its_service.first, its_instance.first)); + if (!its_info) { + return; + } + services_to_report_.push_front( + { its_service.first, + its_instance.first, + its_info->get_major(), + its_info->get_minor(), + true, _endpoint }); } } } @@ -783,10 +961,23 @@ void routing_manager_impl::on_connect(std::shared_ptr<endpoint> _endpoint) { } } } + for (const auto &s : services_to_report_) { + host_->on_availability(s.service_id_, s.instance_id_, true, s.major_, + s.minor_); + if (s.reliable_) { + stub_->on_offer_service(VSOMEIP_ROUTING_CLIENT, s.service_id_, + s.instance_id_, s.major_, s.minor_); + if (discovery_) { + discovery_->on_reliable_endpoint_connected(s.service_id_, + s.instance_id_, s.endpoint_); + } + } + } } void routing_manager_impl::on_disconnect(std::shared_ptr<endpoint> _endpoint) { // Is called when endpoint->connect fails! + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); for (auto &its_service : remote_services_) { for (auto &its_instance : its_service.second) { for (auto &its_client : its_instance.second) { @@ -822,9 +1013,43 @@ void routing_manager_impl::on_disconnect(std::shared_ptr<endpoint> _endpoint) { } } -void routing_manager_impl::on_stop_offer_service(service_t _service, +void routing_manager_impl::on_stop_offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) { - + { + std::lock_guard<std::mutex> its_lock(local_services_mutex_); + auto found_service = local_services_.find(_service); + if (found_service != local_services_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + if ( std::get<0>(found_instance->second) != _major + || std::get<1>(found_instance->second) != _minor + || std::get<2>(found_instance->second) != _client) { + VSOMEIP_WARNING + << "routing_manager_impl::on_stop_offer_service: " + << "trying to delete service not matching exactly " + << "the one offered previously: " << "[" << std::hex + << std::setw(4) << std::setfill('0') << _service + << "." << _instance << "." << std::dec + << static_cast<std::uint32_t>(_major) + << "." << _minor << "] by application: " + << std::hex << std::setw(4) << std::setfill('0') + << _client << ". Stored: [" << std::hex + << std::setw(4) << std::setfill('0') << _service + << "." << _instance << "." << std::dec + << static_cast<std::uint32_t>(std::get<0>(found_instance->second)) << "." + << std::get<1>(found_instance->second) + << "] by application: " << std::hex << std::setw(4) + << std::setfill('0') << std::get<2>(found_instance->second); + } + if (std::get<2>(found_instance->second) == _client) { + found_service->second.erase(_instance); + if (found_service->second.size() == 0) { + local_services_.erase(_service); + } + } + } + } + } { std::lock_guard<std::mutex> its_lock(events_mutex_); auto its_events_service = events_.find(_service); @@ -960,7 +1185,7 @@ bool routing_manager_impl::deliver_notification( if (its_event) { std::vector< byte_t > its_data; std::set<client_t> its_local_client_set; - if(its_event->is_field() && !its_event->is_shadow()) { + if(its_event->is_field() && !its_event->is_provided()) { // store the current value of the remote field const uint32_t its_length(utility::get_payload_size(_data, _length)); std::shared_ptr<payload> its_payload = @@ -1072,6 +1297,7 @@ void routing_manager_impl::init_service_info( if (its_reliable_endpoint) { its_info->set_endpoint(its_reliable_endpoint, true); its_reliable_endpoint->increment_use_count(); + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); service_instances_[_service][its_reliable_endpoint.get()] = _instance; } @@ -1083,6 +1309,7 @@ void routing_manager_impl::init_service_info( if (its_unreliable_endpoint) { its_info->set_endpoint(its_unreliable_endpoint, false); its_unreliable_endpoint->increment_use_count(); + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); service_instances_[_service][its_unreliable_endpoint.get()] = _instance; } @@ -1229,6 +1456,22 @@ std::shared_ptr<endpoint> routing_manager_impl::find_or_create_local( void routing_manager_impl::remove_local(client_t _client) { routing_manager_base::remove_local(_client); + std::forward_list<std::pair<service_t, instance_t>> services_to_release_; + { + std::lock_guard<std::mutex> its_lock(requested_services_mutex_); + auto its_client = requested_services_.find(_client); + if (its_client != requested_services_.end()) { + for (auto its_service : its_client->second) { + for (auto its_instance : its_service.second) { + services_to_release_.push_front( + { its_service.first, its_instance.first }); + } + } + } + } + for (const auto &s : services_to_release_) { + release_service(_client, s.first, s.second); + } } instance_t routing_manager_impl::find_instance(service_t _service, @@ -1251,39 +1494,39 @@ std::shared_ptr<endpoint> routing_manager_impl::create_remote_client( uint16_t its_local_port; if (configuration_->get_client_port(_service, _instance, _reliable, - used_client_ports_, its_local_port)) { - auto found_service = remote_service_info_.find(_service); - if (found_service != remote_service_info_.end()) { - auto found_instance = found_service->second.find(_instance); - if (found_instance != found_service->second.end()) { - auto found_reliability = found_instance->second.find(_reliable); - if (found_reliability != found_instance->second.end()) { - its_endpoint_def = found_reliability->second; - its_endpoint = create_client_endpoint( - its_endpoint_def->get_address(), - its_local_port, - its_endpoint_def->get_port(), - _reliable, _client, - configuration_->is_someip(_service, _instance) - ); - } - } - } - if (its_endpoint) { - used_client_ports_[_reliable].insert(its_local_port); - service_instances_[_service][its_endpoint.get()] = _instance; - remote_services_[_service][_instance][_client][_reliable] = its_endpoint; - if (_client == VSOMEIP_ROUTING_CLIENT) { - client_endpoints_by_ip_[its_endpoint_def->get_address()] - [its_endpoint_def->get_port()] - [_reliable] = its_endpoint; - // Set the basic route to the service in the service info - auto found_service_info = find_service(_service, _instance); - if (found_service_info) { - found_service_info->set_endpoint(its_endpoint, _reliable); - } - } - } + used_client_ports_, its_local_port)) { + auto found_service = remote_service_info_.find(_service); + if (found_service != remote_service_info_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + auto found_reliability = found_instance->second.find(_reliable); + if (found_reliability != found_instance->second.end()) { + its_endpoint_def = found_reliability->second; + its_endpoint = create_client_endpoint( + its_endpoint_def->get_address(), + its_local_port, + its_endpoint_def->get_port(), + _reliable, _client, + configuration_->is_someip(_service, _instance) + ); + } + } + } + if (its_endpoint) { + used_client_ports_[_reliable].insert(its_local_port); + service_instances_[_service][its_endpoint.get()] = _instance; + remote_services_[_service][_instance][_client][_reliable] = its_endpoint; + if (_client == VSOMEIP_ROUTING_CLIENT) { + client_endpoints_by_ip_[its_endpoint_def->get_address()] + [its_endpoint_def->get_port()] + [_reliable] = its_endpoint; + // Set the basic route to the service in the service info + auto found_service_info = find_service(_service, _instance); + if (found_service_info) { + found_service_info->set_endpoint(its_endpoint, _reliable); + } + } + } } return its_endpoint; } @@ -1383,7 +1626,7 @@ bool routing_manager_impl::is_field(service_t _service, instance_t _instance, return false; } -void routing_manager_impl::add_routing_info( +std::chrono::milliseconds routing_manager_impl::add_routing_info( service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor, ttl_t _ttl, const boost::asio::ip::address &_reliable_address, @@ -1391,6 +1634,37 @@ void routing_manager_impl::add_routing_info( const boost::asio::ip::address &_unreliable_address, uint16_t _unreliable_port) { + std::chrono::seconds default_ttl(DEFAULT_TTL); + std::chrono::milliseconds its_smallest_ttl = + std::chrono::duration_cast<std::chrono::milliseconds>(default_ttl); + std::chrono::milliseconds its_ttl(std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::seconds(_ttl))); + + bool offer_exists(false); + for (auto &s : get_services()) { + for (auto &i : s.second) { + if (i.second->is_local()) { + continue; //ignore local services + } + std::chrono::milliseconds its_found_ttl(i.second->get_precise_ttl()); + if ( its_found_ttl < its_smallest_ttl ) { + its_smallest_ttl = its_found_ttl; + if( its_ttl < its_smallest_ttl ) { + its_smallest_ttl = its_ttl; + } + } + offer_exists = true; + } + } + //if no remote service is in the list yet + if( !offer_exists ) { + if( its_ttl < its_smallest_ttl ) { + // set smallest TTL to TTL of first incoming remote offer + // this allows expiration of service if no further offer message is received which would trigger update_routing info + its_smallest_ttl = its_ttl; + } + } + // Create/Update service info std::shared_ptr<serviceinfo> its_info(find_service(_service, _instance)); if (!its_info) { @@ -1406,6 +1680,23 @@ void routing_manager_impl::add_routing_info( its_info = create_service_info(_service, _instance, _major, _minor, _ttl, is_local); init_service_info(_service, _instance, is_local); + } else if (its_info->is_local()) { + // We received a service info for a service which is already offered locally + VSOMEIP_ERROR << "routing_manager_impl::add_routing_info: " + << "rejecting routing info. Remote: " + << ((_reliable_port != ILLEGAL_PORT) ? _reliable_address.to_string() + : _unreliable_address.to_string()) << " is trying to offer [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(_major) << "." + << std::dec << _minor + << "] on port " << ((_reliable_port != ILLEGAL_PORT) ? _reliable_port + : _unreliable_port) << " offered previously on this node: [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(its_info->get_major()) + << "." << its_info->get_minor() << "]"; + return its_smallest_ttl; } else { its_info->set_ttl(_ttl); } @@ -1427,7 +1718,15 @@ void routing_manager_impl::add_routing_info( && its_definition->get_port() == _reliable_port) { is_reliable_known = true; } else { - VSOMEIP_WARNING << "Reliable service endpoint has changed!"; + VSOMEIP_WARNING << "Reliable service endpoint has changed: [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(_major) << "." + << std::dec << _minor << "] old: " + << its_definition->get_address().to_string() << ":" + << its_definition->get_port() << " new: " + << _reliable_address.to_string() << ":" + << _reliable_port; } } } @@ -1439,7 +1738,15 @@ void routing_manager_impl::add_routing_info( && its_definition->get_port() == _unreliable_port) { is_unreliable_known = true; } else { - VSOMEIP_WARNING << "Unreliable service endpoint has changed!"; + VSOMEIP_WARNING << "Unreliable service endpoint has changed: [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(_major) << "." + << std::dec << _minor << "] old: " + << its_definition->get_address().to_string() << ":" + << its_definition->get_port() << " new: " + << _unreliable_address.to_string() << ":" + << _unreliable_port; } } } @@ -1447,12 +1754,10 @@ void routing_manager_impl::add_routing_info( } // Add endpoint(s) if necessary - bool is_added(false); if (_reliable_port != ILLEGAL_PORT && !is_reliable_known) { std::shared_ptr<endpoint_definition> endpoint_def = endpoint_definition::get(_reliable_address, _reliable_port, true); remote_service_info_[_service][_instance][true] = endpoint_def; - is_added = !is_unreliable_known; // check if service was requested and establish TCP connection if necessary { @@ -1495,19 +1800,54 @@ void routing_manager_impl::add_routing_info( } } } + } else if (_reliable_port != ILLEGAL_PORT && is_reliable_known) { + std::lock_guard<std::mutex> its_lock(requested_services_mutex_); + for(const auto &client_id : requested_services_) { + auto found_service = client_id.second.find(_service); + if (found_service != client_id.second.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + for (const auto &major_minor_pair : found_instance->second) { + if ((major_minor_pair.first == _major + || _major == DEFAULT_MAJOR) + && (major_minor_pair.second <= _minor + || _minor == DEFAULT_MINOR + || major_minor_pair.second == ANY_MINOR)) { + host_->on_availability(_service, _instance, + true, its_info->get_major(), its_info->get_minor()); + if (!stub_->contained_in_routing_info( + VSOMEIP_ROUTING_CLIENT, _service, _instance, + its_info->get_major(), + its_info->get_minor())) { + stub_->on_offer_service(VSOMEIP_ROUTING_CLIENT, + _service, _instance, + its_info->get_major(), + its_info->get_minor()); + if (discovery_) { + discovery_->on_reliable_endpoint_connected( + _service, _instance, + its_info->get_endpoint(true)); + } + } + break; + } + } + } + } + } } if (_unreliable_port != ILLEGAL_PORT && !is_unreliable_known) { std::shared_ptr<endpoint_definition> endpoint_def = endpoint_definition::get(_unreliable_address, _unreliable_port, false); remote_service_info_[_service][_instance][false] = endpoint_def; - is_added = !is_reliable_known; - if (is_added) { + if (!is_reliable_known) { host_->on_availability(_service, _instance, true, _major, _minor); stub_->on_offer_service(VSOMEIP_ROUTING_CLIENT, _service, _instance, _major, _minor); } } + return its_smallest_ttl; } void routing_manager_impl::del_routing_info(service_t _service, instance_t _instance, @@ -1519,7 +1859,6 @@ void routing_manager_impl::del_routing_info(service_t _service, instance_t _inst host_->on_availability(_service, _instance, false, its_info->get_major(), its_info->get_minor()); stub_->on_stop_offer_service(VSOMEIP_ROUTING_CLIENT, _service, _instance, its_info->get_major(), its_info->get_minor()); - // Implicit unsubscribe { std::lock_guard<std::mutex> its_lock(eventgroups_mutex_); @@ -1533,28 +1872,29 @@ void routing_manager_impl::del_routing_info(service_t _service, instance_t _inst } } } + + clear_identified_clients( _service, _instance); + clear_identifying_clients( _service, _instance); + { - std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); - auto its_service = identified_clients_.find(_service); - if (its_service != identified_clients_.end()) { - auto found_instance = its_service->second.find(_instance); - if (found_instance != its_service->second.end()) { - auto found_reliable = found_instance->second.find(true); - if (found_reliable != found_instance->second.end()) { - found_reliable->second.clear(); - } - auto found_unreliable = found_instance->second.find(false); - if (found_unreliable != found_instance->second.end()) { - found_unreliable->second.clear(); - } + std::lock_guard<std::mutex> its_lock(remote_subscribers_mutex_); + auto found_service = remote_subscribers_.find(_service); + if (found_service != remote_subscribers_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + found_instance->second.clear(); } } } - if (_has_reliable) + if (_has_reliable) { clear_client_endpoints(_service, _instance, true); - if (_has_unreliable) + clear_remote_service_info(_service, _instance, true); + } + if (_has_unreliable) { clear_client_endpoints(_service, _instance, false); + clear_remote_service_info(_service, _instance, false); + } clear_multicast_endpoints(_service, _instance); @@ -1562,6 +1902,14 @@ void routing_manager_impl::del_routing_info(service_t _service, instance_t _inst clear_service_info(_service, _instance, true); if (_has_unreliable) clear_service_info(_service, _instance, false); + + // For expired services using only unreliable endpoints that have never been created before + if (!_has_reliable && !_has_unreliable) { + clear_remote_service_info(_service, _instance, true); + clear_remote_service_info(_service, _instance, false); + clear_service_info(_service, _instance, true); + clear_service_info(_service, _instance, false); + } } std::chrono::milliseconds routing_manager_impl::update_routing_info(std::chrono::milliseconds _elapsed) { @@ -1574,7 +1922,7 @@ std::chrono::milliseconds routing_manager_impl::update_routing_info(std::chrono: for (auto &s : get_services()) { for (auto &i : s.second) { - if (routing_manager_base::find_local(s.first, i.first)) { + if (routing_manager_base::find_local_client(s.first, i.first) != VSOMEIP_ROUTING_CLIENT) { continue; //don't expire local services } ttl_t its_ttl = i.second->get_ttl(); @@ -1600,6 +1948,8 @@ std::chrono::milliseconds routing_manager_impl::update_routing_info(std::chrono: for (auto &s : its_expired_offers) { for (auto &i : s.second) { + VSOMEIP_DEBUG << "update_routing_info: elapsed=" << _elapsed.count() + << " : delete service/instance " << std::hex << s.first << "/" << i.first; del_routing_info(s.first, i.first, i.second.first, i.second.second); } } @@ -1614,7 +1964,7 @@ void routing_manager_impl::expire_services(const boost::asio::ip::address &_addr for (auto &s : get_services()) { for (auto &i : s.second) { - if (routing_manager_base::find_local(s.first, i.first)) { + if (routing_manager_base::find_local_client(s.first, i.first) != VSOMEIP_ROUTING_CLIENT) { continue; //don't expire local services } bool is_gone(false); @@ -1646,6 +1996,8 @@ void routing_manager_impl::expire_services(const boost::asio::ip::address &_addr for (auto &s : its_expired_offers) { for (auto &i : s.second) { + VSOMEIP_DEBUG << "expire_services for address: " << _address.to_string() + << " : delete service/instance " << std::hex << s.first << "/" << i.first; del_routing_info(s.first, i.first, i.second.first, i.second.second); } } @@ -1664,8 +2016,13 @@ void routing_manager_impl::expire_subscriptions(const boost::asio::ip::address & for (auto &its_endpoint : its_invalid_endpoints) { its_eventgroup.second->remove_target(its_endpoint); + auto target = routing_manager_base::find_local(its_service.first, its_instance.first); + if (target) { + stub_->send_unsubscribe(target, VSOMEIP_ROUTING_CLIENT, its_service.first, + its_instance.first, its_eventgroup.first, true); + } } - if(its_eventgroup.second->is_multicast() && + if(its_eventgroup.second->is_multicast() && its_invalid_endpoints.size() && 0 == its_eventgroup.second->get_unreliable_target_count() ) { //clear multicast targets if no subscriber is left for multicast eventgroup its_eventgroup.second->clear_multicast_targets(); @@ -1706,7 +2063,7 @@ void routing_manager_impl::init_routing_info() { bool routing_manager_impl::on_subscribe_accepted(service_t _service, instance_t _instance, eventgroup_t _eventgroup, const std::shared_ptr<endpoint_definition> _target, - const std::chrono::high_resolution_clock::time_point &_expiration) { + const std::chrono::steady_clock::time_point &_expiration) { std::shared_ptr<eventgroupinfo> its_eventgroup = find_eventgroup(_service, _instance, _eventgroup); if (its_eventgroup) { @@ -1759,8 +2116,10 @@ bool routing_manager_impl::on_subscribe_accepted(service_t _service, instance_t stub_->send_subscribe(routing_manager_base::find_local(_service, _instance), client, _service, _instance, _eventgroup, its_eventgroup->get_major(), true); - - remote_subscribers_[_service][_instance][client].insert(_target); + { + std::lock_guard<std::mutex> its_lock(remote_subscribers_mutex_); + remote_subscribers_[_service][_instance][client].insert(_target); + } } else { VSOMEIP_ERROR<< "subscribe: attempt to subscribe to unknown eventgroup!"; return false; @@ -1772,14 +2131,14 @@ void routing_manager_impl::on_subscribe( service_t _service, instance_t _instance, eventgroup_t _eventgroup, std::shared_ptr<endpoint_definition> _subscriber, std::shared_ptr<endpoint_definition> _target, - const std::chrono::high_resolution_clock::time_point &_expiration) { + const std::chrono::steady_clock::time_point &_expiration) { std::shared_ptr<eventgroupinfo> its_eventgroup = find_eventgroup(_service, _instance, _eventgroup); if (its_eventgroup) { // IP address of target is a multicast address if the event is in a multicast eventgroup bool target_added(false); - if( its_eventgroup->is_multicast() && !_subscriber->is_reliable()) { + if (its_eventgroup->is_multicast() && !_subscriber->is_reliable()) { // Event is in multicast eventgroup and subscribe for UDP target_added = its_eventgroup->add_target({ _target, _expiration }, {_subscriber, _expiration}); @@ -1797,9 +2156,9 @@ void routing_manager_impl::on_subscribe( } if (target_added) { // unicast or multicast - // send initial events + // send initial events if we already have a cached field (is_set) for (auto its_event : its_eventgroup->get_events()) { - if (its_event->is_field()) { + if (its_event->is_field() && its_event->is_set()) { its_event->notify_one(_subscriber); // unicast } } @@ -1830,10 +2189,20 @@ void routing_manager_impl::on_unsubscribe(service_t _service, clear_remote_subscriber(_service, _instance, its_client, _target); stub_->send_unsubscribe(routing_manager_base::find_local(_service, _instance), - its_client, _service, _instance, _eventgroup); + its_client, _service, _instance, _eventgroup, true); host_->on_subscription(_service, _instance, _eventgroup, its_client, false); + if (its_eventgroup->get_targets().size() == 0) { + std::set<std::shared_ptr<event> > its_events + = its_eventgroup->get_events(); + for (auto e : its_events) { + if (e->is_shadow()) { + e->unset_payload(); + } + } + } + } else { VSOMEIP_ERROR<<"unsubscribe: attempt to subscribe to unknown eventgroup!"; } @@ -1924,6 +2293,7 @@ void routing_manager_impl::on_subscribe_nack(client_t _client, bool routing_manager_impl::deliver_specific_endpoint_message(service_t _service, instance_t _instance, const byte_t *_data, length_t _size, endpoint *_receiver) { // Try to deliver specific endpoint message (for selective subscribers) + std::lock_guard<std::recursive_mutex> its_lock(endpoint_mutex_); auto found_servic = remote_services_.find(_service); if (found_servic != remote_services_.end()) { auto found_instance = found_servic->second.find(_instance); @@ -2011,21 +2381,8 @@ void routing_manager_impl::clear_client_endpoints(service_t _service, instance_t } } } - // Clear remote_service_info_ - if (remote_service_info_.find(_service) != remote_service_info_.end()) { - if (remote_service_info_[_service].find(_instance) != remote_service_info_[_service].end()) { - remote_service_info_[_service][_instance].erase(_reliable); - auto found_endpoint_def = remote_service_info_[_service][_instance].find(!_reliable); - if (found_endpoint_def == remote_service_info_[_service][_instance].end()) { - remote_service_info_[_service].erase(_instance); - if (0 >= remote_service_info_[_service].size()) { - remote_service_info_.erase(_service); - } - } - } - } - if (1 >= service_instances_[_service].size()) { + if (!service_instances_[_service].size()) { service_instances_.erase(_service); } if(deleted_endpoint) { @@ -2233,16 +2590,33 @@ void routing_manager_impl::send_error(return_code_e _return_code, void routing_manager_impl::on_identify_response(client_t _client, service_t _service, instance_t _instance, bool _reliable) { - std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); - identified_clients_[_service][_instance][_reliable].insert(_client); + { + std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); + auto its_service = identifying_clients_.find(_service); + if (its_service != identifying_clients_.end()) { + auto its_instance = its_service->second.find(_instance); + if (its_instance != its_service->second.end()) { + auto its_reliable = its_instance->second.find(_reliable); + if (its_reliable != its_instance->second.end()) { + its_reliable->second.erase(_client); + } + } + } + identified_clients_[_service][_instance][_reliable].insert(_client); + } discovery_->send_subscriptions(_service, _instance, _client, _reliable); } void routing_manager_impl::identify_for_subscribe(client_t _client, service_t _service, instance_t _instance, major_version_t _major) { - if (!has_identified(_client, _service, _instance, false)) { + if (!has_identified(_client, _service, _instance, false) && + !is_identifying(_client, _service, _instance, false)) { auto unreliable_endpoint = find_or_create_remote_client(_service, _instance, false, _client); if (unreliable_endpoint) { + { + std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); + identifying_clients_[_service][_instance][false].insert(_client); + } auto message = runtime::get()->create_message(false); message->set_service(_service); message->set_instance(_instance); @@ -2258,9 +2632,14 @@ void routing_manager_impl::identify_for_subscribe(client_t _client, } } } - if (!has_identified(_client, _service, _instance, true)) { + if (!has_identified(_client, _service, _instance, true) && + !is_identifying(_client, _service, _instance, true)) { auto reliable_endpoint = find_or_create_remote_client(_service, _instance, true, _client); if (reliable_endpoint) { + { + std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); + identifying_clients_[_service][_instance][true].insert(_client); + } auto message = runtime::get()->create_message(true); message->set_service(_service); message->set_instance(_instance); @@ -2294,6 +2673,30 @@ bool routing_manager_impl::supports_selective(service_t _service, instance_t _in return supports_selective; } +bool routing_manager_impl::is_identifying(client_t _client, service_t _service, + instance_t _instance, bool _reliable) { + if (!supports_selective(_service, _instance)) { + // For legacy selective services clients can't be identified! + return false; + } + bool is_identifieing(false); + std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); + auto its_service = identifying_clients_.find(_service); + if (its_service != identifying_clients_.end()) { + auto its_instance = its_service->second.find(_instance); + if (its_instance != its_service->second.end()) { + auto its_reliable = its_instance->second.find(_reliable); + if (its_reliable != its_instance->second.end()) { + auto its_client = its_reliable->second.find(_client); + if (its_client != its_reliable->second.end()) { + is_identifieing = true; + } + } + } + } + return is_identifieing; +} + bool routing_manager_impl::has_identified(client_t _client, service_t _service, instance_t _instance, bool _reliable) { if (!supports_selective(_service, _instance)) { @@ -2321,6 +2724,7 @@ bool routing_manager_impl::has_identified(client_t _client, service_t _service, void routing_manager_impl::clear_remote_subscriber( service_t _service, instance_t _instance, client_t _client, const std::shared_ptr<endpoint_definition> &_target) { + std::lock_guard<std::mutex> its_lock(remote_subscribers_mutex_); auto its_service = remote_subscribers_.find(_service); if (its_service != remote_subscribers_.end()) { auto its_instance = its_service->second.find(_instance); @@ -2337,13 +2741,13 @@ void routing_manager_impl::clear_remote_subscriber( } } -std::chrono::high_resolution_clock::time_point +std::chrono::steady_clock::time_point routing_manager_impl::expire_subscriptions() { std::lock_guard<std::mutex> its_lock(eventgroups_mutex_); - std::chrono::high_resolution_clock::time_point now - = std::chrono::high_resolution_clock::now(); - std::chrono::high_resolution_clock::time_point next_expiration - = std::chrono::high_resolution_clock::now() + std::chrono::hours(24); + std::chrono::steady_clock::time_point now + = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point next_expiration + = std::chrono::steady_clock::now() + std::chrono::hours(24); for (auto &its_service : eventgroups_) { for (auto &its_instance : its_service.second) { @@ -2366,6 +2770,12 @@ routing_manager_impl::expire_subscriptions() { clear_remote_subscriber(its_service.first, its_instance.first, its_client, its_endpoint); + auto target = routing_manager_base::find_local(its_service.first, its_instance.first); + if (target) { + stub_->send_unsubscribe(target, VSOMEIP_ROUTING_CLIENT, its_service.first, + its_instance.first, its_eventgroup.first, true); + } + VSOMEIP_DEBUG << "Expired subscription (" << std::hex << its_service.first << "." << its_instance .first << "." @@ -2374,7 +2784,7 @@ routing_manager_impl::expire_subscriptions() { << std::dec << its_endpoint->get_port() << "(" << std::hex << its_client << ")"; } - if(its_eventgroup.second->is_multicast() && + if(its_eventgroup.second->is_multicast() && its_expired_endpoints.size() && 0 == its_eventgroup.second->get_unreliable_target_count() ) { //clear multicast targets if no unreliable subscriber is left for multicast eventgroup its_eventgroup.second->clear_multicast_targets(); @@ -2386,8 +2796,407 @@ routing_manager_impl::expire_subscriptions() { return next_expiration; } -bool routing_manager_impl::queue_message(const byte_t *_data, uint32_t _size) const { - return stub_->queue_message(_data, _size); +void routing_manager_impl::log_version_timer_cbk(boost::system::error_code const & _error) { + if (!_error) { + +#ifndef VSOMEIP_VERSION +#define VSOMEIP_VERSION "unknown version" +#endif + + VSOMEIP_INFO << "vSomeIP " << VSOMEIP_VERSION; + version_log_timer_.expires_from_now( + std::chrono::seconds(configuration_->get_log_version_interval())); + version_log_timer_.async_wait(std::bind(&routing_manager_impl::log_version_timer_cbk, + this, std::placeholders::_1)); + } +} + +#ifndef WITHOUT_SYSTEMD +void routing_manager_impl::watchdog_cbk(boost::system::error_code const &_error) { + if (!_error) { + static bool is_ready(false); + static bool has_interval(false); + static uint64_t its_interval(0); + + if (is_ready) { + sd_notify(0, "WATCHDOG=1"); + } else { + is_ready = true; + sd_notify(0, "READY=1"); + if (0 < sd_watchdog_enabled(0, &its_interval)) { + has_interval = true; + } + } + + if (has_interval) { + watchdog_timer_.expires_from_now(std::chrono::microseconds(its_interval / 2)); + watchdog_timer_.async_wait(std::bind(&routing_manager_impl::watchdog_cbk, + this, std::placeholders::_1)); + } + } +} +#endif + +void routing_manager_impl::clear_remote_service_info(service_t _service, instance_t _instance, bool _reliable) { + // Clear remote_service_info_ + if (remote_service_info_.find(_service) != remote_service_info_.end()) { + if (remote_service_info_[_service].find(_instance) != remote_service_info_[_service].end()) { + remote_service_info_[_service][_instance].erase(_reliable); + auto found_endpoint_def = remote_service_info_[_service][_instance].find(!_reliable); + if (found_endpoint_def == remote_service_info_[_service][_instance].end()) { + remote_service_info_[_service].erase(_instance); + if (0 >= remote_service_info_[_service].size()) { + remote_service_info_.erase(_service); + } + } + } + } } +bool routing_manager_impl::handle_local_offer_service(client_t _client, service_t _service, + instance_t _instance, major_version_t _major,minor_version_t _minor) { + bool previously_offered(false); + { + std::lock_guard<std::mutex> its_lock(local_services_mutex_); + auto found_service = local_services_.find(_service); + if (found_service != local_services_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + const major_version_t its_stored_major(std::get<0>(found_instance->second)); + const minor_version_t its_stored_minor(std::get<1>(found_instance->second)); + const client_t its_stored_client(std::get<2>(found_instance->second)); + if ( its_stored_major == _major + && its_stored_minor == _minor + && its_stored_client == _client) { + VSOMEIP_WARNING << "routing_manager_impl::handle_local_offer_service: " + << "Application: " << std::hex << std::setfill('0') + << std::setw(4) << _client << " is offering: [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(_major) << "." + << _minor << "] offered previously by itself."; + previously_offered = true; + } else if ( its_stored_major == _major + && its_stored_minor == _minor + && its_stored_client != _client) { + // check if previous offering application is still alive + bool already_pinged(false); + { + std::lock_guard<std::mutex> its_lock(pending_offers_mutex_); + auto found_service2 = pending_offers_.find(_service); + if (found_service2 != pending_offers_.end()) { + auto found_instance2 = found_service2->second.find(_instance); + if (found_instance2 != found_service2->second.end()) { + if(std::get<2>(found_instance2->second) == _client) { + already_pinged = true; + } else { + VSOMEIP_ERROR << "routing_manager_impl::handle_local_offer_service: " + << "rejecting service registration. Application: " + << std::hex << std::setfill('0') << std::setw(4) + << _client << " is trying to offer [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(_major) << "." + << std::dec << _minor + << "] current pending offer by application: " << std::hex + << std::setfill('0') << std::setw(4) + << its_stored_client << ": [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(its_stored_major) + << "." << its_stored_minor << "]"; + return false; + } + } + } + } + if (!already_pinged) { + // find out endpoint of previously offering application + std::shared_ptr<local_client_endpoint_base_impl> + its_old_endpoint + = std::dynamic_pointer_cast<local_client_endpoint_base_impl>( + find_local(its_stored_client)); + if (its_old_endpoint) { + std::lock_guard<std::mutex> its_lock(pending_offers_mutex_); + if(stub_->send_ping(its_stored_client)) { + pending_offers_[_service][_instance] = + std::make_tuple(_major, _minor, _client, + its_stored_client); + VSOMEIP_WARNING << "OFFER(" + << std::hex << std::setw(4) << std::setfill('0') << _client <<"): [" + << std::hex << std::setw(4) << std::setfill('0') << _service << "." + << std::hex << std::setw(4) << std::setfill('0') << _instance + << ":" << std::dec << int(_major) << "." << std::dec << _minor + << "] is now pending. Waiting for pong from application: " + << std::hex << std::setw(4) << std::setfill('0') << its_stored_client; + return false; + } + } else if (its_stored_client == host_->get_client()) { + VSOMEIP_ERROR << "routing_manager_impl::handle_local_offer_service: " + << "rejecting service registration. Application: " + << std::hex << std::setfill('0') << std::setw(4) + << _client << " is trying to offer [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(_major) << "." + << std::dec << _minor + << "] offered previously by routing manager stub itself with application: " + << std::hex << std::setfill('0') << std::setw(4) + << its_stored_client << ": [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(its_stored_major) + << "." << its_stored_minor << "] which is still alive"; + return false; + } + } else { + return false; + } + } else { + VSOMEIP_ERROR << "routing_manager_impl::handle_local_offer_service: " + << "rejecting service registration. Application: " + << std::hex << std::setfill('0') << std::setw(4) + << _client << " is trying to offer [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(_major) << "." + << std::dec << _minor + << "] offered previously by application: " << std::hex + << std::setfill('0') << std::setw(4) + << its_stored_client << ": [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(its_stored_major) + << "." << its_stored_minor << "]"; + return false; + } + } + } + + // check if the same service instance is already offered remotely + if (routing_manager_base::offer_service(_client, _service, _instance, + _major, _minor) || previously_offered) { + local_services_[_service][_instance] = std::make_tuple(_major, + _minor, _client); + } else { + VSOMEIP_ERROR << "routing_manager_impl::handle_local_offer_service: " + << "rejecting service registration. Application: " + << std::hex << std::setfill('0') << std::setw(4) + << _client << " is trying to offer [" + << std::hex << std::setfill('0') << std::setw(4) << _service << "." + << std::hex << std::setfill('0') << std::setw(4) << _instance << "." + << std::dec << static_cast<std::uint32_t>(_major) << "." + << std::dec << _minor << "]" + << "] already offered remotely"; + return false; + } + } + return true; +} + +void routing_manager_impl::on_pong(client_t _client) { + std::lock_guard<std::mutex> its_lock(pending_offers_mutex_); + if (pending_offers_.size() == 0) { + return; + } + for (auto service_iter = pending_offers_.begin(); + service_iter != pending_offers_.end(); ) { + for (auto instance_iter = service_iter->second.begin(); + instance_iter != service_iter->second.end(); ) { + if (std::get<3>(instance_iter->second) == _client) { + // received pong from an application were another application wants + // to offer its service, delete the other applications offer as + // the current offering application is still alive + VSOMEIP_WARNING << "OFFER(" + << std::hex << std::setw(4) << std::setfill('0') + << std::get<2>(instance_iter->second) <<"): [" + << std::hex << std::setw(4) << std::setfill('0') + << service_iter->first << "." + << std::hex << std::setw(4) << std::setfill('0') + << instance_iter->first << ":" << std::dec + << std::uint32_t(std::get<0>(instance_iter->second)) + << "." << std::dec << std::get<1>(instance_iter->second) + << "] was rejected as application: " + << std::hex << std::setw(4) << std::setfill('0') << _client + << " is still alive"; + instance_iter = service_iter->second.erase(instance_iter); + } else { + ++instance_iter; + } + } + + if (service_iter->second.size() == 0) { + service_iter = pending_offers_.erase(service_iter); + } else { + ++service_iter; + } + } +} + +void routing_manager_impl::on_clientendpoint_error(client_t _client) { + VSOMEIP_WARNING << "Application/Client " + << std::hex << std::setw(4) << std::setfill('0') + << _client << " will be deregistered because of an client endpoint error."; + stub_->deregister_erroneous_client(_client); +} + +void routing_manager_impl::confirm_pending_offers(client_t _client) { + std::forward_list<std::tuple<client_t, service_t, instance_t, major_version_t, + minor_version_t>> its_offers; + { + std::lock_guard<std::mutex> its_lock(pending_offers_mutex_); + if (pending_offers_.size() == 0) { + return; + } + + for (auto service_iter = pending_offers_.begin(); + service_iter != pending_offers_.end(); ) { + for (auto instance_iter = service_iter->second.begin(); + instance_iter != service_iter->second.end(); ) { + if (std::get<3>(instance_iter->second) == _client) { + VSOMEIP_WARNING << "OFFER(" + << std::hex << std::setw(4) << std::setfill('0') + << std::get<2>(instance_iter->second) <<"): [" + << std::hex << std::setw(4) << std::setfill('0') + << service_iter->first << "." + << std::hex << std::setw(4) << std::setfill('0') + << instance_iter->first << ":" << std::dec + << std::uint32_t(std::get<0>(instance_iter->second)) + << "." << std::dec << std::get<1>(instance_iter->second) + << "] is not pending anymore as application: " + << std::hex << std::setw(4) << std::setfill('0') + << std::get<3>(instance_iter->second) + << " is dead. Offering again!"; + its_offers.push_front(std::make_tuple( + std::get<2>(instance_iter->second), + service_iter->first, + instance_iter->first, + std::get<0>(instance_iter->second), + std::get<1>(instance_iter->second))); + instance_iter = service_iter->second.erase(instance_iter); + } else { + ++instance_iter; + } + } + + if (service_iter->second.size() == 0) { + service_iter = pending_offers_.erase(service_iter); + } else { + ++service_iter; + } + } + } + for (const auto &offer : its_offers) { + offer_service(std::get<0>(offer), std::get<1>(offer), std::get<2>(offer), + std::get<3>(offer), std::get<4>(offer)); + } +} + +void routing_manager_impl::remove_specific_client_endpoint(client_t _client, service_t _service, instance_t _instance, bool _reliable) +{ + std::lock_guard<std::mutex> its_lock(specific_endpoint_clients_mutex_); + auto found_service = specific_endpoint_clients_.find(_service); + if(found_service != specific_endpoint_clients_.end()){ + auto found_instance = found_service->second.find(_instance); + if(found_instance != found_service->second.end()) { + auto its_client = found_instance->second.find(_client); + if (its_client != found_instance->second.end()) { + if (remote_services_.find(_service) != remote_services_.end()) { + if (remote_services_[_service].find(_instance) != remote_services_[_service].end()) { + auto endpoint = remote_services_[_service][_instance][_client][_reliable]; + if (endpoint) { + service_instances_[_service].erase(endpoint.get()); + endpoint->stop(); + } + remote_services_[_service][_instance][_client].erase(_reliable); + auto found_endpoint = remote_services_[_service][_instance][_client].find(!_reliable); + if (found_endpoint == remote_services_[_service][_instance][_client].end()) { + remote_services_[_service][_instance].erase(_client); + } + } + } + } + } + } +} + +void routing_manager_impl::clear_identified_clients( service_t _service, instance_t _instance) { + std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); + auto its_service = identified_clients_.find(_service); + if (its_service != identified_clients_.end()) { + auto found_instance = its_service->second.find(_instance); + if (found_instance != its_service->second.end()) { + auto found_reliable = found_instance->second.find(true); + if (found_reliable != found_instance->second.end()) { + found_reliable->second.clear(); + } + auto found_unreliable = found_instance->second.find(false); + if (found_unreliable != found_instance->second.end()) { + found_unreliable->second.clear(); + } + } + } +} + +void routing_manager_impl::clear_identifying_clients( service_t _service, instance_t _instance) { + std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); + auto its_service = identifying_clients_.find(_service); + if (its_service != identifying_clients_.end()) { + auto found_instance = its_service->second.find(_instance); + if (found_instance != its_service->second.end()) { + auto found_reliable = found_instance->second.find(true); + if (found_reliable != found_instance->second.end()) { + found_reliable->second.clear(); + } + auto found_unreliable = found_instance->second.find(false); + if (found_unreliable != found_instance->second.end()) { + found_unreliable->second.clear(); + } + } + } +} + +void routing_manager_impl::remove_identified_client(service_t _service, instance_t _instance, client_t _client) { + std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); + auto its_service = identified_clients_.find(_service); + if (its_service != identified_clients_.end()) { + auto found_instance = its_service->second.find(_instance); + if (found_instance != its_service->second.end()) { + auto found_reliable = found_instance->second.find(true); + if (found_reliable != found_instance->second.end()) { + auto found_client = found_reliable->second.find(_client); + if(found_client != found_reliable->second.end()) + found_reliable->second.erase(_client); + } + auto found_unreliable = found_instance->second.find(false); + if (found_unreliable != found_instance->second.end()) { + auto found_client = found_unreliable->second.find(_client); + if(found_client != found_unreliable->second.end()) + found_unreliable->second.erase(_client); + } + } + } +} + +void routing_manager_impl::remove_identifying_client(service_t _service, instance_t _instance, client_t _client) { + std::lock_guard<std::mutex> its_lock(identified_clients_mutex_); + auto its_service = identifying_clients_.find(_service); + if (its_service != identifying_clients_.end()) { + auto found_instance = its_service->second.find(_instance); + if (found_instance != its_service->second.end()) { + auto found_reliable = found_instance->second.find(true); + if (found_reliable != found_instance->second.end()) { + auto found_client = found_reliable->second.find(_client); + if(found_client != found_reliable->second.end()) + found_reliable->second.erase(_client); + } + auto found_unreliable = found_instance->second.find(false); + if (found_unreliable != found_instance->second.end()) { + auto found_client = found_unreliable->second.find(_client); + if(found_client != found_unreliable->second.end()) + found_unreliable->second.erase(_client); + } + } + } +} } // namespace vsomeip diff --git a/implementation/routing/src/routing_manager_proxy.cpp b/implementation/routing/src/routing_manager_proxy.cpp index 0a3f7b5..9491544 100644 --- a/implementation/routing/src/routing_manager_proxy.cpp +++ b/implementation/routing/src/routing_manager_proxy.cpp @@ -8,6 +8,7 @@ #include <mutex> #include <unordered_set> #include <future> +#include <forward_list> #ifndef WIN32 // for umask @@ -38,9 +39,10 @@ routing_manager_proxy::routing_manager_proxy(routing_manager_host *_host) : routing_manager_base(_host), is_connected_(false), is_started_(false), - state_(state_type_e::ST_DEREGISTERED), + state_(inner_state_type_e::ST_DEREGISTERED), sender_(0), - receiver_(0) + receiver_(0), + register_application_timer_(io_) { } @@ -50,41 +52,29 @@ routing_manager_proxy::~routing_manager_proxy() { void routing_manager_proxy::init() { routing_manager_base::init(); - std::stringstream its_sender_path; - sender_ = create_local(VSOMEIP_ROUTING_CLIENT); - - std::stringstream its_client; - its_client << VSOMEIP_BASE_PATH << std::hex << client_; -#ifdef WIN32 - ::_unlink(its_client.str().c_str()); - int port = VSOMEIP_INTERNAL_BASE_PORT + client_; -#else - ::unlink(its_client.str().c_str()); - const mode_t previous_mask(::umask(static_cast<mode_t>(configuration_->get_umask()))); -#endif - receiver_ = std::make_shared<local_server_endpoint_impl>(shared_from_this(), -#ifdef WIN32 - boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port), -#else - boost::asio::local::stream_protocol::endpoint(its_client.str()), -#endif - io_, configuration_->get_max_message_size_local()); + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + sender_ = create_local(VSOMEIP_ROUTING_CLIENT); + } -#ifdef WIN32 - VSOMEIP_DEBUG << "Listening at " << port; -#else - ::umask(previous_mask); - VSOMEIP_DEBUG<< "Listening at " << its_client.str(); -#endif + init_receiver(); } void routing_manager_proxy::start() { is_started_ = true; - if (!sender_) { + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (!sender_) { + // application has been stopped and started again + sender_ = create_local(VSOMEIP_ROUTING_CLIENT); + } + } + if (!receiver_) { // application has been stopped and started again - sender_ = create_local(VSOMEIP_ROUTING_CLIENT); + init_receiver(); } + if (sender_) { sender_->start(); } @@ -94,7 +84,31 @@ void routing_manager_proxy::start() { } void routing_manager_proxy::stop() { - deregister_application(); + std::unique_lock<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERING) { + register_application_timer_.cancel(); + } + while (state_ == inner_state_type_e::ST_REGISTERING) { + std::cv_status status = state_condition_.wait_for(its_lock, std::chrono::milliseconds(1000)); + if (status == std::cv_status::timeout) { + VSOMEIP_WARNING << std::hex << client_ << " registering timeout on stop"; + break; + } + } + + if (state_ == inner_state_type_e::ST_REGISTERED) { + deregister_application(); + // Waiting de-register acknowledge to synchronize shutdown + while (state_ == inner_state_type_e::ST_REGISTERED) { + std::cv_status status = state_condition_.wait_for(its_lock, std::chrono::milliseconds(1000)); + if (status == std::cv_status::timeout) { + VSOMEIP_WARNING << std::hex << client_ << " couldn't deregister application - timeout"; + break; + } + } + } + is_started_ = false; + its_lock.unlock(); if (receiver_) { receiver_->stop(); @@ -110,30 +124,39 @@ void routing_manager_proxy::stop() { } } - // delete the sender - sender_ = nullptr; + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + // delete the sender + sender_ = nullptr; + } + receiver_ = nullptr; std::stringstream its_client; its_client << VSOMEIP_BASE_PATH << std::hex << client_; #ifdef WIN32 ::_unlink(its_client.str().c_str()); #else - ::unlink(its_client.str().c_str()); + if (-1 == ::unlink(its_client.str().c_str())) { + VSOMEIP_ERROR<< "routing_manager_proxy::stop unlink failed (" + << its_client.str() << "): "<< std::strerror(errno); + } #endif - - is_started_ = false; } -void routing_manager_proxy::offer_service(client_t _client, service_t _service, +bool routing_manager_proxy::offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) { - - routing_manager_base::offer_service(_client, _service, _instance, _major, _minor); - - if (is_connected_) { - send_offer_service(_client, _service, _instance, _major, _minor); + if(!routing_manager_base::offer_service(_client, _service, _instance, _major, _minor)) { + VSOMEIP_WARNING << "routing_manager_proxy::offer_service," + << "routing_manager_base::offer_service returned false"; + } + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED) { + send_offer_service(_client, _service, _instance, _major, _minor); + } + service_data_t offer = { _service, _instance, _major, _minor, false }; + pending_offers_.insert(offer); } - service_data_t offer = { _service, _instance, _major, _minor, false }; - std::lock_guard<std::mutex> its_lock(pending_mutex_); - pending_offers_.insert(offer); + return true; } void routing_manager_proxy::send_offer_service(client_t _client, @@ -158,7 +181,12 @@ void routing_manager_proxy::send_offer_service(client_t _client, std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 5], &_minor, sizeof(_minor)); - sender_->send(its_command, sizeof(its_command)); + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } } void routing_manager_proxy::stop_offer_service(client_t _client, @@ -172,36 +200,43 @@ void routing_manager_proxy::stop_offer_service(client_t _client, // create server endpoints which needs to be freed clear_service_info(_service, _instance, false); - if (is_connected_) { - byte_t its_command[VSOMEIP_STOP_OFFER_SERVICE_COMMAND_SIZE]; - uint32_t its_size = VSOMEIP_STOP_OFFER_SERVICE_COMMAND_SIZE - - VSOMEIP_COMMAND_HEADER_SIZE; - - its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_STOP_OFFER_SERVICE; - std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, - sizeof(client_)); - std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, - sizeof(its_size)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, - sizeof(_service)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, - sizeof(_instance)); - its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4] = _major; - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 5], &_minor, - sizeof(_minor)); - - sender_->send(its_command, sizeof(its_command)); - } - std::lock_guard<std::mutex> its_lock(pending_mutex_); - auto it = pending_offers_.begin(); - while (it != pending_offers_.end()) { - if (it->service_ == _service - && it->instance_ == _instance) { - break; + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED) { + byte_t its_command[VSOMEIP_STOP_OFFER_SERVICE_COMMAND_SIZE]; + uint32_t its_size = VSOMEIP_STOP_OFFER_SERVICE_COMMAND_SIZE + - VSOMEIP_COMMAND_HEADER_SIZE; + + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_STOP_OFFER_SERVICE; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, + sizeof(client_)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, + sizeof(_service)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, + sizeof(_instance)); + its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4] = _major; + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 5], &_minor, + sizeof(_minor)); + + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } + } + auto it = pending_offers_.begin(); + while (it != pending_offers_.end()) { + if (it->service_ == _service + && it->instance_ == _instance) { + break; + } + it++; } - it++; + if (it != pending_offers_.end()) pending_offers_.erase(it); } - if (it != pending_offers_.end()) pending_offers_.erase(it); } void routing_manager_proxy::request_service(client_t _client, @@ -209,29 +244,73 @@ void routing_manager_proxy::request_service(client_t _client, minor_version_t _minor, bool _use_exclusive_proxy) { routing_manager_base::request_service(_client, _service, _instance, _major, _minor, _use_exclusive_proxy); - send_request_service(_client, _service, _instance, _major, _minor, - _use_exclusive_proxy); + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED) { + send_request_service(_client, _service, _instance, _major, _minor, + _use_exclusive_proxy); + } + service_data_t request = { _service, _instance, _major, _minor, _use_exclusive_proxy }; + pending_requests_.insert(request); + } } void routing_manager_proxy::release_service(client_t _client, service_t _service, instance_t _instance) { routing_manager_base::release_service(_client, _service, _instance); - send_release_service(_client, _service, _instance); + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED) { + send_release_service(_client, _service, _instance); + } + auto it = pending_requests_.begin(); + while (it != pending_requests_.end()) { + if (it->service_ == _service + && it->instance_ == _instance) { + break; + } + it++; + } + if (it != pending_requests_.end()) pending_requests_.erase(it); + } } void routing_manager_proxy::register_event(client_t _client, service_t _service, instance_t _instance, event_t _event, const std::set<eventgroup_t> &_eventgroups, - bool _is_field, bool _is_provided, bool _is_shadow, bool _is_cache_placeholder) { + bool _is_field, + std::chrono::milliseconds _cycle, bool _change_resets_cycle, + epsilon_change_func_t _epsilon_change_func, + bool _is_provided, bool _is_shadow, bool _is_cache_placeholder) { (void)_is_shadow; (void)_is_cache_placeholder; routing_manager_base::register_event(_client, _service, _instance, - _event,_eventgroups, _is_field, _is_provided); - - send_register_event(client_, _service, _instance, - _event, _eventgroups, _is_field, _is_provided); + _event,_eventgroups, _is_field, + _cycle, _change_resets_cycle, + _epsilon_change_func, + _is_provided); + + if (!_is_provided || + configuration_->is_offered_remote(_service, _instance)) { + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED) { + send_register_event(client_, _service, _instance, + _event, _eventgroups, _is_field, _is_provided); + } + event_data_t registration = { + _service, + _instance, + _event, + _is_field, + _is_provided, + _eventgroups + }; + pending_event_registrations_.insert(registration); + } + } } void routing_manager_proxy::unregister_event(client_t _client, @@ -241,39 +320,49 @@ void routing_manager_proxy::unregister_event(client_t _client, routing_manager_base::unregister_event(_client, _service, _instance, _event, _is_provided); - if (is_connected_) { - byte_t its_command[VSOMEIP_UNREGISTER_EVENT_COMMAND_SIZE]; - uint32_t its_size = VSOMEIP_UNREGISTER_EVENT_COMMAND_SIZE - - VSOMEIP_COMMAND_HEADER_SIZE; - - its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_UNREGISTER_EVENT; - std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, - sizeof(client_)); - std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, - sizeof(its_size)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, - sizeof(_service)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, - sizeof(_instance)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_event, - sizeof(_event)); - its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 6] - = static_cast<byte_t>(_is_provided); - - sender_->send(its_command, sizeof(its_command)); - } - std::lock_guard<std::mutex> its_lock(pending_mutex_); - auto it = pending_event_registrations_.begin(); - while (it != pending_event_registrations_.end()) { - if (it->service_ == _service - && it->instance_ == _instance - && it->event_ == _event) { - break; + if (!_is_provided || + configuration_->is_offered_remote(_service, _instance)) { + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED) { + byte_t its_command[VSOMEIP_UNREGISTER_EVENT_COMMAND_SIZE]; + uint32_t its_size = VSOMEIP_UNREGISTER_EVENT_COMMAND_SIZE + - VSOMEIP_COMMAND_HEADER_SIZE; + + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_UNREGISTER_EVENT; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, + sizeof(client_)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, + sizeof(_service)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, + sizeof(_instance)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_event, + sizeof(_event)); + its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 6] + = static_cast<byte_t>(_is_provided); + + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } + } + auto it = pending_event_registrations_.begin(); + while (it != pending_event_registrations_.end()) { + if (it->service_ == _service + && it->instance_ == _instance + && it->event_ == _event) { + break; + } + it++; + } + if (it != pending_event_registrations_.end()) + pending_event_registrations_.erase(it); } - it++; } - if (it != pending_event_registrations_.end()) - pending_event_registrations_.erase(it); } bool routing_manager_proxy::is_field(service_t _service, instance_t _instance, @@ -288,15 +377,16 @@ bool routing_manager_proxy::is_field(service_t _service, instance_t _instance, void routing_manager_proxy::subscribe(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup, major_version_t _major, subscription_type_e _subscription_type) { - - if (is_connected_ && is_available(_service, _instance, _major)) { - send_subscribe(_client, _service, _instance, _eventgroup, _major, - _subscription_type); + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED && is_available(_service, _instance, _major)) { + send_subscribe(_client, _service, _instance, _eventgroup, _major, + _subscription_type); + } + eventgroup_data_t subscription = { _service, _instance, _eventgroup, _major, + _subscription_type}; + pending_subscriptions_.insert(subscription); } - eventgroup_data_t subscription = { _service, _instance, _eventgroup, _major, - _subscription_type}; - std::lock_guard<std::mutex> its_lock(pending_mutex_); - pending_subscriptions_.insert(subscription); } void routing_manager_proxy::send_subscribe(client_t _client, service_t _service, @@ -329,7 +419,12 @@ void routing_manager_proxy::send_subscribe(client_t _client, service_t _service, auto its_target = find_or_create_local(target_client); its_target->send(its_command, sizeof(its_command)); } else { - sender_->send(its_command, sizeof(its_command)); + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } } } @@ -356,7 +451,12 @@ void routing_manager_proxy::send_subscribe_nack(client_t _subscriber, if (its_target) { its_target->send(its_command, sizeof(its_command)); } else { - sender_->send(its_command, sizeof(its_command)); + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } } } @@ -383,56 +483,70 @@ void routing_manager_proxy::send_subscribe_ack(client_t _subscriber, if (its_target) { its_target->send(its_command, sizeof(its_command)); } else { - sender_->send(its_command, sizeof(its_command)); + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + } } } void routing_manager_proxy::unsubscribe(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup) { (void)_client; - - if (is_connected_) { - byte_t its_command[VSOMEIP_UNSUBSCRIBE_COMMAND_SIZE]; - uint32_t its_size = VSOMEIP_UNSUBSCRIBE_COMMAND_SIZE - - VSOMEIP_COMMAND_HEADER_SIZE; - - its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_UNSUBSCRIBE; - std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &_client, - sizeof(_client)); - std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, - sizeof(its_size)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, - sizeof(_service)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, - sizeof(_instance)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_eventgroup, - sizeof(_eventgroup)); - - auto its_target = find_local(_service, _instance); - if (its_target) { - its_target->send(its_command, sizeof(its_command)); - } else { - sender_->send(its_command, sizeof(its_command)); + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED) { + bool is_remote(false); + byte_t its_command[VSOMEIP_UNSUBSCRIBE_COMMAND_SIZE]; + uint32_t its_size = VSOMEIP_UNSUBSCRIBE_COMMAND_SIZE + - VSOMEIP_COMMAND_HEADER_SIZE; + + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_UNSUBSCRIBE; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &_client, + sizeof(_client)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, + sizeof(_service)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, + sizeof(_instance)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_eventgroup, + sizeof(_eventgroup)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 6], &is_remote, + sizeof(is_remote)); + + auto its_target = find_local(_service, _instance); + if (its_target) { + its_target->send(its_command, sizeof(its_command)); + } else { + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } + }; + } } - } - std::lock_guard<std::mutex> its_lock(pending_mutex_); - auto it = pending_subscriptions_.begin(); - while (it != pending_subscriptions_.end()) { - if (it->service_ == _service - && it->instance_ == _instance) { - break; + auto it = pending_subscriptions_.begin(); + while (it != pending_subscriptions_.end()) { + if (it->service_ == _service + && it->instance_ == _instance) { + break; + } + it++; } - it++; + if (it != pending_subscriptions_.end()) pending_subscriptions_.erase(it); } - if (it != pending_subscriptions_.end()) pending_subscriptions_.erase(it); } bool routing_manager_proxy::send(client_t _client, const byte_t *_data, length_t _size, instance_t _instance, bool _flush, - bool _reliable, - bool _initial) { - bool is_sent(false); + bool _reliable) { + bool is_sent(false); + bool has_remote_subscribers(false); if (_size > VSOMEIP_MESSAGE_TYPE_POS) { std::shared_ptr<endpoint> its_target; if (utility::is_request(_data[VSOMEIP_MESSAGE_TYPE_POS])) { @@ -461,24 +575,46 @@ bool routing_manager_proxy::send(client_t _client, const byte_t *_data, } else if (utility::is_notification(_data[VSOMEIP_MESSAGE_TYPE_POS]) && _client == VSOMEIP_ROUTING_CLIENT) { // notify - send_local_notification(get_client(), _data, _size, - _instance, _flush, _reliable, _initial); + has_remote_subscribers = send_local_notification(get_client(), _data, _size, + _instance, _flush, _reliable); } else if (utility::is_notification(_data[VSOMEIP_MESSAGE_TYPE_POS]) && _client != VSOMEIP_ROUTING_CLIENT) { // notify_one its_target = find_local(_client); if (its_target) { return send_local(its_target, get_client(), _data, _size, - _instance, _flush, _reliable, VSOMEIP_SEND, false, _initial); + _instance, _flush, _reliable, VSOMEIP_SEND); } } // If no direct endpoint could be found/is connected // or for notifications ~> route to routing_manager_stub if (!its_target || !its_target->is_connected()) { - its_target = sender_; + if (sender_) { + its_target = sender_; + } else { + return false; + } + } + + bool send(true); + uint8_t command = VSOMEIP_SEND; + + if (utility::is_notification(_data[VSOMEIP_MESSAGE_TYPE_POS])) { + if (_client) { + command = VSOMEIP_NOTIFY_ONE; + } else { + command = VSOMEIP_NOTIFY; + // Do we need to deliver a notification to the routing manager? + // Only for services offered remote which already have remote clients subscribed to + service_t its_service = VSOMEIP_BYTES_TO_WORD( + _data[VSOMEIP_SERVICE_POS_MIN], + _data[VSOMEIP_SERVICE_POS_MAX]); + send = configuration_->is_offered_remote(its_service, _instance) + && has_remote_subscribers; + } } #ifdef USE_DLT - else { + else if (its_target != sender_) { uint16_t its_data_size = uint16_t(_size > USHRT_MAX ? USHRT_MAX : _size); @@ -488,12 +624,10 @@ bool routing_manager_proxy::send(client_t _client, const byte_t *_data, _data, its_data_size); } #endif - uint8_t command = utility::is_notification(_data[VSOMEIP_MESSAGE_TYPE_POS]) ? - VSOMEIP_NOTIFY : VSOMEIP_SEND; - if (utility::is_notification(_data[VSOMEIP_MESSAGE_TYPE_POS]) && _client) { - command = VSOMEIP_NOTIFY_ONE; + if (send) { + is_sent = send_local(its_target, _client, _data, _size, + _instance, _flush, _reliable, command); } - is_sent = send_local(its_target, _client, _data, _size, _instance, _flush, _reliable, command, false, _initial); } return (is_sent); } @@ -517,22 +651,26 @@ bool routing_manager_proxy::send_to( void routing_manager_proxy::notify( service_t _service, instance_t _instance, event_t _event, - std::shared_ptr<payload> _payload) { - std::shared_ptr<message> its_notification - = runtime::get()->create_notification(); - its_notification->set_service(_service); - its_notification->set_instance(_instance); - its_notification->set_method(_event); - its_notification->set_payload(_payload); - auto service_info = find_service(_service, _instance); - if (service_info) { - its_notification->set_interface_version(service_info->get_major()); - } - if (is_connected_) { - routing_manager_base::notify(_service, _instance, _event, _payload); - } else if (is_field(_service, _instance, _event)){ - std::lock_guard<std::mutex> its_lock(pending_mutex_); - pending_notifications_[_service][_instance][_event] = its_notification; + std::shared_ptr<payload> _payload, bool _force) { + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ == inner_state_type_e::ST_REGISTERED) { + routing_manager_base::notify(_service, _instance, _event, _payload, _force); + } + + if (is_field(_service, _instance, _event)){ + std::shared_ptr<message> its_notification + = runtime::get()->create_notification(); + its_notification->set_service(_service); + its_notification->set_instance(_instance); + its_notification->set_method(_event); + its_notification->set_payload(_payload); + auto service_info = find_service(_service, _instance); + if (service_info) { + its_notification->set_interface_version(service_info->get_major()); + } + pending_notifications_[_service][_instance][_event] = its_notification; + } } } @@ -566,9 +704,9 @@ void routing_manager_proxy::on_error(const byte_t *_data, length_t _length, } void routing_manager_proxy::release_port(uint16_t _port, bool _reliable) { - (void)_port; - (void)_reliable; - // intentionally empty + (void)_port; + (void)_reliable; + // intentionally empty } void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, @@ -589,7 +727,7 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, instance_t its_instance; eventgroup_t its_eventgroup; major_version_t its_major; - bool subscription_accepted; + bool is_remote_subscriber; if (_size > VSOMEIP_COMMAND_SIZE_POS_MAX) { its_command = _data[VSOMEIP_COMMAND_TYPE_POS]; @@ -603,9 +741,9 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, instance_t its_instance; std::memcpy(&its_instance, &_data[_size - sizeof(instance_t) - sizeof(bool) - - sizeof(bool) - sizeof(bool)], sizeof(instance_t)); + - sizeof(bool)], sizeof(instance_t)); bool its_reliable; - std::memcpy(&its_reliable, &_data[_size - sizeof(bool) - sizeof(bool)], + std::memcpy(&its_reliable, &_data[_size - sizeof(bool)], sizeof(its_reliable)); deserializer_->set_data(&_data[VSOMEIP_COMMAND_PAYLOAD_POS], its_length); @@ -615,9 +753,6 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, its_message->set_instance(its_instance); its_message->set_reliable(its_reliable); if(its_message->get_message_type() == message_type_e::MT_NOTIFICATION) { - bool its_initial(false); - std::memcpy(&its_initial, &_data[_size - sizeof(bool)], sizeof(its_initial)); - its_message->set_initial(its_initial); cache_event_payload(its_message); } host_->on_message(its_message); @@ -648,29 +783,37 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, sizeof(its_eventgroup)); std::memcpy(&its_major, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 6], sizeof(its_major)); - bool is_remote_subscriber; std::memcpy(&is_remote_subscriber, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 7], sizeof(is_remote_subscriber)); - if (is_remote_subscriber || known_clients_.find(its_client) != known_clients_.end()) { - subscription_accepted = host_->on_subscription(its_service, its_instance, its_eventgroup, its_client, true); - if (!is_remote_subscriber) { - (void) find_or_create_local(its_client); - if (!subscription_accepted) { - send_subscribe_nack(its_client, its_service, its_instance, its_eventgroup); - } else { - routing_manager_base::subscribe(its_client, its_service, its_instance, its_eventgroup, - its_major, subscription_type_e::SU_RELIABLE_AND_UNRELIABLE); - send_subscribe_ack(its_client, its_service, its_instance, its_eventgroup); - } + if (is_remote_subscriber) { + // Remote subscriber: Notify routing manager initially + count subscribes + (void)host_->on_subscription(its_service, its_instance, + its_eventgroup, its_client, true); + bool inserted = insert_subscription(its_service, its_instance, its_eventgroup, + VSOMEIP_ROUTING_CLIENT); + if (inserted) { + notify_remote_initally(its_service, its_instance, its_eventgroup); } - } else { - if (!is_remote_subscriber) { - eventgroup_data_t subscription = { its_service, its_instance, - its_eventgroup, its_major, - subscription_type_e::SU_RELIABLE_AND_UNRELIABLE}; - pending_ingoing_subscripitons_[its_client].insert(subscription); + (void)get_remote_subscriber_count(its_service, its_instance, its_eventgroup, true); + } else if (known_clients_.find(its_client) != known_clients_.end()) { + // Local & already known subscriber: create endpoint + send (N)ACK + insert subscription + bool subscription_accepted = host_->on_subscription(its_service, its_instance, + its_eventgroup, its_client, true); + (void) find_or_create_local(its_client); + if (!subscription_accepted) { + send_subscribe_nack(its_client, its_service, its_instance, its_eventgroup); + } else { + routing_manager_base::subscribe(its_client, its_service, its_instance, + its_eventgroup, its_major, subscription_type_e::SU_RELIABLE_AND_UNRELIABLE); + send_subscribe_ack(its_client, its_service, its_instance, its_eventgroup); } + } else { + // Local & not yet known subscriber ~> set pending until subscriber gets known! + eventgroup_data_t subscription = { its_service, its_instance, + its_eventgroup, its_major, + subscription_type_e::SU_RELIABLE_AND_UNRELIABLE}; + pending_ingoing_subscripitons_[its_client].insert(subscription); } VSOMEIP_DEBUG << "SUBSCRIBE(" << std::hex << std::setw(4) << std::setfill('0') << its_client <<"): [" @@ -687,8 +830,19 @@ void routing_manager_proxy::on_message(const byte_t *_data, length_t _size, sizeof(its_instance)); std::memcpy(&its_eventgroup, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 4], sizeof(its_eventgroup)); + std::memcpy(&is_remote_subscriber, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 6], + sizeof(is_remote_subscriber)); host_->on_subscription(its_service, its_instance, its_eventgroup, its_client, false); - routing_manager_base::unsubscribe(its_client, its_service, its_instance, its_eventgroup); + if (!is_remote_subscriber) { + // Local subscriber: withdraw subscription + routing_manager_base::unsubscribe(its_client, its_service, its_instance, its_eventgroup); + } else { + // Remote subscriber: withdraw subscription only if no more remote subscriber exists + if (!get_remote_subscriber_count(its_service, its_instance, its_eventgroup, false)) { + routing_manager_base::unsubscribe(VSOMEIP_ROUTING_CLIENT, its_service, + its_instance, its_eventgroup); + } + } VSOMEIP_DEBUG << "UNSUBSCRIBE(" << std::hex << std::setw(4) << std::setfill('0') << its_client << "): [" << std::hex << std::setw(4) << std::setfill('0') << its_service << "." @@ -743,10 +897,18 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, msg << std::hex << std::setw(2) << std::setfill('0') << (int)_data[i] << " "; VSOMEIP_DEBUG << msg.str(); #endif - state_type_e its_state(state_type_e::ST_DEREGISTERED); - bool restart_sender(_size == 0); + inner_state_type_e its_state(inner_state_type_e::ST_DEREGISTERED); + bool restart_sender(_size == 1); // 1 indicates a routing master stop std::map<service_t, std::map<instance_t, std::tuple< major_version_t, minor_version_t, client_t> > > old_local_services; std::unordered_set<client_t> clients_to_delete; + struct service_info { + service_t service_id_; + instance_t instance_id_; + major_version_t major_; + minor_version_t minor_; + }; + std::forward_list<struct service_info> services_to_remove; + std::forward_list<struct service_info> services_to_add; { std::lock_guard<std::mutex> its_lock(local_services_mutex_); old_local_services = local_services_; @@ -765,7 +927,7 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, i += uint32_t(sizeof(client_t)); if (its_client == client_) { - its_state = state_type_e::ST_REGISTERED; + its_state = inner_state_type_e::ST_REGISTERED; } known_clients.insert(its_client); @@ -806,98 +968,126 @@ void routing_manager_proxy::on_routing_info(const byte_t *_data, } } // Which clients are no longer needed?! - for (auto client : get_connected_clients()) { + for (const auto client : get_connected_clients()) { if (known_clients.find(client) == known_clients.end()) { clients_to_delete.insert(client); } } known_clients_ = known_clients; - } - // inform host about its own registration state changes - if (state_ != its_state) { - if (its_state == state_type_e::ST_REGISTERED) { - VSOMEIP_INFO << std::hex << "Application/Client " << get_client() - << " is registered."; - } else { - VSOMEIP_INFO << std::hex << "Application/Client " << get_client() - << " is deregistered."; + // Check for services that are no longer available + for (const auto &i : old_local_services) { + auto found_service = local_services_.find(i.first); + if (found_service != local_services_.end()) { + for (const auto &j : i.second) { + auto found_instance = found_service->second.find(j.first); + if (found_instance == found_service->second.end()) { + services_to_remove.push_front( + { i.first, j.first, std::get<0>(j.second), + std::get<1>(j.second) }); + } + } + } else { + for (const auto &j : i.second) { + services_to_remove.push_front( + { i.first, j.first, std::get<0>(j.second), + std::get<1>(j.second) }); + } + } } - host_->on_state(its_state); - state_ = its_state; - } - // Check for services that are no longer available - for (auto i : old_local_services) { - auto found_service = local_services_.find(i.first); - if (found_service != local_services_.end()) { - for (auto j : i.second) { - auto found_instance = found_service->second.find(j.first); - if (found_instance == found_service->second.end()) { - auto version_info = j.second; - on_stop_offer_service(i.first, j.first, std::get<0>(version_info), std::get<1>(version_info)); - host_->on_availability(i.first, j.first, false, std::get<0>(version_info), std::get<1>(version_info)); + // Check for services that are newly available + for (const auto &i : local_services_) { + auto found_service = old_local_services.find(i.first); + if (found_service != old_local_services.end()) { + for (const auto &j : i.second) { + auto found_instance = found_service->second.find(j.first); + if (found_instance == found_service->second.end()) { + services_to_add.push_front( + { i.first, j.first, std::get<0>(j.second), + std::get<1>(j.second) }); + } + } + } else { + for (const auto &j : i.second) { + services_to_add.push_front( + { i.first, j.first, std::get<0>(j.second), + std::get<1>(j.second) }); } - } - } else { - for (auto j : i.second) { - on_stop_offer_service(i.first, j.first, std::get<0>(j.second), std::get<1>(j.second)); - host_->on_availability(i.first, j.first, false, std::get<0>(j.second), std::get<1>(j.second)); } } } - // Check for services that are newly available - for (auto i : local_services_) { - auto found_service = old_local_services.find(i.first); - if (found_service != old_local_services.end()) { - for (auto j : i.second) { - auto found_instance = found_service->second.find(j.first); - if (found_instance == found_service->second.end()) { - send_pending_subscriptions(i.first, j.first, std::get<0>(j.second)); - host_->on_availability(i.first, j.first, true, std::get<0>(j.second), std::get<1>(j.second)); - } - } - } else { - for (auto j : i.second) { - send_pending_subscriptions(i.first, j.first, std::get<0>(j.second)); - host_->on_availability(i.first, j.first, true, std::get<0>(j.second), std::get<1>(j.second)); + if (state_ != its_state) { + VSOMEIP_INFO << std::hex << "Application/Client " << get_client() + << (its_state == inner_state_type_e::ST_REGISTERED ? + " is registered." : " is deregistered."); + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (its_state == inner_state_type_e::ST_REGISTERED) { + boost::system::error_code ec; + register_application_timer_.cancel(ec); + send_pending_commands(); } + state_ = its_state; } + + // inform host about its own registration state changes + host_->on_state(static_cast<state_type_e>(its_state)); + + // Notify stop() call about clean deregistration + state_condition_.notify_one(); } - for (client_t client : known_clients_) { - auto its_client = pending_ingoing_subscripitons_.find(client); - if (its_client != pending_ingoing_subscripitons_.end()) { - for (auto subscription : its_client->second) { - bool subscription_accepted = host_->on_subscription(subscription.service_, subscription.instance_, subscription.eventgroup_, client, true); - (void) find_or_create_local(client); - if (!subscription_accepted) { - send_subscribe_nack(client, subscription.service_, subscription.instance_, subscription.eventgroup_); - } else { - routing_manager_base::subscribe(client, subscription.service_, subscription.instance_, subscription.eventgroup_, - subscription.major_, subscription_type_e::SU_RELIABLE_AND_UNRELIABLE); - send_subscribe_ack(client, subscription.service_, subscription.instance_, subscription.eventgroup_); + // Report services that are no longer available + for (const struct service_info &sr : services_to_remove) { + on_stop_offer_service(sr.service_id_, sr.instance_id_, sr.major_, sr.minor_); + host_->on_availability(sr.service_id_, sr.instance_id_, false, sr.major_, sr.minor_); + } + // Report services that are newly available + for (const struct service_info &sa : services_to_add) { + send_pending_subscriptions(sa.service_id_, sa.instance_id_, sa.major_); + host_->on_availability(sa.service_id_, sa.instance_id_, true, sa.major_, sa.minor_); + } + + if (pending_ingoing_subscripitons_.size()) { + for (const client_t client : known_clients_) { + auto its_client = pending_ingoing_subscripitons_.find(client); + if (its_client != pending_ingoing_subscripitons_.end()) { + for (const auto subscription : its_client->second) { + bool subscription_accepted = host_->on_subscription(subscription.service_, subscription.instance_, subscription.eventgroup_, client, true); + (void) find_or_create_local(client); + if (!subscription_accepted) { + send_subscribe_nack(client, subscription.service_, subscription.instance_, subscription.eventgroup_); + } else { + routing_manager_base::subscribe(client, subscription.service_, subscription.instance_, subscription.eventgroup_, + subscription.major_, subscription_type_e::SU_RELIABLE_AND_UNRELIABLE); + send_subscribe_ack(client, subscription.service_, subscription.instance_, subscription.eventgroup_); + } } } + pending_ingoing_subscripitons_.erase(client); } - pending_ingoing_subscripitons_.erase(client); } - if (clients_to_delete.size() || restart_sender) { + if (clients_to_delete.size()) { std::async(std::launch::async, [this, clients_to_delete, restart_sender] () { - for (auto client : clients_to_delete) { + for (const auto client : clients_to_delete) { if (client != VSOMEIP_ROUTING_CLIENT) { remove_local(client); } } - if (restart_sender && is_started_ && sender_) { - VSOMEIP_INFO << std::hex << "Application/Client " << get_client() - <<": Reconnecting to routing manager."; - sender_->start(); - } }); } + + if (restart_sender && is_started_) { + VSOMEIP_INFO << std::hex << "Application/Client " << get_client() + <<": Reconnecting to routing manager."; + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->start(); + } + } } void routing_manager_proxy::register_application() { @@ -910,30 +1100,20 @@ void routing_manager_proxy::register_application() { sizeof(uint32_t)); if (is_connected_) { - (void)sender_->send(its_command, sizeof(its_command)); - - for (auto &po : pending_offers_) - send_offer_service(client_, po.service_, po.instance_, - po.major_, po.minor_); - - std::lock_guard<std::mutex> its_lock(pending_mutex_); - for (auto &per : pending_event_registrations_) - send_register_event(client_, per.service_, per.instance_, - per.event_, per.eventgroups_, - per.is_field_, per.is_provided_); - - for (auto &s : pending_notifications_) { - for (auto &i : s.second) { - for (auto &pn : i.second) { - routing_manager_base::notify(s.first, i.first, - pn.first, pn.second->get_payload()); - } + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + { + std::lock_guard<std::mutex> its_state_lock(state_mutex_); + state_ = inner_state_type_e::ST_REGISTERING; } - } + sender_->send(its_command, sizeof(its_command)); - for (auto &po : pending_requests_) { - send_request_service(client_, po.service_, po.instance_, - po.major_, po.minor_, po.use_exclusive_proxy_); + register_application_timer_.cancel(); + register_application_timer_.expires_from_now(std::chrono::milliseconds(1000)); + register_application_timer_.async_wait( + std::bind( + &routing_manager_proxy::register_application_timeout_cbk, + this, std::placeholders::_1)); } } } @@ -948,7 +1128,12 @@ void routing_manager_proxy::deregister_application() { std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, sizeof(its_size)); if (is_connected_) - (void)sender_->send(&its_command[0], uint32_t(its_command.size())); + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(&its_command[0], uint32_t(its_command.size())); + } + } } void routing_manager_proxy::send_pong() const { @@ -958,8 +1143,14 @@ void routing_manager_proxy::send_pong() const { std::memcpy(&its_pong[VSOMEIP_COMMAND_CLIENT_POS], &client_, sizeof(client_t)); - if (is_connected_) - sender_->send(its_pong, sizeof(its_pong)); + if (is_connected_) { + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_pong, sizeof(its_pong)); + } + } + } } void routing_manager_proxy::send_request_service(client_t _client, service_t _service, @@ -967,32 +1158,32 @@ void routing_manager_proxy::send_request_service(client_t _client, service_t _se minor_version_t _minor, bool _use_exclusive_proxy) { (void)_client; - if (is_connected_) { - byte_t its_command[VSOMEIP_REQUEST_SERVICE_COMMAND_SIZE]; - uint32_t its_size = VSOMEIP_REQUEST_SERVICE_COMMAND_SIZE - - VSOMEIP_COMMAND_HEADER_SIZE; - - its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_REQUEST_SERVICE; - std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, - sizeof(client_)); - std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, - sizeof(its_size)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, - sizeof(_service)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, - sizeof(_instance)); - its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4] = _major; - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 5], &_minor, - sizeof(_minor)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 9], &_use_exclusive_proxy, - sizeof(_use_exclusive_proxy)); - - sender_->send(its_command, sizeof(its_command)); - } else { - service_data_t request = { _service, _instance, _major, _minor, _use_exclusive_proxy }; - std::lock_guard<std::mutex> its_lock(pending_mutex_); - pending_requests_.insert(request); + byte_t its_command[VSOMEIP_REQUEST_SERVICE_COMMAND_SIZE]; + uint32_t its_size = VSOMEIP_REQUEST_SERVICE_COMMAND_SIZE + - VSOMEIP_COMMAND_HEADER_SIZE; + + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_REQUEST_SERVICE; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, + sizeof(client_)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, + sizeof(_service)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, + sizeof(_instance)); + its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4] = _major; + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 5], &_minor, + sizeof(_minor)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 9], &_use_exclusive_proxy, + sizeof(_use_exclusive_proxy)); + + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } } + } void routing_manager_proxy::send_release_service(client_t _client, service_t _service, @@ -1002,18 +1193,21 @@ void routing_manager_proxy::send_release_service(client_t _client, service_t _se uint32_t its_size = VSOMEIP_RELEASE_SERVICE_COMMAND_SIZE - VSOMEIP_COMMAND_HEADER_SIZE; - if (is_connected_) { - its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_RELEASE_SERVICE; - std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, - sizeof(client_)); - std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, - sizeof(its_size)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, - sizeof(_service)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, - sizeof(_instance)); - - sender_->send(its_command, sizeof(its_command)); + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_RELEASE_SERVICE; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, + sizeof(client_)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, + sizeof(_service)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, + sizeof(_instance)); + + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, sizeof(its_command)); + } } } @@ -1021,53 +1215,46 @@ void routing_manager_proxy::send_register_event(client_t _client, service_t _service, instance_t _instance, event_t _event, const std::set<eventgroup_t> &_eventgroups, bool _is_field, bool _is_provided) { - if (is_connected_) { - uint32_t its_eventgroups_size = uint32_t(_eventgroups.size() * sizeof(eventgroup_t)); - byte_t *its_command = new byte_t[VSOMEIP_REGISTER_EVENT_COMMAND_SIZE + its_eventgroups_size]; - uint32_t its_size = VSOMEIP_REGISTER_EVENT_COMMAND_SIZE - + its_eventgroups_size - - VSOMEIP_COMMAND_HEADER_SIZE; - - its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_REGISTER_EVENT; - std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, - sizeof(_client)); - std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, - sizeof(its_size)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, - sizeof(_service)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, - sizeof(_instance)); - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_event, - sizeof(_event)); - its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 6] - = static_cast<byte_t>(_is_field); - its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 7] - = static_cast<byte_t>(_is_provided); - - std::size_t i = 8; - for (auto eg : _eventgroups) { - std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + i], &eg, - sizeof(eventgroup_t)); - i += sizeof(eventgroup_t); - } - sender_->send(its_command, - uint32_t(VSOMEIP_REGISTER_EVENT_COMMAND_SIZE - + its_eventgroups_size)); + uint32_t its_eventgroups_size = uint32_t(_eventgroups.size() * sizeof(eventgroup_t)); + byte_t *its_command = new byte_t[VSOMEIP_REGISTER_EVENT_COMMAND_SIZE + its_eventgroups_size]; + uint32_t its_size = VSOMEIP_REGISTER_EVENT_COMMAND_SIZE + + its_eventgroups_size + - VSOMEIP_COMMAND_HEADER_SIZE; - delete[] its_command; - } else { - event_data_t registration = { - _service, - _instance, - _event, - _is_field, - _is_provided, - _eventgroups - }; - std::lock_guard<std::mutex> its_lock(pending_mutex_); - pending_event_registrations_.insert(registration); + its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_REGISTER_EVENT; + std::memcpy(&its_command[VSOMEIP_COMMAND_CLIENT_POS], &client_, + sizeof(_client)); + std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, + sizeof(its_size)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS], &_service, + sizeof(_service)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 2], &_instance, + sizeof(_instance)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_event, + sizeof(_event)); + its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 6] + = static_cast<byte_t>(_is_field); + its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 7] + = static_cast<byte_t>(_is_provided); + + std::size_t i = 8; + for (auto eg : _eventgroups) { + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + i], &eg, + sizeof(eventgroup_t)); + i += sizeof(eventgroup_t); } + + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, + uint32_t(VSOMEIP_REGISTER_EVENT_COMMAND_SIZE + + its_eventgroups_size)); + } + } + + delete[] its_command; } void routing_manager_proxy::on_subscribe_ack(client_t _client, @@ -1099,13 +1286,12 @@ void routing_manager_proxy::on_identify_response(client_t _client, service_t _se sizeof(_instance)); std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_reliable, sizeof(_reliable)); - sender_->send(its_command, size); -} - -bool routing_manager_proxy::queue_message(const byte_t *_data, uint32_t _size) const { - std::shared_ptr<local_server_endpoint_impl> its_server_endpoint - = std::dynamic_pointer_cast<local_server_endpoint_impl>(receiver_); - return its_server_endpoint->queue_message(_data, _size); + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + sender_->send(its_command, size); + } + } } void routing_manager_proxy::cache_event_payload( @@ -1124,7 +1310,10 @@ void routing_manager_proxy::cache_event_payload( // create a placeholder field until someone requests this event with // full information like eventgroup, field or not etc. routing_manager_base::register_event(host_->get_client(), its_service, - its_instance, its_method, its_eventgroups, true, false, false, true); + its_instance, its_method, its_eventgroups, true, + std::chrono::milliseconds::zero(), false, + nullptr, + false, false, true); std::shared_ptr<event> its_event = find_event(its_service, its_instance, its_method); if (its_event) { its_event->set_payload_dont_notify(_message->get_payload()); @@ -1135,6 +1324,7 @@ void routing_manager_proxy::cache_event_payload( void routing_manager_proxy::send_pending_subscriptions(service_t _service, instance_t _instance, major_version_t _major) { + std::lock_guard<std::mutex> its_lock(state_mutex_); for (auto &ps : pending_subscriptions_) { if (ps.service_ == _service && ps.instance_ == _instance && ps.major_ == _major) { @@ -1163,4 +1353,154 @@ void routing_manager_proxy::on_stop_offer_service(service_t _service, } } +void routing_manager_proxy::send_pending_commands() { + for (auto &po : pending_offers_) + send_offer_service(client_, po.service_, po.instance_, + po.major_, po.minor_); + + for (auto &per : pending_event_registrations_) + send_register_event(client_, per.service_, per.instance_, + per.event_, per.eventgroups_, + per.is_field_, per.is_provided_); + + for (auto &s : pending_notifications_) { + for (auto &i : s.second) { + for (auto &pn : i.second) { + routing_manager_base::notify(s.first, i.first, + pn.first, pn.second->get_payload(), false); + } + } + } + + for (auto &po : pending_requests_) { + send_request_service(client_, po.service_, po.instance_, + po.major_, po.minor_, po.use_exclusive_proxy_); + } +} + +void routing_manager_proxy::init_receiver() { + std::stringstream its_client; + its_client << VSOMEIP_BASE_PATH << std::hex << client_; +#ifdef WIN32 + ::_unlink(its_client.str().c_str()); + int port = VSOMEIP_INTERNAL_BASE_PORT + client_; +#else + if (-1 == ::unlink(its_client.str().c_str()) && errno != ENOENT) { + VSOMEIP_ERROR << "routing_manager_proxy::init_receiver unlink failed (" + << its_client.str() << "): "<< std::strerror(errno); + } + const mode_t previous_mask(::umask(static_cast<mode_t>(configuration_->get_umask()))); +#endif + try { + receiver_ = std::make_shared<local_server_endpoint_impl>(shared_from_this(), +#ifdef WIN32 + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port), +#else + boost::asio::local::stream_protocol::endpoint(its_client.str()), +#endif + io_, configuration_->get_max_message_size_local()); +#ifdef WIN32 + VSOMEIP_DEBUG << "Listening at " << port; +#else + VSOMEIP_DEBUG<< "Listening at " << its_client.str(); +#endif + } catch (const std::exception &e) { + host_->on_error(error_code_e::SERVER_ENDPOINT_CREATION_FAILED); + VSOMEIP_ERROR << "Client ID: " << std::hex << client_ << ": " << e.what(); + } +#ifndef WIN32 + ::umask(previous_mask); +#endif +} + +void routing_manager_proxy::notify_remote_initally(service_t _service, instance_t _instance, + eventgroup_t _eventgroup) { + auto its_eventgroup = find_eventgroup(_service, _instance, _eventgroup); + if (its_eventgroup) { + std::set<std::shared_ptr<event> > its_events + = its_eventgroup->get_events(); + for (auto e : its_events) { + if (e->is_field() && e->is_set()) { + std::shared_ptr<message> its_notification + = runtime::get()->create_notification(); + its_notification->set_service(_service); + its_notification->set_instance(_instance); + its_notification->set_method(e->get_event()); + its_notification->set_payload(e->get_payload()); + auto service_info = find_service(_service, _instance); + if (service_info) { + its_notification->set_interface_version(service_info->get_major()); + } + std::lock_guard<std::mutex> its_lock(serialize_mutex_); + if (serializer_->serialize(its_notification.get())) { + { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + if (sender_) { + send_local(sender_, VSOMEIP_ROUTING_CLIENT, serializer_->get_data(), + serializer_->get_size(), _instance, true, false, VSOMEIP_NOTIFY); + } + } + serializer_->reset(); + } else { + VSOMEIP_ERROR << "Failed to serialize message. Check message size!"; + } + } + } + } + +} + +uint32_t routing_manager_proxy::get_remote_subscriber_count(service_t _service, + instance_t _instance, eventgroup_t _eventgroup, bool _increment) { + uint32_t count (0); + bool found(false); + auto found_service = remote_subscriber_count_.find(_service); + if (found_service != remote_subscriber_count_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + auto found_group = found_instance->second.find(_eventgroup); + if (found_group != found_instance->second.end()) { + found = true; + if (_increment) { + found_group->second = found_group->second + 1; + } else { + if (found_group->second > 0) { + found_group->second = found_group->second - 1; + } + } + count = found_group->second; + } + } + } + if (!found) { + if (_increment) { + remote_subscriber_count_[_service][_instance][_eventgroup] = 1; + count = 1; + } + } + return count; +} + +void routing_manager_proxy::register_application_timeout_cbk( + boost::system::error_code const &_error) { + if (!_error) { + bool register_again(false); + { + std::lock_guard<std::mutex> its_lock(state_mutex_); + if (state_ != inner_state_type_e::ST_REGISTERED) { + state_ = inner_state_type_e::ST_DEREGISTERED; + register_again = true; + } + } + if (register_again) { + std::lock_guard<std::recursive_mutex> its_lock(sender_mutex_); + VSOMEIP_WARNING << std::hex << "Client 0x" << get_client() << " register timeout!" + << " : Restart route to stub!"; + if (sender_) { + sender_->start(); + } + } + } +} + } // namespace vsomeip diff --git a/implementation/routing/src/routing_manager_stub.cpp b/implementation/routing/src/routing_manager_stub.cpp index 01ca85a..900f266 100644 --- a/implementation/routing/src/routing_manager_stub.cpp +++ b/implementation/routing/src/routing_manager_stub.cpp @@ -6,6 +6,7 @@ #include <chrono> #include <functional> #include <iomanip> +#include <forward_list> #ifndef WIN32 // for umask @@ -18,6 +19,7 @@ #include <vsomeip/constants.hpp> #include <vsomeip/primitive_types.hpp> #include <vsomeip/runtime.hpp> +#include <vsomeip/error.hpp> #include "../include/routing_manager_stub.hpp" #include "../include/routing_manager_stub_host.hpp" @@ -30,68 +32,39 @@ namespace vsomeip { +const std::vector<byte_t> routing_manager_stub::its_ping_( + { VSOMEIP_PING, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); + routing_manager_stub::routing_manager_stub( routing_manager_stub_host *_host, std::shared_ptr<configuration> _configuration) : host_(_host), io_(_host->get_io()), watchdog_timer_(_host->get_io()), + endpoint_(nullptr), + local_receiver_(nullptr), configuration_(_configuration), - routingCommandSize_(VSOMEIP_ROUTING_INFO_SIZE_INIT) { + routingCommandSize_(VSOMEIP_ROUTING_INFO_SIZE_INIT), + client_registration_running_(false), + configured_watchdog_timeout_(configuration_->get_watchdog_timeout()), + pinged_clients_timer_(io_) { } routing_manager_stub::~routing_manager_stub() { } void routing_manager_stub::init() { - std::stringstream its_endpoint_path; - its_endpoint_path << VSOMEIP_BASE_PATH << VSOMEIP_ROUTING_CLIENT; - endpoint_path_ = its_endpoint_path.str(); - - std::stringstream its_local_receiver_path; - its_local_receiver_path << VSOMEIP_BASE_PATH << std::hex << host_->get_client(); - local_receiver_path_ = its_local_receiver_path.str(); -#if WIN32 - ::_unlink(endpoint_path_.c_str()); - ::_unlink(local_receiver_path_.c_str()); - int port = VSOMEIP_INTERNAL_BASE_PORT; - VSOMEIP_DEBUG << "Routing endpoint at " << port; -#else - ::unlink(endpoint_path_.c_str()); - ::unlink(local_receiver_path_.c_str()); - VSOMEIP_DEBUG << "Routing endpoint at " << endpoint_path_; - - const mode_t previous_mask(::umask(static_cast<mode_t>(configuration_->get_umask()))); -#endif - - endpoint_ = - std::make_shared < local_server_endpoint_impl - > (shared_from_this(), - #ifdef WIN32 - boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port), - #else - boost::asio::local::stream_protocol::endpoint(endpoint_path_), - #endif - io_, configuration_->get_max_message_size_local()); - - local_receiver_ = - std::make_shared < local_server_endpoint_impl - > (shared_from_this(), - #ifdef WIN32 - boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port + host_->get_client()), - #else - boost::asio::local::stream_protocol::endpoint(local_receiver_path_), - #endif - io_, configuration_->get_max_message_size_local()); - -#ifndef WIN32 - ::umask(previous_mask); -#endif + init_routing_endpoint(); } void routing_manager_stub::start() { - endpoint_->start(); - local_receiver_->start(); + if(!endpoint_) { + // application has been stopped and started again + init_routing_endpoint(); + } + if(endpoint_) { + endpoint_->start(); + } client_registration_running_ = true; client_registration_thread_ = std::make_shared<std::thread>( @@ -107,6 +80,9 @@ void routing_manager_stub::start() { } else { VSOMEIP_INFO << "Watchdog is disabled!"; } + + std::lock_guard<std::mutex> its_lock(routing_info_mutex_); + routing_info_[host_->get_client()].first = 0; } void routing_manager_stub::stop() { @@ -117,17 +93,31 @@ void routing_manager_stub::stop() { } watchdog_timer_.cancel(); - endpoint_->stop(); - local_receiver_->stop(); + endpoint_->stop(); + endpoint_ = nullptr; #ifdef WIN32 ::_unlink(endpoint_path_.c_str()); - ::_unlink(local_receiver_path_.c_str()); #else - ::unlink(endpoint_path_.c_str()); - ::unlink(local_receiver_path_.c_str()); + if (-1 == ::unlink(endpoint_path_.c_str())) { + VSOMEIP_ERROR << "routing_manager_stub::stop() unlink failed (" + << endpoint_path_ << "): "<< std::strerror(errno); + } #endif + if(local_receiver_) { + local_receiver_->stop(); + local_receiver_ = nullptr; +#ifdef WIN32 + ::_unlink(local_receiver_path_.c_str()); +#else + if (-1 == ::unlink(local_receiver_path_.c_str())) { + VSOMEIP_ERROR << "routing_manager_stub::stop() unlink (local receiver) failed (" + << local_receiver_path_ << "): "<< std::strerror(errno); + } +#endif + } + broadcast_routing_info(true); } @@ -203,7 +193,7 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, VSOMEIP_INFO << "Application/Client " << std::hex << std::setw(4) << std::setfill('0') << its_client << " is registering."; - pending_client_registrations_[its_client].push_back(true); + pending_client_registrations_[its_client].push_back(registration_type_e::REGISTER); client_registration_condition_.notify_one(); } break; @@ -214,7 +204,7 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, VSOMEIP_INFO << "Application/Client " << std::hex << std::setw(4) << std::setfill('0') << its_client << " is deregistering."; - pending_client_registrations_[its_client].push_back(false); + pending_client_registrations_[its_client].push_back(registration_type_e::DEREGISTER); client_registration_condition_.notify_one(); } break; @@ -237,11 +227,6 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(its_minor)); host_->offer_service(its_client, its_service, its_instance, its_major, its_minor); - VSOMEIP_DEBUG << "OFFER(" - << std::hex << std::setw(4) << std::setfill('0') << its_client <<"): [" - << std::hex << std::setw(4) << std::setfill('0') << its_service << "." - << std::hex << std::setw(4) << std::setfill('0') << its_instance - << ":" << std::dec << int(its_major) << "." << std::dec << its_minor << "]"; break; case VSOMEIP_STOP_OFFER_SERVICE: @@ -257,11 +242,6 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(its_minor)); host_->stop_offer_service(its_client, its_service, its_instance, its_major, its_minor); - VSOMEIP_DEBUG << "STOP OFFER(" - << std::hex << std::setw(4) << std::setfill('0') << its_client <<"): [" - << std::hex << std::setw(4) << std::setfill('0') << its_service << "." - << std::hex << std::setw(4) << std::setfill('0') << its_instance - << ":" << std::dec << int(its_major) << "." << its_minor << "]"; break; case VSOMEIP_SUBSCRIBE: @@ -279,12 +259,6 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(its_subscription_type)); host_->subscribe(its_client, its_service, its_instance, its_eventgroup, its_major, its_subscription_type); - VSOMEIP_DEBUG << "SUBSCRIBE(" - << std::hex << std::setw(4) << std::setfill('0') << its_client <<"): [" - << std::hex << std::setw(4) << std::setfill('0') << its_service << "." - << std::hex << std::setw(4) << std::setfill('0') << its_instance << "." - << std::hex << std::setw(4) << std::setfill('0') << its_eventgroup << ":" - << std::dec << (uint16_t)its_major << "]"; break; case VSOMEIP_UNSUBSCRIBE: @@ -296,11 +270,6 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(its_eventgroup)); host_->unsubscribe(its_client, its_service, its_instance, its_eventgroup); - VSOMEIP_DEBUG << "UNSUBSCRIBE(" - << std::hex << std::setw(4) << std::setfill('0') << its_client << "): [" - << std::hex << std::setw(4) << std::setfill('0') << its_service << "." - << std::hex << std::setw(4) << std::setfill('0') << its_instance << "." - << std::hex << std::setw(4) << std::setfill('0') << its_eventgroup << "]"; break; case VSOMEIP_SUBSCRIBE_ACK: @@ -339,10 +308,8 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, its_data[VSOMEIP_SERVICE_POS_MIN], its_data[VSOMEIP_SERVICE_POS_MAX]); std::memcpy(&its_instance, &_data[_size - sizeof(instance_t) - - sizeof(bool) - sizeof(bool) - - sizeof(bool)], sizeof(its_instance)); - std::memcpy(&its_reliable, &_data[_size - sizeof(bool) - - sizeof(bool)], sizeof(its_reliable)); + - sizeof(bool) - sizeof(bool)], sizeof(its_instance)); + std::memcpy(&its_reliable, &_data[_size - sizeof(bool)], sizeof(its_reliable)); host_->on_message(its_service, its_instance, its_data, its_size, its_reliable); break; @@ -355,8 +322,7 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, its_data[VSOMEIP_CLIENT_POS_MIN], its_data[VSOMEIP_CLIENT_POS_MAX]); std::memcpy(&its_instance, &_data[_size - sizeof(instance_t) - - sizeof(bool) - sizeof(bool) - - sizeof(bool)], sizeof(its_instance)); + - sizeof(bool) - sizeof(bool)], sizeof(its_instance)); host_->on_notification(its_client, its_service, its_instance, its_data, its_size); break; case VSOMEIP_NOTIFY_ONE: @@ -365,8 +331,7 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, its_data[VSOMEIP_SERVICE_POS_MIN], its_data[VSOMEIP_SERVICE_POS_MAX]); std::memcpy(&its_instance, &_data[_size - sizeof(instance_t) - - sizeof(bool) - sizeof(bool) - - sizeof(bool)], sizeof(its_instance)); + - sizeof(bool) - sizeof(bool)], sizeof(its_instance)); host_->on_notification(its_client, its_service, its_instance, its_data, its_size, true); break; @@ -384,11 +349,6 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, sizeof(use_exclusive_proxy)); host_->request_service(its_client, its_service, its_instance, its_major, its_minor, use_exclusive_proxy); - VSOMEIP_DEBUG << "REQUEST(" - << std::hex << std::setw(4) << std::setfill('0') << its_client << "): [" - << std::hex << std::setw(4) << std::setfill('0') << its_service << "." - << std::hex << std::setw(4) << std::setfill('0') << its_instance << ":" - << std::dec << int(its_major) << "." << std::dec << its_minor << "]"; break; case VSOMEIP_RELEASE_SERVICE: @@ -398,10 +358,6 @@ void routing_manager_stub::on_message(const byte_t *_data, length_t _size, &_data[VSOMEIP_COMMAND_PAYLOAD_POS + 2], sizeof(its_instance)); host_->release_service(its_client, its_service, its_instance); - VSOMEIP_DEBUG << "RELEASE(" - << std::hex << std::setw(4) << std::setfill('0') << its_client << "): [" - << std::hex << std::setw(4) << std::setfill('0') << its_service << "." - << std::hex << std::setw(4) << std::setfill('0') << its_instance << "]"; break; case VSOMEIP_REGISTER_EVENT: @@ -499,14 +455,15 @@ void routing_manager_stub::on_deregister_application(client_t _client) { for (auto &its_instance : its_service.second) { auto its_version = its_instance.second; routing_info_mutex_.unlock(); - host_->on_stop_offer_service(its_service.first, its_instance.first, its_version.first, its_version.second); + host_->on_stop_offer_service(_client, its_service.first, + its_instance.first, its_version.first, + its_version.second); routing_info_mutex_.lock(); } } } routing_info_.erase(_client); routing_info_mutex_.unlock(); - host_->remove_local(_client); } void routing_manager_stub::client_registration_func(void) { @@ -516,34 +473,94 @@ void routing_manager_stub::client_registration_func(void) { client_registration_condition_.wait(its_lock); } - std::map<client_t, std::vector<bool>> its_registrations( - pending_client_registrations_); + std::map<client_t, std::vector<registration_type_e>> its_registrations( + pending_client_registrations_); pending_client_registrations_.clear(); its_lock.unlock(); for (auto r : its_registrations) { for (auto b : r.second) { - if (b) { + if (b == registration_type_e::REGISTER) { on_register_application(r.first); } else { on_deregister_application(r.first); } + { + // Inform (de)registered client first then broadcast all others + // Don't inform client if we deregister because of an client + // endpoint error to avoid writing in an already closed socket + std::lock_guard<std::mutex> its_guard(routing_info_mutex_); + if (b != registration_type_e::DEREGISTER_ERROR_CASE) { + send_routing_info(r.first); + } + for (auto& info : routing_info_) { + if (info.first != VSOMEIP_ROUTING_CLIENT && + info.first != host_->get_client() && + info.first != r.first) { + send_routing_info(info.first); + } + } + } + if (b == registration_type_e::DEREGISTER + || b == registration_type_e::DEREGISTER_ERROR_CASE) { + host_->remove_local(r.first); + } + if (b == registration_type_e::DEREGISTER_ERROR_CASE) { + utility::release_client_id(r.first); + host_->confirm_pending_offers(r.first); + } } } - - { - std::lock_guard<std::mutex> its_guard(routing_info_mutex_); - broadcast_routing_info(); - } - its_lock.lock(); } } +void routing_manager_stub::init_routing_endpoint() { + std::stringstream its_endpoint_path; + its_endpoint_path << VSOMEIP_BASE_PATH << VSOMEIP_ROUTING_CLIENT; + endpoint_path_ = its_endpoint_path.str(); + +#if WIN32 + ::_unlink(endpoint_path_.c_str()); + int port = VSOMEIP_INTERNAL_BASE_PORT; + VSOMEIP_DEBUG << "Routing endpoint at " << port; +#else + if (-1 == ::unlink(endpoint_path_.c_str()) && errno != ENOENT) { + VSOMEIP_ERROR << "routing_manager_stub::init_endpoint unlink failed (" + << endpoint_path_ << "): "<< std::strerror(errno); + } + VSOMEIP_DEBUG << "Routing endpoint at " << endpoint_path_; + + const mode_t previous_mask(::umask(static_cast<mode_t>(configuration_->get_umask()))); +#endif + + try { + endpoint_ = + std::make_shared < local_server_endpoint_impl + > (shared_from_this(), + #ifdef WIN32 + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port), + #else + boost::asio::local::stream_protocol::endpoint(endpoint_path_), + #endif + io_, configuration_->get_max_message_size_local()); + } catch (const std::exception &e) { + VSOMEIP_ERROR << ERROR_INFO[static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED)] + << " (" << static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED) << ")"; + VSOMEIP_ERROR << "routing_manager_stub::init_endpoint Client ID: " + << std::hex << VSOMEIP_ROUTING_CLIENT << ": " << e.what(); + } +#ifndef WIN32 + ::umask(previous_mask); +#endif +} void routing_manager_stub::on_offer_service(client_t _client, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) { std::lock_guard<std::mutex> its_guard(routing_info_mutex_); + if (_client == host_->get_client()) { + create_local_receiver(); + } routing_info_[_client].second[_service][_instance] = std::make_pair(_major, _minor); broadcast_routing_info(); } @@ -603,6 +620,7 @@ void routing_manager_stub::send_routing_info(client_t _client, bool _empty) { // Routing info loop for (auto &info : routing_info_) { if (_empty) { + its_command.push_back(0x0); break; } std::size_t its_size_pos = its_command.size(); @@ -658,6 +676,9 @@ void routing_manager_stub::send_routing_info(client_t _client, bool _empty) { // File overall size std::size_t its_size = its_command.size() - VSOMEIP_COMMAND_PAYLOAD_POS; + if (_empty) { + its_size = 1; // Indicates stopping routing! + } std::memcpy(&its_command[VSOMEIP_COMMAND_SIZE_POS_MIN], &its_size, sizeof(uint32_t)); its_size += VSOMEIP_COMMAND_PAYLOAD_POS; @@ -686,16 +707,16 @@ void routing_manager_stub::send_routing_info(client_t _client, bool _empty) { void routing_manager_stub::broadcast_routing_info(bool _empty) { for (auto& info : routing_info_) { - if (info.first != VSOMEIP_ROUTING_CLIENT) { + if (info.first != VSOMEIP_ROUTING_CLIENT && info.first != host_->get_client()) { send_routing_info(info.first, _empty); } } } -void routing_manager_stub::broadcast(std::vector<byte_t> &_command) const { +void routing_manager_stub::broadcast(const std::vector<byte_t> &_command) const { std::lock_guard<std::mutex> its_guard(routing_info_mutex_); for (auto a : routing_info_) { - if (a.first > 0) { + if (a.first != VSOMEIP_ROUTING_CLIENT && a.first != host_->get_client()) { std::shared_ptr<endpoint> its_endpoint = host_->find_local(a.first); if (its_endpoint) { @@ -725,6 +746,9 @@ void routing_manager_stub::send_subscribe(std::shared_ptr<vsomeip::endpoint> _ta sizeof(_eventgroup)); its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 6] = _major; its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 7] = _is_remote_subscriber; + // set byte for subscription_type to zero. It's only used + // in subscribe messages sent from rm_proxies to rm_stub. + its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 8] = 0x0; _target->send(its_command, sizeof(its_command)); } @@ -732,7 +756,7 @@ void routing_manager_stub::send_subscribe(std::shared_ptr<vsomeip::endpoint> _ta void routing_manager_stub::send_unsubscribe(std::shared_ptr<vsomeip::endpoint> _target, client_t _client, service_t _service, instance_t _instance, - eventgroup_t _eventgroup) { + eventgroup_t _eventgroup, bool _is_remote_subscriber) { if (_target) { byte_t its_command[VSOMEIP_UNSUBSCRIBE_COMMAND_SIZE]; uint32_t its_size = VSOMEIP_UNSUBSCRIBE_COMMAND_SIZE @@ -748,6 +772,8 @@ void routing_manager_stub::send_unsubscribe(std::shared_ptr<vsomeip::endpoint> _ sizeof(_instance)); std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 4], &_eventgroup, sizeof(_eventgroup)); + std::memcpy(&its_command[VSOMEIP_COMMAND_PAYLOAD_POS + 6], &_is_remote_subscriber, + sizeof(_is_remote_subscriber)); _target->send(its_command, sizeof(its_command)); } @@ -758,8 +784,8 @@ void routing_manager_stub::send_subscribe_ack(client_t _client, service_t _servi std::shared_ptr<endpoint> its_endpoint = host_->find_local(_client); if (its_endpoint) { - byte_t its_command[VSOMEIP_SUBSCRIBE_COMMAND_SIZE]; - uint32_t its_size = VSOMEIP_SUBSCRIBE_COMMAND_SIZE + byte_t its_command[VSOMEIP_SUBSCRIBE_ACK_COMMAND_SIZE]; + uint32_t its_size = VSOMEIP_SUBSCRIBE_ACK_COMMAND_SIZE - VSOMEIP_COMMAND_HEADER_SIZE; its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_SUBSCRIBE_ACK; @@ -783,8 +809,8 @@ void routing_manager_stub::send_subscribe_nack(client_t _client, service_t _serv std::shared_ptr<endpoint> its_endpoint = host_->find_local(_client); if (its_endpoint) { - byte_t its_command[VSOMEIP_SUBSCRIBE_COMMAND_SIZE]; - uint32_t its_size = VSOMEIP_SUBSCRIBE_COMMAND_SIZE + byte_t its_command[VSOMEIP_SUBSCRIBE_NACK_COMMAND_SIZE]; + uint32_t its_size = VSOMEIP_SUBSCRIBE_NACK_COMMAND_SIZE - VSOMEIP_COMMAND_HEADER_SIZE; its_command[VSOMEIP_COMMAND_TYPE_POS] = VSOMEIP_SUBSCRIBE_NACK; @@ -803,23 +829,43 @@ void routing_manager_stub::send_subscribe_nack(client_t _client, service_t _serv } } +bool routing_manager_stub::contained_in_routing_info( + client_t _client, service_t _service, instance_t _instance, + major_version_t _major, minor_version_t _minor) const { + std::lock_guard<std::mutex> its_guard(routing_info_mutex_); + auto found_client = routing_info_.find(_client); + if (found_client != routing_info_.end()) { + auto found_service = found_client->second.second.find(_service); + if (found_service != found_client->second.second.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + if (found_instance->second.first == _major + && found_instance->second.second == _minor) { + return true; + } + } + } + } + return false; +} + // Watchdog void routing_manager_stub::broadcast_ping() const { - const byte_t its_ping[] = { - VSOMEIP_PING, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - - std::vector<byte_t> its_command(sizeof(its_ping)); - its_command.assign(its_ping, its_ping + sizeof(its_ping)); - broadcast(its_command); + broadcast(its_ping_); } void routing_manager_stub::on_pong(client_t _client) { - auto found_info = routing_info_.find(_client); - if (found_info != routing_info_.end()) { - found_info->second.first = 0; - } else { - VSOMEIP_ERROR << "Received PONG from unregistered application!"; + { + std::lock_guard<std::mutex> its_lock(routing_info_mutex_); + auto found_info = routing_info_.find(_client); + if (found_info != routing_info_.end()) { + found_info->second.first = 0; + } else { + VSOMEIP_ERROR << "Received PONG from unregistered application!"; + } } + remove_from_pinged_clients(_client); + host_->on_pong(_client); } void routing_manager_stub::start_watchdog() { @@ -864,8 +910,7 @@ void routing_manager_stub::check_watchdog() { } } for (auto i : lost) { - utility::release_client_id(i); - on_deregister_application(i); + deregister_erroneous_client(i); } start_watchdog(); }; @@ -873,10 +918,201 @@ void routing_manager_stub::check_watchdog() { watchdog_timer_.async_wait(its_callback); } -bool routing_manager_stub::queue_message(const byte_t *_data, uint32_t _size) { - std::shared_ptr<local_server_endpoint_impl> its_server_endpoint - = std::dynamic_pointer_cast<local_server_endpoint_impl>(endpoint_); - return its_server_endpoint->queue_message(_data, _size); +void routing_manager_stub::create_local_receiver() { + if (local_receiver_) { + return; + } + std::stringstream its_local_receiver_path; + its_local_receiver_path << VSOMEIP_BASE_PATH << std::hex << host_->get_client(); + local_receiver_path_ = its_local_receiver_path.str(); +#if WIN32 + ::_unlink(local_receiver_path_.c_str()); + int port = VSOMEIP_INTERNAL_BASE_PORT; +#else + if (-1 == ::unlink(local_receiver_path_.c_str()) && errno != ENOENT) { + VSOMEIP_ERROR << "routing_manager_stub::create_local_receiver unlink (local receiver) failed (" + << local_receiver_path_ << "): "<< std::strerror(errno); + } + + const mode_t previous_mask(::umask(static_cast<mode_t>(configuration_->get_umask()))); +#endif + + try { + local_receiver_ = + std::make_shared < local_server_endpoint_impl + > (shared_from_this(), + #ifdef WIN32 + boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port + host_->get_client()), + #else + boost::asio::local::stream_protocol::endpoint(local_receiver_path_), + #endif + io_, configuration_->get_max_message_size_local()); + } catch (const std::exception &e) { + VSOMEIP_ERROR << ERROR_INFO[static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED)] + << " (" << static_cast<int>(error_code_e::SERVER_ENDPOINT_CREATION_FAILED) << ")"; + VSOMEIP_ERROR << "routing_manager_stub::_local_receiver Client ID: " + << std::hex << VSOMEIP_ROUTING_CLIENT << ": " << e.what(); + } +#ifndef WIN32 + ::umask(previous_mask); +#endif + local_receiver_->start(); +} + +bool routing_manager_stub::send_ping(client_t _client) { + std::shared_ptr<endpoint> its_endpoint = host_->find_local(_client); + if (!its_endpoint) { + return false; + } + + { + std::lock_guard<std::mutex> its_lock(pinged_clients_mutex_); + + if (pinged_clients_.find(_client) != pinged_clients_.end()) { + // client was already pinged: don't ping again and wait for answer + // or timeout of previous ping. + return true; + } + + boost::system::error_code ec; + pinged_clients_timer_.cancel(ec); + if (ec) { + VSOMEIP_ERROR << "routing_manager_stub::send_ping cancellation of " + "timer failed: " << ec.message(); + } + const std::chrono::steady_clock::time_point now( + std::chrono::steady_clock::now()); + + std::chrono::milliseconds next_timeout(configured_watchdog_timeout_); + for (const auto &tp : pinged_clients_) { + const std::chrono::milliseconds its_clients_timeout = + std::chrono::duration_cast<std::chrono::milliseconds>( + now - tp.second); + if (next_timeout > its_clients_timeout) { + next_timeout = its_clients_timeout; + } + } + + pinged_clients_[_client] = now; + + ec.clear(); + pinged_clients_timer_.expires_from_now(next_timeout, ec); + if (ec) { + VSOMEIP_ERROR<< "routing_manager_stub::send_ping setting " + "expiry time of timer failed: " << ec.message(); + } + pinged_clients_timer_.async_wait( + std::bind(&routing_manager_stub::on_ping_timer_expired, this, + std::placeholders::_1)); + return its_endpoint->send(&its_ping_[0], uint32_t(its_ping_.size()), + true); + } +} + +void routing_manager_stub::on_ping_timer_expired( + boost::system::error_code const &_error) { + if(_error) { + return; + } + std::forward_list<client_t> timed_out_clients; + std::chrono::milliseconds next_timeout(configured_watchdog_timeout_); + bool pinged_clients_remaining(false); + + { + // remove timed out clients + std::lock_guard<std::mutex> its_lock(pinged_clients_mutex_); + const std::chrono::steady_clock::time_point now( + std::chrono::steady_clock::now()); + + for (auto client_iter = pinged_clients_.begin(); + client_iter != pinged_clients_.end(); ) { + if ((now - client_iter->second) >= configured_watchdog_timeout_) { + timed_out_clients.push_front(client_iter->first); + client_iter = pinged_clients_.erase(client_iter); + } else { + ++client_iter; + } + } + pinged_clients_remaining = (pinged_clients_.size() > 0); + + if(pinged_clients_remaining) { + // find out next timeout + for (const auto &tp : pinged_clients_) { + const std::chrono::milliseconds its_clients_timeout = + std::chrono::duration_cast<std::chrono::milliseconds>( + now - tp.second); + if (next_timeout > its_clients_timeout) { + next_timeout = its_clients_timeout; + } + } + } + } + + for (const client_t client : timed_out_clients) { + // client did not respond to ping. Report client_endpoint_error + // in order to accept pending offers trying to replace the offers of + // the now not responding client + host_->on_clientendpoint_error(client); + } + if (pinged_clients_remaining) { + boost::system::error_code ec; + pinged_clients_timer_.expires_from_now(next_timeout, ec); + if (ec) { + VSOMEIP_ERROR<< "routing_manager_stub::on_ping_timer_expired " + "setting expiry time of timer failed: " << ec.message(); + } + pinged_clients_timer_.async_wait( + std::bind(&routing_manager_stub::on_ping_timer_expired, this, + std::placeholders::_1)); + } +} + +void routing_manager_stub::remove_from_pinged_clients(client_t _client) { + std::lock_guard<std::mutex> its_lock(pinged_clients_mutex_); + if (!pinged_clients_.size()) { + return; + } + boost::system::error_code ec; + pinged_clients_timer_.cancel(ec); + if (ec) { + VSOMEIP_ERROR << "routing_manager_stub::remove_from_pinged_clients " + "cancellation of timer failed: " << ec.message(); + } + pinged_clients_.erase(_client); + + if (!pinged_clients_.size()) { + return; + } + const std::chrono::steady_clock::time_point now( + std::chrono::steady_clock::now()); + std::chrono::milliseconds next_timeout(configured_watchdog_timeout_); + // find out next timeout + for (const auto &tp : pinged_clients_) { + const std::chrono::milliseconds its_clients_timeout = + std::chrono::duration_cast<std::chrono::milliseconds>( + now - tp.second); + if (next_timeout > its_clients_timeout) { + next_timeout = its_clients_timeout; + } + } + ec.clear(); + pinged_clients_timer_.expires_from_now(next_timeout, ec); + if (ec) { + VSOMEIP_ERROR<< "routing_manager_stub::remove_from_pinged_clients " + "setting expiry time of timer failed: " << ec.message(); + } + pinged_clients_timer_.async_wait( + std::bind(&routing_manager_stub::on_ping_timer_expired, this, + std::placeholders::_1)); +} + +void routing_manager_stub::deregister_erroneous_client(client_t _client) { + std::lock_guard<std::mutex> its_lock(client_registration_mutex_); + VSOMEIP_INFO << "Application/Client " + << std::hex << std::setw(4) << std::setfill('0') + << _client << " is deregistering."; + pending_client_registrations_[_client].push_back(registration_type_e::DEREGISTER_ERROR_CASE); + client_registration_condition_.notify_one(); } } // namespace vsomeip diff --git a/implementation/runtime/include/application_impl.hpp b/implementation/runtime/include/application_impl.hpp index 06f60eb..5b3fe26 100644 --- a/implementation/runtime/include/application_impl.hpp +++ b/implementation/runtime/include/application_impl.hpp @@ -17,7 +17,7 @@ #include <vector> #include <boost/asio/signal_set.hpp> -#include <boost/asio/system_timer.hpp> +#include <boost/asio/steady_timer.hpp> #include <vsomeip/export.hpp> #include <vsomeip/application.hpp> @@ -55,6 +55,12 @@ public: instance_t _instance, event_t _event, const std::set<eventgroup_t> &_eventgroups, bool _is_field); + VSOMEIP_EXPORT void offer_event(service_t _service, + instance_t _instance, event_t _event, + const std::set<eventgroup_t> &_eventgroups, + bool _is_field, + std::chrono::milliseconds _cycle, bool _change_resets_cycle_, + const epsilon_change_func_t &_epsilon_change_func); VSOMEIP_EXPORT void stop_offer_event(service_t _service, instance_t _instance, event_t _event); @@ -86,9 +92,15 @@ public: VSOMEIP_EXPORT void notify(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload) const; + VSOMEIP_EXPORT void notify(service_t _service, instance_t _instance, + event_t _event, std::shared_ptr<payload> _payload, + bool _force) const; VSOMEIP_EXPORT void notify_one(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload, client_t _client) const; + VSOMEIP_EXPORT void notify_one(service_t _service, instance_t _instance, + event_t _event, std::shared_ptr<payload> _payload, client_t _client, + bool _force) const; VSOMEIP_EXPORT void register_state_handler(state_handler_t _handler); VSOMEIP_EXPORT void unregister_state_handler(); @@ -137,6 +149,10 @@ public: // service_discovery_host VSOMEIP_EXPORT routing_manager * get_routing_manager() const; + VSOMEIP_EXPORT bool are_available(available_t &_available, + service_t _service = ANY_SERVICE, instance_t _instance = ANY_INSTANCE, + major_version_t _major = ANY_MAJOR, minor_version_t _minor = ANY_MINOR) const; + private: // // Types @@ -151,6 +167,17 @@ private: bool is_dispatching_; }; + struct message_handler { + message_handler(message_handler_t _handler) : + handler_(_handler) {} + + bool operator<(const message_handler& _other) const { + return handler_.target<void (*)(const std::shared_ptr<message> &)>() + < _other.handler_.target<void (*)(const std::shared_ptr<message> &)>(); + } + message_handler_t handler_; + }; + // // Methods // @@ -165,10 +192,6 @@ private: bool is_available_unlocked(service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) const; - typedef std::map<service_t, std::map<instance_t, std::map<major_version_t, minor_version_t >>> available_t; - bool are_available(available_t &_available, - service_t _service = ANY_SERVICE, instance_t _instance = ANY_INSTANCE, - major_version_t _major = ANY_MAJOR, minor_version_t _minor = ANY_MINOR) const; bool are_available_unlocked(available_t &_available, service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor) const; @@ -184,7 +207,7 @@ private: void remove_elapsed_dispatchers(); void clear_all_handler(); - void wait_for_stop(); + void shutdown(); void send_back_cached_event(service_t _service, instance_t _instance, event_t _event); void send_back_cached_eventgroup(service_t _service, instance_t _instance, eventgroup_t _eventgroup); @@ -205,6 +228,7 @@ private: std::string folder_; // configuration folder boost::asio::io_service io_; + std::shared_ptr<boost::asio::io_service::work> work_; // Proxy to or the Routing Manager itself std::shared_ptr<routing_manager> routing_; @@ -236,8 +260,11 @@ private: std::map<client_t, error_handler_t > > > > eventgroup_error_handlers_; mutable std::mutex subscription_error_mutex_; +#ifdef VSOMEIP_ENABLE_SIGNAL_HANDLING // Signals boost::asio::signal_set signals_; + bool catched_signal_; +#endif // Handlers mutable std::deque<std::shared_ptr<sync_handler>> handlers_; @@ -255,7 +282,7 @@ private: std::mutex dispatcher_mutex_; // Condition to wakeup the dispatcher thread mutable std::condition_variable dispatcher_condition_; - boost::asio::system_timer dispatcher_timer_; + boost::asio::steady_timer dispatcher_timer_; std::size_t max_dispatchers_; std::size_t max_dispatch_time_; @@ -267,15 +294,24 @@ private: bool stopped_; std::thread stop_thread_; - bool catched_signal_; + std::condition_variable block_stop_cv_; + std::mutex block_stop_mutex_; + bool block_stopping_; static uint32_t app_counter__; + static std::mutex app_counter_mutex__; bool is_routing_manager_host_; // Event subscriptions std::mutex event_subscriptions_mutex_; std::map<service_t, std::map<instance_t, std::map<event_t, bool>>> event_subscriptions_; + + std::thread::id stop_caller_id_; + + bool stopped_called_; + + std::thread::id io_thread_id_; }; } // namespace vsomeip diff --git a/implementation/runtime/src/application_impl.cpp b/implementation/runtime/src/application_impl.cpp index dc7bda5..d8f08d6 100644 --- a/implementation/runtime/src/application_impl.cpp +++ b/implementation/runtime/src/application_impl.cpp @@ -10,6 +10,7 @@ #ifndef WIN32 #include <dlfcn.h> #endif + #include <vsomeip/defines.hpp> #include <vsomeip/runtime.hpp> @@ -28,18 +29,29 @@ namespace vsomeip { uint32_t application_impl::app_counter__ = 0; +std::mutex application_impl::app_counter_mutex__; application_impl::application_impl(const std::string &_name) - : is_initialized_(false), name_(_name), + : client_(ILLEGAL_CLIENT), + session_(1), + is_initialized_(false), name_(_name), file_(VSOMEIP_DEFAULT_CONFIGURATION_FILE), folder_(VSOMEIP_DEFAULT_CONFIGURATION_FOLDER), routing_(0), state_(state_type_e::ST_DEREGISTERED), +#ifdef VSOMEIP_ENABLE_SIGNAL_HANDLING signals_(io_, SIGINT, SIGTERM), + catched_signal_(false), +#endif + is_dispatching_(false), dispatcher_timer_(io_), + max_dispatchers_(VSOMEIP_MAX_DISPATCHERS), + max_dispatch_time_(VSOMEIP_MAX_DISPATCH_TIME), logger_(logger::get()), stopped_(false), - catched_signal_(false) { + block_stopping_(false), + is_routing_manager_host_(false), + stopped_called_(false) { } application_impl::~application_impl() { @@ -144,13 +156,17 @@ bool application_impl::init() { max_dispatch_time_ = its_configuration->get_max_dispatch_time(name_); std::string its_routing_host = its_configuration->get_routing_host(); - if (!utility::auto_configuration_init(name_)) { + if (!utility::auto_configuration_init()) { VSOMEIP_WARNING << "Could _not_ initialize auto-configuration:" " Cannot guarantee unique application identifiers!"; } else { // Client Identifier client_t its_old_client = client_; - client_ = utility::request_client_id(client_); + client_ = utility::request_client_id(name_, client_); + if (client_ == ILLEGAL_CLIENT) { + VSOMEIP_ERROR << "Couldn't acquire client identifier"; + return false; + } VSOMEIP_INFO << "SOME/IP client identifier configured. " << "Using " << std::hex << std::setfill('0') << std::setw(4) @@ -162,14 +178,16 @@ bool application_impl::init() { // Routing if (its_routing_host == "") { - is_routing_manager_host_ = utility::is_routing_manager_host(); + is_routing_manager_host_ = utility::is_routing_manager_host(client_); VSOMEIP_INFO << "No routing manager configured. " << "Using auto-configuration (" << (is_routing_manager_host_ ? "Host" : "Proxy") << ")"; - } else { - is_routing_manager_host_ = (its_routing_host == name_); - } + } + } + + if (its_routing_host != "") { + is_routing_manager_host_ = (its_routing_host == name_); } if (is_routing_manager_host_) { @@ -189,12 +207,12 @@ bool application_impl::init() { std::shared_ptr<cfg::trace> its_trace_cfg = its_configuration->get_trace(); auto &its_channels_cfg = its_trace_cfg->channels_; - for(auto it = its_channels_cfg.begin(); it != its_channels_cfg.end(); ++it) { + for (auto it = its_channels_cfg.begin(); it != its_channels_cfg.end(); ++it) { its_trace_connector->add_channel(it->get()->id_, it->get()->name_); } auto &its_filter_rules_cfg = its_trace_cfg->filter_rules_; - for(auto it = its_filter_rules_cfg.begin(); it != its_filter_rules_cfg.end(); ++it) { + for (auto it = its_filter_rules_cfg.begin(); it != its_filter_rules_cfg.end(); ++it) { std::shared_ptr<cfg::trace_filter_rule> its_filter_rule_cfg = *it; tc::trace_connector::filter_rule_t its_filter_rule; @@ -206,9 +224,12 @@ bool application_impl::init() { } bool enable_tracing = its_trace_cfg->is_enabled_; - if(enable_tracing) + if (enable_tracing) its_trace_connector->init(); its_trace_connector->set_enabled(enable_tracing); + + bool enable_sd_tracing = its_trace_cfg->is_sd_enabled_; + its_trace_connector->set_sd_enabled(enable_sd_tracing); #endif VSOMEIP_DEBUG << "Application(" << (name_ != "" ? name_ : "unnamed") @@ -219,6 +240,7 @@ bool application_impl::init() { is_initialized_ = true; } +#ifdef VSOMEIP_ENABLE_SIGNAL_HANDLING if (is_initialized_) { signals_.add(SIGINT); signals_.add(SIGTERM); @@ -240,7 +262,7 @@ bool application_impl::init() { }; signals_.async_wait(its_signal_handler); } - +#endif return is_initialized_; } @@ -253,9 +275,27 @@ void application_impl::start() { VSOMEIP_ERROR << "Trying to start an already started application."; return; } + if (stopped_) { + utility::release_client_id(client_); + utility::auto_configuration_exit(client_); + + { + std::lock_guard<std::mutex> its_lock_start_stop(block_stop_mutex_); + block_stopping_ = true; + } + block_stop_cv_.notify_all(); + + stopped_ = false; + return; + } + stopped_ = false; + stopped_called_ = false; + VSOMEIP_INFO << "Starting vsomeip application \"" << name_ << "\"."; is_dispatching_ = true; + io_thread_id_ = std::this_thread::get_id(); + auto its_main_dispatcher = std::make_shared<std::thread>( std::bind(&application_impl::main_dispatch, this)); dispatchers_[its_main_dispatcher->get_id()] = its_main_dispatcher; @@ -263,49 +303,76 @@ void application_impl::start() { if (stop_thread_.joinable()) { stop_thread_.join(); } - stop_thread_= std::thread(&application_impl::wait_for_stop, this); + stop_thread_= std::thread(&application_impl::shutdown, this); if (routing_) routing_->start(); } - start_stop_mutex_.lock(); + app_counter_mutex__.lock(); app_counter__++; - start_stop_mutex_.unlock(); + app_counter_mutex__.unlock(); - VSOMEIP_INFO << "Starting vsomeip application \"" << name_ << "\"."; + work_ = std::make_shared<boost::asio::io_service::work>(io_); io_.run(); - { - std::lock_guard<std::mutex> its_lock(start_stop_mutex_); - stopped_ = true; - stop_cv_.notify_one(); - } if (stop_thread_.joinable()) { stop_thread_.join(); } - start_stop_mutex_.lock(); + utility::release_client_id(client_); + utility::auto_configuration_exit(client_); + + { + std::lock_guard<std::mutex> its_lock_start_stop(block_stop_mutex_); + block_stopping_ = true; + } + block_stop_cv_.notify_all(); + + { + std::lock_guard<std::mutex> its_lock(start_stop_mutex_); + stopped_ = false; + } + + app_counter_mutex__.lock(); app_counter__--; +#ifdef VSOMEIP_ENABLE_SIGNAL_HANDLING if (catched_signal_ && !app_counter__) { - start_stop_mutex_.unlock(); + app_counter_mutex__.unlock(); VSOMEIP_INFO << "Exiting vsomeip application..."; exit(0); } - start_stop_mutex_.unlock(); +#endif + app_counter_mutex__.unlock(); } void application_impl::stop() { #ifndef WIN32 // Gives serious problems under Windows. VSOMEIP_INFO << "Stopping vsomeip application \"" << name_ << "\"."; #endif - utility::release_client_id(client_); - utility::auto_configuration_exit(); - - std::lock_guard<std::mutex> its_lock_start_stop(start_stop_mutex_); - stopped_ = true; + bool block = true; + { + std::lock_guard<std::mutex> its_lock_start_stop(start_stop_mutex_); + if (stopped_ || stopped_called_) { + return; + } + stop_caller_id_ = std::this_thread::get_id(); + stopped_ = true; + stopped_called_ = true; + if (io_thread_id_ == std::this_thread::get_id()) { + block = false; + } + } stop_cv_.notify_one(); + + if (block) { + std::unique_lock<std::mutex> block_stop_lock(block_stop_mutex_); + while (!block_stopping_) { + block_stop_cv_.wait(block_stop_lock); + } + block_stopping_ = false; + } } void application_impl::offer_service(service_t _service, instance_t _instance, @@ -573,16 +640,27 @@ void application_impl::send(std::shared_ptr<message> _message, bool _flush) { void application_impl::notify(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload) const { + return notify(_service, _instance, _event, _payload, false); +} + +void application_impl::notify(service_t _service, instance_t _instance, + event_t _event, std::shared_ptr<payload> _payload, bool _force) const { if (routing_) - routing_->notify(_service, _instance, _event, _payload); + routing_->notify(_service, _instance, _event, _payload, _force); } void application_impl::notify_one(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload, client_t _client) const { + return notify_one(_service, _instance, _event, _payload, _client, false); +} +void application_impl::notify_one(service_t _service, instance_t _instance, + event_t _event, std::shared_ptr<payload> _payload, + client_t _client, bool _force) const { if (routing_) { - routing_->notify_one(_service, _instance, _event, _payload, _client); + routing_->notify_one(_service, _instance, _event, _payload, _client, + _force); } } @@ -597,6 +675,7 @@ void application_impl::unregister_state_handler() { void application_impl::register_availability_handler(service_t _service, instance_t _instance, availability_handler_t _handler, major_version_t _major, minor_version_t _minor) { + std::lock_guard<std::mutex> availability_lock(availability_mutex_); if (state_ == state_type_e::ST_REGISTERED) { do_register_availability_handler(_service, _instance, _handler, _major, _minor); @@ -609,23 +688,19 @@ void application_impl::register_availability_handler(service_t _service, void application_impl::do_register_availability_handler(service_t _service, instance_t _instance, availability_handler_t _handler, major_version_t _major, minor_version_t _minor) { - - { - std::unique_lock<std::mutex> availability_lock(availability_mutex_); available_t available; - bool are_available = are_available_unlocked(available, _service, _instance, _major, _minor); - availability_[_service][_instance] = std::make_tuple(_major, _minor, _handler, true); + bool are_available = are_available_unlocked(available, _service, _instance, _major, _minor); + availability_[_service][_instance] = std::make_tuple(_major, _minor, _handler, true); - std::lock_guard<std::mutex> handlers_lock(handlers_mutex_); + std::lock_guard<std::mutex> handlers_lock(handlers_mutex_); - std::shared_ptr<sync_handler> its_sync_handler - = std::make_shared<sync_handler>([_handler, are_available, available]() { - for(auto available_services_it : available) - for(auto available_instances_it : available_services_it.second) - _handler(available_services_it.first, available_instances_it.first, are_available); - }); - handlers_.push_back(its_sync_handler); - } + std::shared_ptr<sync_handler> its_sync_handler + = std::make_shared<sync_handler>([_handler, are_available, available]() { + for(auto available_services_it : available) + for(auto available_instances_it : available_services_it.second) + _handler(available_services_it.first, available_instances_it.first, are_available); + }); + handlers_.push_back(its_sync_handler); dispatcher_condition_.notify_one(); } @@ -770,10 +845,21 @@ void application_impl::unregister_message_handler(service_t _service, } void application_impl::offer_event(service_t _service, instance_t _instance, - event_t _event, const std::set<eventgroup_t> &_eventgroups, bool _is_field) { + event_t _event, const std::set<eventgroup_t> &_eventgroups, + bool _is_field) { + return offer_event(_service, _instance, _event, _eventgroups, _is_field, + std::chrono::milliseconds::zero(), false, nullptr); +} + +void application_impl::offer_event(service_t _service, instance_t _instance, + event_t _event, const std::set<eventgroup_t> &_eventgroups, + bool _is_field, + std::chrono::milliseconds _cycle, bool _change_resets_cycle, + const epsilon_change_func_t &_epsilon_change_func) { if (routing_) routing_->register_event(client_, _service, _instance, _event, - _eventgroups, _is_field, true); + _eventgroups, _is_field, _cycle, _change_resets_cycle, + _epsilon_change_func, true); } void application_impl::stop_offer_event(service_t _service, instance_t _instance, @@ -783,10 +869,14 @@ void application_impl::stop_offer_event(service_t _service, instance_t _instance } void application_impl::request_event(service_t _service, instance_t _instance, - event_t _event, const std::set<eventgroup_t> &_eventgroups, bool _is_field) { + event_t _event, const std::set<eventgroup_t> &_eventgroups, + bool _is_field) { if (routing_) routing_->register_event(client_, _service, _instance, _event, - _eventgroups, _is_field, false); + _eventgroups, _is_field, + std::chrono::milliseconds::zero(), false, + nullptr, + false); } void application_impl::release_event(service_t _service, instance_t _instance, @@ -829,6 +919,7 @@ void application_impl::on_state(state_type_e _state) { if (state_ != _state) { state_ = _state; if (state_ == state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> availability_lock(availability_mutex_); for (auto &its_service : availability_) { for (auto &its_instance : its_service.second) { if (!std::get<3>(its_instance.second)) { @@ -847,9 +938,10 @@ void application_impl::on_state(state_type_e _state) { { std::lock_guard<std::mutex> its_lock(handlers_mutex_); + state_handler_t handler = handler_; std::shared_ptr<sync_handler> its_sync_handler - = std::make_shared<sync_handler>([this, _state]() { - handler_(_state); + = std::make_shared<sync_handler>([handler, _state]() { + handler(_state); }); handlers_.push_back(its_sync_handler); @@ -897,16 +989,9 @@ void application_impl::on_availability(service_t _service, instance_t _instance, if( found_instance != found_service->second.end()) { auto found_version = found_instance->second; auto requested_major = std::get<0>(found_version); - auto requested_minor = std::get<1>(found_version); if(requested_major == _major) { - if(requested_minor <= _minor ) { - has_handler = true; - its_handler = std::get<2>(found_version); - } - } else if (requested_major == DEFAULT_MAJOR && - requested_minor == DEFAULT_MINOR) { - has_handler = true; - its_handler = std::get<2>(found_version); + has_handler = true; + its_handler = std::get<2>(found_version); } } @@ -915,16 +1000,9 @@ void application_impl::on_availability(service_t _service, instance_t _instance, if( found_instance != found_service->second.end()) { auto found_version = found_instance->second; auto requested_major = std::get<0>(found_version); - auto requested_minor = std::get<1>(found_version); if(requested_major == ANY_MAJOR) { - if(requested_minor == ANY_MINOR) { - has_wildcard_handler = true; - its_wildcard_handler = std::get<2>(found_version); - } - } else if (requested_major == DEFAULT_MAJOR && - requested_minor == DEFAULT_MINOR) { - has_handler = true; - its_handler = std::get<2>(found_version); + has_wildcard_handler = true; + its_wildcard_handler = std::get<2>(found_version); } } } @@ -976,10 +1054,6 @@ void application_impl::on_message(std::shared_ptr<message> _message) { instance_t its_instance = _message->get_instance(); method_t its_method = _message->get_method(); - std::map<method_t, message_handler_t>::iterator found_method; - message_handler_t its_handler; - bool has_handler(false); - if (_message->get_message_type() == message_type_e::MT_NOTIFICATION) { std::lock_guard<std::mutex> its_lock(event_subscriptions_mutex_); auto found_service = event_subscriptions_.find(its_service); @@ -1010,39 +1084,68 @@ void application_impl::on_message(std::shared_ptr<message> _message) { { std::lock_guard<std::mutex> its_lock(members_mutex_); - + std::set<message_handler> its_handlers; auto found_service = members_.find(its_service); - if (found_service == members_.end()) { - found_service = members_.find(ANY_SERVICE); - } if (found_service != members_.end()) { auto found_instance = found_service->second.find(its_instance); - if (found_instance == found_service->second.end()) { - found_instance = found_service->second.find(ANY_INSTANCE); - } if (found_instance != found_service->second.end()) { auto found_method = found_instance->second.find(its_method); - if (found_method == found_instance->second.end()) { - found_method = found_instance->second.find(ANY_METHOD); + if (found_method != found_instance->second.end()) { + its_handlers.insert(found_method->second); } - + auto found_any_method = found_instance->second.find(ANY_METHOD); + if (found_any_method != found_instance->second.end()) { + its_handlers.insert(found_any_method->second); + } + } + auto found_any_instance = found_service->second.find(ANY_INSTANCE); + if (found_any_instance != found_service->second.end()) { + auto found_method = found_any_instance->second.find(its_method); + if (found_method != found_any_instance->second.end()) { + its_handlers.insert(found_method->second); + } + auto found_any_method = found_any_instance->second.find(ANY_METHOD); + if (found_any_method != found_any_instance->second.end()) { + its_handlers.insert(found_any_method->second); + } + } + } + auto found_any_service = members_.find(ANY_SERVICE); + if (found_any_service != members_.end()) { + auto found_instance = found_any_service->second.find(its_instance); + if (found_instance != found_any_service->second.end()) { + auto found_method = found_instance->second.find(its_method); if (found_method != found_instance->second.end()) { - its_handler = found_method->second; - has_handler = true; + its_handlers.insert(found_method->second); + } + auto found_any_method = found_instance->second.find(ANY_METHOD); + if (found_any_method != found_instance->second.end()) { + its_handlers.insert(found_any_method->second); + } + } + auto found_any_instance = found_any_service->second.find(ANY_INSTANCE); + if (found_any_instance != found_any_service->second.end()) { + auto found_method = found_any_instance->second.find(its_method); + if (found_method != found_any_instance->second.end()) { + its_handlers.insert(found_method->second); + } + auto found_any_method = found_any_instance->second.find(ANY_METHOD); + if (found_any_method != found_any_instance->second.end()) { + its_handlers.insert(found_any_method->second); } } } - if (has_handler) { + if (its_handlers.size()) { std::lock_guard<std::mutex> its_lock(handlers_mutex_); - std::shared_ptr<sync_handler> its_sync_handler - = std::make_shared<sync_handler>( - [its_handler, _message]() { - its_handler(_message); - } - ); - - handlers_.push_back(its_sync_handler); + for (const auto &its_handler : its_handlers) { + auto handler = its_handler.handler_; + std::shared_ptr<sync_handler> its_sync_handler = + std::make_shared<sync_handler>([handler, _message]() { + handler(_message); + }); + handlers_.push_back(its_sync_handler); + } } dispatcher_condition_.notify_one(); } @@ -1071,7 +1174,9 @@ void application_impl::main_dispatch() { // Cancel other waiting dispatcher dispatcher_condition_.notify_all(); // Wait for new handlers to execute - dispatcher_condition_.wait(its_lock); + while (handlers_.empty() && is_dispatching_) { + dispatcher_condition_.wait(its_lock); + } } else { while (!handlers_.empty()) { std::shared_ptr<sync_handler> its_handler = handlers_.front(); @@ -1132,7 +1237,7 @@ void application_impl::invoke_handler(std::shared_ptr<sync_handler> &_handler) { std::bind(&application_impl::dispatch, this)); dispatchers_[its_dispatcher->get_id()] = its_dispatcher; } else { - VSOMEIP_DEBUG << "Maximum number of dispatchers exceeded."; + VSOMEIP_ERROR << "Maximum number of dispatchers exceeded."; } } }); @@ -1189,39 +1294,50 @@ void application_impl::clear_all_handler() { std::lock_guard<std::mutex> its_lock(members_mutex_); members_.clear(); } + { + std::unique_lock<std::mutex> its_lock(handlers_mutex_); + handlers_.clear(); + } } -void application_impl::wait_for_stop() { - +void application_impl::shutdown() { { std::unique_lock<std::mutex> its_lock(start_stop_mutex_); while(!stopped_) { stop_cv_.wait(its_lock); } - stopped_ = false; - - // join dispatch threads + } + { + std::unique_lock<std::mutex> its_lock(handlers_mutex_); is_dispatching_ = false; - dispatcher_condition_.notify_all(); } - + dispatcher_condition_.notify_all(); for (auto its_dispatcher : dispatchers_) { - if (its_dispatcher.second->joinable()) - its_dispatcher.second->join(); + if (its_dispatcher.second->get_id() != stop_caller_id_) { + if (its_dispatcher.second->joinable()) { + its_dispatcher.second->join(); + } + } else { + // If the caller of stop() is one of our dispatchers + // it can happen the shutdown mechanism will block + // as that thread probably can't be joined. The reason + // is the caller of stop() probably wants to join the + // thread once call start (which got to the IO-Thread) + // and which is expected to return after stop() has been + // called. + // Therefore detach this thread instead of joining because + // after it will return to "main_dispatch" it will be + // properly shutdown anyways because "is_dispatching_" + // was set to "false" here. + its_dispatcher.second->detach(); + } } if (routing_) routing_->stop(); + work_.reset(); io_.stop(); - - { - std::unique_lock<std::mutex> its_lock(start_stop_mutex_); - while(!stopped_) { - stop_cv_.wait(its_lock); - } - stopped_ = false; - } } bool application_impl::is_routing() const { diff --git a/implementation/service_discovery/include/defines.hpp b/implementation/service_discovery/include/defines.hpp index 52c7216..a0129b6 100644 --- a/implementation/service_discovery/include/defines.hpp +++ b/implementation/service_discovery/include/defines.hpp @@ -12,11 +12,15 @@ #define VSOMEIP_MAX_UDP_SD_PAYLOAD 1380 #define VSOMEIP_SOMEIP_SD_DATA_SIZE 12 +#define VSOMEIP_SOMEIP_SD_ENTRY_LENGTH_SIZE 4 #define VSOMEIP_SOMEIP_SD_ENTRY_SIZE 16 #define VSOMEIP_SOMEIP_SD_IPV4_OPTION_SIZE 12 #define VSOMEIP_SOMEIP_SD_IPV6_OPTION_SIZE 24 +#define VSOMEIP_SOMEIP_SD_OPTION_LENGTH_SIZE 4 #define VSOMEIP_SOMEIP_SD_OPTION_HEADER_SIZE 3 + + #define VSOMEIP_SD_IPV4_OPTION_LENGTH 0x0009 #define VSOMEIP_SD_IPV6_OPTION_LENGTH 0x0015 diff --git a/implementation/service_discovery/include/fsm_base.hpp b/implementation/service_discovery/include/fsm_base.hpp index ede92d3..b04f9a6 100644 --- a/implementation/service_discovery/include/fsm_base.hpp +++ b/implementation/service_discovery/include/fsm_base.hpp @@ -9,7 +9,7 @@ #include <memory> #include <boost/asio/io_service.hpp> -#include <boost/asio/system_timer.hpp> +#include <boost/asio/steady_timer.hpp> namespace vsomeip { namespace sd { @@ -28,8 +28,8 @@ public: bool _use_alt_timer) = 0; private: - boost::asio::system_timer timer_; - boost::asio::system_timer alt_timer_; + boost::asio::steady_timer timer_; + boost::asio::steady_timer alt_timer_; }; } // namespace sd diff --git a/implementation/service_discovery/include/ip_option_impl.hpp b/implementation/service_discovery/include/ip_option_impl.hpp index e753b4e..e1344db 100644 --- a/implementation/service_discovery/include/ip_option_impl.hpp +++ b/implementation/service_discovery/include/ip_option_impl.hpp @@ -25,7 +25,7 @@ public: layer_four_protocol_e get_layer_four_protocol() const; void set_layer_four_protocol(layer_four_protocol_e _protocol); - bool is_multicast() const; + virtual bool is_multicast() const = 0; virtual bool serialize(vsomeip::serializer *_to) const = 0; virtual bool deserialize(vsomeip::deserializer *_from) = 0; diff --git a/implementation/service_discovery/include/ipv4_option_impl.hpp b/implementation/service_discovery/include/ipv4_option_impl.hpp index c233f0f..fd80be9 100644 --- a/implementation/service_discovery/include/ipv4_option_impl.hpp +++ b/implementation/service_discovery/include/ipv4_option_impl.hpp @@ -21,6 +21,7 @@ public: const ipv4_address_t & get_address() const;
void set_address(const ipv4_address_t &_address);
+ bool is_multicast() const;
bool serialize(vsomeip::serializer *_to) const;
bool deserialize(vsomeip::deserializer *_from);
diff --git a/implementation/service_discovery/include/ipv6_option_impl.hpp b/implementation/service_discovery/include/ipv6_option_impl.hpp index c2b962f..c6fd270 100644 --- a/implementation/service_discovery/include/ipv6_option_impl.hpp +++ b/implementation/service_discovery/include/ipv6_option_impl.hpp @@ -21,6 +21,7 @@ public: const ipv6_address_t & get_address() const;
void set_address(const ipv6_address_t &_address);
+ bool is_multicast() const;
bool serialize(vsomeip::serializer *_to) const;
bool deserialize(vsomeip::deserializer *_from);
diff --git a/implementation/service_discovery/include/message_impl.hpp b/implementation/service_discovery/include/message_impl.hpp index a8573b8..add6fb4 100755 --- a/implementation/service_discovery/include/message_impl.hpp +++ b/implementation/service_discovery/include/message_impl.hpp @@ -73,6 +73,8 @@ public: bool serialize(vsomeip::serializer *_to) const;
bool deserialize(vsomeip::deserializer *_from);
+ length_t get_someip_length() const;
+
private:
entry_impl * deserialize_entry(vsomeip::deserializer *_from);
option_impl * deserialize_option(vsomeip::deserializer *_from);
diff --git a/implementation/service_discovery/include/service_discovery_host.hpp b/implementation/service_discovery/include/service_discovery_host.hpp index 1795d89..3f7a594 100644 --- a/implementation/service_discovery/include/service_discovery_host.hpp +++ b/implementation/service_discovery/include/service_discovery_host.hpp @@ -43,7 +43,7 @@ public: virtual bool send_to(const std::shared_ptr<endpoint_definition> &_target, const byte_t *_data, uint32_t _size, uint16_t _sd_port) = 0; - virtual void add_routing_info(service_t _service, instance_t _instance, + virtual std::chrono::milliseconds add_routing_info(service_t _service, instance_t _instance, major_version_t _major, minor_version_t _minor, ttl_t _ttl, const boost::asio::ip::address &_reliable_address, uint16_t _reliable_port, @@ -60,7 +60,7 @@ public: eventgroup_t _eventgroup, std::shared_ptr<endpoint_definition> _subscriber, std::shared_ptr<endpoint_definition> _target, - const std::chrono::high_resolution_clock::time_point &_expiration) = 0; + const std::chrono::steady_clock::time_point &_expiration) = 0; virtual void on_unsubscribe(service_t _service, instance_t _instance, eventgroup_t _eventgroup, @@ -81,7 +81,7 @@ public: virtual bool on_subscribe_accepted(service_t _service, instance_t _instance, eventgroup_t _eventgroup, std::shared_ptr<endpoint_definition> _target, - const std::chrono::high_resolution_clock::time_point &_expiration) = 0; + const std::chrono::steady_clock::time_point &_expiration) = 0; virtual void on_subscribe_nack(client_t _client, service_t _service, instance_t _instance, eventgroup_t _eventgroup) = 0; @@ -89,7 +89,7 @@ public: virtual bool has_identified(client_t _client, service_t _service, instance_t _instance, bool _reliable) = 0; - virtual std::chrono::high_resolution_clock::time_point expire_subscriptions() = 0; + virtual std::chrono::steady_clock::time_point expire_subscriptions() = 0; }; } // namespace sd diff --git a/implementation/service_discovery/include/service_discovery_impl.hpp b/implementation/service_discovery/include/service_discovery_impl.hpp index 4e64328..910037d 100644 --- a/implementation/service_discovery/include/service_discovery_impl.hpp +++ b/implementation/service_discovery/include/service_discovery_impl.hpp @@ -11,7 +11,7 @@ #include <mutex> #include <set> -#include <boost/asio/system_timer.hpp> +#include <boost/asio/steady_timer.hpp> #include "service_discovery.hpp" #include "../../endpoints/include/endpoint_definition.hpp" @@ -41,7 +41,7 @@ typedef std::map<service_t, std::map<instance_t, std::shared_ptr<request> > > re struct accepted_subscriber_t { std::shared_ptr < endpoint_definition > subscriber; std::shared_ptr < endpoint_definition > target; - std::chrono::high_resolution_clock::time_point its_expiration; + std::chrono::steady_clock::time_point its_expiration; vsomeip::service_t service_id; vsomeip::instance_t instance_id; vsomeip::eventgroup_t eventgroup_; @@ -189,12 +189,6 @@ private: bool check_static_header_fields( const std::shared_ptr<const message> &_message) const; - void send_eventgroup_subscription_nack(service_t _service, - instance_t _instance, - eventgroup_t _eventgroup, - major_version_t _major, - uint8_t _counter, - uint16_t _reserved); bool check_layer_four_protocol( const std::shared_ptr<const ip_option_impl> _ip_option) const; void get_subscription_endpoints(subscription_type_e _subscription_type, @@ -262,13 +256,13 @@ private: std::weak_ptr<runtime> runtime_; // TTL handling for services offered by other hosts - boost::asio::system_timer ttl_timer_; + boost::asio::steady_timer ttl_timer_; std::chrono::milliseconds smallest_ttl_; ttl_t ttl_; // TTL handling for subscriptions done by other hosts - boost::asio::system_timer subscription_expiration_timer_; - std::chrono::high_resolution_clock::time_point next_subscription_expiration_; + boost::asio::steady_timer subscription_expiration_timer_; + std::chrono::steady_clock::time_point next_subscription_expiration_; uint32_t max_message_size_; }; diff --git a/implementation/service_discovery/include/subscription.hpp b/implementation/service_discovery/include/subscription.hpp index eb2fb66..3cdcd34 100644 --- a/implementation/service_discovery/include/subscription.hpp +++ b/implementation/service_discovery/include/subscription.hpp @@ -25,7 +25,7 @@ public: std::shared_ptr<endpoint> _unreliable, subscription_type_e _subscription_type, uint8_t _counter, - std::chrono::high_resolution_clock::time_point _expiration); + std::chrono::steady_clock::time_point _expiration); ~subscription(); major_version_t get_major() const; @@ -44,8 +44,8 @@ public: uint8_t get_counter() const; - std::chrono::high_resolution_clock::time_point get_expiration() const; - void set_expiration(std::chrono::high_resolution_clock::time_point _expiration); + std::chrono::steady_clock::time_point get_expiration() const; + void set_expiration(std::chrono::steady_clock::time_point _expiration); private: major_version_t major_; @@ -61,7 +61,7 @@ private: uint8_t counter_; - std::chrono::high_resolution_clock::time_point expiration_; + std::chrono::steady_clock::time_point expiration_; }; } // namespace sd diff --git a/implementation/service_discovery/src/entry_impl.cpp b/implementation/service_discovery/src/entry_impl.cpp index efff9da..0637383 100755 --- a/implementation/service_discovery/src/entry_impl.cpp +++ b/implementation/service_discovery/src/entry_impl.cpp @@ -9,6 +9,7 @@ #include "../include/message_impl.hpp"
#include "../../message/include/deserializer.hpp"
#include "../../message/include/serializer.hpp"
+#include "../../logging/include/logger.hpp"
namespace vsomeip {
namespace sd {
@@ -87,25 +88,20 @@ const std::vector<uint8_t> & entry_impl::get_options(uint8_t _run) const { void entry_impl::assign_option(const std::shared_ptr<option_impl> &_option) {
uint8_t option_index = uint8_t(get_owning_message()->get_option_index(_option));
- if (0x10 > option_index) { // as we have only a nibble for the option counter
- if (options_[0].empty() ||
+ if (options_[0].empty() ||
options_[0][0] == option_index + 1 ||
options_[0][options_[0].size() - 1] + 1 == option_index) {
- options_[0].push_back(option_index);
- std::sort(options_[0].begin(), options_[0].end());
- num_options_[0]++;
- } else
- if (options_[1].empty() ||
+ options_[0].push_back(option_index);
+ std::sort(options_[0].begin(), options_[0].end());
+ num_options_[0]++;
+ } else if (options_[1].empty() ||
options_[1][0] == option_index + 1 ||
options_[1][options_[1].size() - 1] + 1 == option_index) {
- options_[1].push_back(option_index);
- std::sort(options_[1].begin(), options_[1].end());
- num_options_[1]++;
- } else {
- // TODO: copy data
- }
+ options_[1].push_back(option_index);
+ std::sort(options_[1].begin(), options_[1].end());
+ num_options_[1]++;
} else {
- // TODO: decide what to do if option does not belong to the message.
+ VSOMEIP_WARNING << "Option is not referenced by entries array, maximum number of endpoint options reached!";
}
}
@@ -143,25 +139,25 @@ bool entry_impl::deserialize(vsomeip::deserializer *_from) { is_successful = is_successful && _from->deserialize(its_type);
type_ = static_cast<entry_type_e>(its_type);
- uint8_t its_index1;
+ uint8_t its_index1(0);
is_successful = is_successful && _from->deserialize(its_index1);
- uint8_t its_index2;
+ uint8_t its_index2(0);
is_successful = is_successful && _from->deserialize(its_index2);
- uint8_t its_numbers;
+ uint8_t its_numbers(0);
is_successful = is_successful && _from->deserialize(its_numbers);
num_options_[0] = uint8_t(its_numbers >> 4);
num_options_[1] = uint8_t(its_numbers & 0xF);
- for (uint8_t i = its_index1; i < its_index1 + num_options_[0]; ++i)
- options_[0].push_back(i);
+ for (uint16_t i = its_index1; i < its_index1 + num_options_[0]; ++i)
+ options_[0].push_back((uint8_t)(i));
- for (uint8_t i = its_index2; i < its_index2 + num_options_[1]; ++i)
- options_[1].push_back(i);
+ for (uint16_t i = its_index2; i < its_index2 + num_options_[1]; ++i)
+ options_[1].push_back((uint8_t)(i));
- uint16_t its_id;
+ uint16_t its_id(0);
is_successful = is_successful && _from->deserialize(its_id);
service_ = static_cast<service_t>(its_id);
diff --git a/implementation/service_discovery/src/eventgroupentry_impl.cpp b/implementation/service_discovery/src/eventgroupentry_impl.cpp index 220fc47..56ad9b0 100755 --- a/implementation/service_discovery/src/eventgroupentry_impl.cpp +++ b/implementation/service_discovery/src/eventgroupentry_impl.cpp @@ -11,13 +11,15 @@ namespace vsomeip {
namespace sd {
-eventgroupentry_impl::eventgroupentry_impl() {
+eventgroupentry_impl::eventgroupentry_impl() :
+ reserved_(0) {
eventgroup_ = 0xFFFF;
counter_ = 0;
}
eventgroupentry_impl::eventgroupentry_impl(const eventgroupentry_impl &_entry)
- : entry_impl(_entry) {
+ : entry_impl(_entry),
+ reserved_(0) {
eventgroup_ = _entry.eventgroup_;
counter_ = _entry.counter_;
}
@@ -84,15 +86,15 @@ bool eventgroupentry_impl::serialize(vsomeip::serializer *_to) const { bool eventgroupentry_impl::deserialize(vsomeip::deserializer *_from) {
bool is_successful = entry_impl::deserialize(_from);
- uint8_t tmp_major_version;
+ uint8_t tmp_major_version(0);
is_successful = is_successful && _from->deserialize(tmp_major_version);
major_version_ = static_cast<major_version_t>(tmp_major_version);
- uint32_t its_ttl;
+ uint32_t its_ttl(0);
is_successful = is_successful && _from->deserialize(its_ttl, true);
ttl_ = static_cast<ttl_t>(its_ttl);
- uint8_t reserved1, reserved2;
+ uint8_t reserved1(0), reserved2(0);
is_successful = is_successful && _from->deserialize(reserved1); // deserialize reserved part 1
is_successful = is_successful && _from->deserialize(reserved2); // deserialize reserved part 2 and counter
diff --git a/implementation/service_discovery/src/ip_option_impl.cpp b/implementation/service_discovery/src/ip_option_impl.cpp index 08f78b9..2dc5799 100644 --- a/implementation/service_discovery/src/ip_option_impl.cpp +++ b/implementation/service_discovery/src/ip_option_impl.cpp @@ -49,11 +49,6 @@ void ip_option_impl::set_layer_four_protocol( protocol_ = _protocol; } -bool ip_option_impl::is_multicast() const { - return (type_ == option_type_e::IP4_MULTICAST - || type_ == option_type_e::IP6_MULTICAST); -} - } // namespace sd } // namespace vsomeip diff --git a/implementation/service_discovery/src/ipv4_option_impl.cpp b/implementation/service_discovery/src/ipv4_option_impl.cpp index 9f2cec4..2551bb0 100644 --- a/implementation/service_discovery/src/ipv4_option_impl.cpp +++ b/implementation/service_discovery/src/ipv4_option_impl.cpp @@ -33,6 +33,10 @@ void ipv4_option_impl::set_address(const ipv4_address_t &_address) { address_ = _address;
}
+bool ipv4_option_impl::is_multicast() const {
+ return (type_ == option_type_e::IP4_MULTICAST);
+}
+
bool ipv4_option_impl::serialize(vsomeip::serializer *_to) const {
bool is_successful = option_impl::serialize(_to);
_to->serialize(&address_[0], uint32_t(address_.size()));
diff --git a/implementation/service_discovery/src/ipv6_option_impl.cpp b/implementation/service_discovery/src/ipv6_option_impl.cpp index 089b509..28a2f02 100755 --- a/implementation/service_discovery/src/ipv6_option_impl.cpp +++ b/implementation/service_discovery/src/ipv6_option_impl.cpp @@ -33,6 +33,10 @@ void ipv6_option_impl::set_address(const ipv6_address_t &_address) { address_ = _address;
}
+bool ipv6_option_impl::is_multicast() const {
+ return (type_ == option_type_e::IP6_MULTICAST);
+}
+
bool ipv6_option_impl::serialize(vsomeip::serializer *_to) const {
bool is_successful = option_impl::serialize(_to);
_to->serialize(&address_[0], uint32_t(address_.size()));
diff --git a/implementation/service_discovery/src/message_impl.cpp b/implementation/service_discovery/src/message_impl.cpp index 8159bd4..dc9df91 100755 --- a/implementation/service_discovery/src/message_impl.cpp +++ b/implementation/service_discovery/src/message_impl.cpp @@ -38,12 +38,18 @@ message_impl::~message_impl() { }
length_t message_impl::get_length() const {
- length_t current_length = (VSOMEIP_SOMEIP_HEADER_SIZE
- + VSOMEIP_SOMEIP_SD_DATA_SIZE);
- current_length += uint32_t(entries_.size() * VSOMEIP_SOMEIP_SD_ENTRY_SIZE);
- for (size_t i = 0; i < options_.size(); ++i) {
- current_length += (options_[i]->get_length()
- + VSOMEIP_SOMEIP_SD_OPTION_HEADER_SIZE);
+ length_t current_length = VSOMEIP_SOMEIP_SD_DATA_SIZE;
+ if( entries_.size()) {
+ current_length += VSOMEIP_SOMEIP_SD_ENTRY_LENGTH_SIZE;
+ current_length += uint32_t(entries_.size() * VSOMEIP_SOMEIP_SD_ENTRY_SIZE);
+ }
+
+ current_length += VSOMEIP_SOMEIP_SD_OPTION_LENGTH_SIZE;
+ if(options_.size()) {
+ for (size_t i = 0; i < options_.size(); ++i) {
+ current_length += (options_[i]->get_length()
+ + VSOMEIP_SOMEIP_SD_OPTION_HEADER_SIZE);
+ }
}
return current_length;
}
@@ -356,5 +362,9 @@ option_impl * message_impl::deserialize_option(vsomeip::deserializer *_from) { return deserialized_option;
}
+length_t message_impl::get_someip_length() const {
+ return header_.length_;
+}
+
} // namespace sd
} // namespace vsomeip
diff --git a/implementation/service_discovery/src/option_impl.cpp b/implementation/service_discovery/src/option_impl.cpp index 71aa085..8d2a11d 100755 --- a/implementation/service_discovery/src/option_impl.cpp +++ b/implementation/service_discovery/src/option_impl.cpp @@ -11,9 +11,9 @@ namespace vsomeip {
namespace sd {
-option_impl::option_impl() {
- length_ = 0;
- type_ = option_type_e::UNKNOWN;
+option_impl::option_impl() :
+ length_(0),
+ type_(option_type_e::UNKNOWN) {
}
option_impl::~option_impl() {
diff --git a/implementation/service_discovery/src/runtime.cpp b/implementation/service_discovery/src/runtime.cpp index be68d24..3d12892 100644 --- a/implementation/service_discovery/src/runtime.cpp +++ b/implementation/service_discovery/src/runtime.cpp @@ -12,7 +12,7 @@ extern "C" __declspec(dllexport) std::shared_ptr<vsomeip::sd::runtime> VSOMEIP_SD_RUNTIME_SYMBOL; } #else -std::shared_ptr<vsomeip::sd::runtime> VSOMEIP_SD_RUNTIME_SYMBOL; +std::shared_ptr<vsomeip::sd::runtime> VSOMEIP_SD_RUNTIME_SYMBOL(vsomeip::sd::runtime::get()); #endif #ifdef WIN32 @@ -22,16 +22,11 @@ std::shared_ptr<vsomeip::sd::runtime> VSOMEIP_SD_RUNTIME_SYMBOL; static void __cdecl f(void); \ __declspec(allocate(".CRT$XCU")) void(__cdecl*f##_)(void) = f; \ static void __cdecl f(void) -#else -#define CCALL -#define INITIALIZER(f) \ - static void f(void) __attribute__((constructor)); \ - static void f(void) -#endif INITIALIZER(init_vsomeip_sd) { VSOMEIP_SD_RUNTIME_SYMBOL = vsomeip::sd::runtime::get(); } +#endif namespace vsomeip { namespace sd { diff --git a/implementation/service_discovery/src/service_discovery_fsm.cpp b/implementation/service_discovery/src/service_discovery_fsm.cpp index a4ede7b..ba1bd18 100644 --- a/implementation/service_discovery/src/service_discovery_fsm.cpp +++ b/implementation/service_discovery/src/service_discovery_fsm.cpp @@ -122,6 +122,12 @@ initial::initial(my_context _context) sc::result initial::react(const ev_timeout &_event) { (void)_event; + VSOMEIP_TRACE << "sd::active.initial.react"; + std::shared_ptr < service_discovery_fsm > fsm = + outermost_context().fsm_.lock(); + if (fsm) { + (void)fsm->send(false, false); + } return transit<repeat>(); } @@ -151,11 +157,11 @@ sc::result repeat::react(const ev_timeout &_event) { } sc::result repeat::react(const ev_find_service &_event) { - VSOMEIP_TRACE << "sd::active.repeat.react.find"; std::shared_ptr < service_discovery_fsm > fsm = outermost_context().fsm_.lock(); // Answer Find Service messages with unicast offer in repetition phase if (fsm) { + VSOMEIP_TRACE << "sd::active.repeat.react.find"; fsm->send_unicast_offer_service(_event.info_, _event.service_, _event.instance_, _event.major_, _event.minor_); } @@ -195,7 +201,7 @@ sc::result offer::react(const ev_find_service &_event) { std::shared_ptr < service_discovery_fsm > fsm = outermost_context().fsm_.lock(); if (fsm) { - if(_event.unicast_flag_) { + if(_event.unicast_flag_) { //SIP_SD_826 if( !fsm->check_is_multicast_offer()) { // SIP_SD_89 fsm->send_unicast_offer_service(_event.info_, _event.service_, _event.instance_, _event.major_, _event.minor_); @@ -203,8 +209,8 @@ sc::result offer::react(const ev_find_service &_event) { fsm->send_multicast_offer_service(_event.info_, _event.service_, _event.instance_, _event.major_, _event.minor_); } - } else { // SIP_SD_91 - fsm->send_multicast_offer_service(_event.info_, _event.service_, _event.instance_, + } else { // SIP_SD_824 + fsm->send_unicast_offer_service(_event.info_, _event.service_, _event.instance_, _event.major_, _event.minor_); } } @@ -247,15 +253,16 @@ send::send(my_context _context) outermost_context().fsm_.lock(); if (fsm) { VSOMEIP_TRACE << "sd::active.main.find.send"; - // Increment to the maximum run value (which is repetition_max-1) - // As new request might be added in the meantime, this will be - // used to calculate the maximum cycle time. - if (context<find>().run_ < fsm->get_repetition_max()) { - context<find>().run_++; + if( context<find>().run_ == 0) + { + outermost_context().start_timer(outermost_context().initial_delay_, true); + } else if ( context<find>().run_ < fsm->get_repetition_max() + 1 ) { + // Increment to the maximum run value (which is repetition_max-1) + // As new request might be added in the meantime, this will be + // used to calculate the maximum cycle time. uint32_t its_timeout = (outermost_context().repetitions_base_delay_ << context<find>().run_); - if (fsm->send(true, true)) - outermost_context().start_timer(its_timeout, true); + outermost_context().start_timer(its_timeout, true); } else { post_event(ev_none()); @@ -267,6 +274,13 @@ send::send(my_context _context) sc::result send::react(const ev_alt_timeout &_event) { (void)_event; + std::shared_ptr < service_discovery_fsm > fsm = + outermost_context().fsm_.lock(); + if (fsm) { + VSOMEIP_TRACE << "sd::active.main.find.initial"; + context<find>().run_++; + (void)fsm->send(true, true); + } return transit<send>(); } diff --git a/implementation/service_discovery/src/service_discovery_impl.cpp b/implementation/service_discovery/src/service_discovery_impl.cpp index 10c6c96..78af86b 100644 --- a/implementation/service_discovery/src/service_discovery_impl.cpp +++ b/implementation/service_discovery/src/service_discovery_impl.cpp @@ -5,6 +5,8 @@ #include <vsomeip/constants.hpp> +#include <forward_list> + #include "../include/constants.hpp" #include "../include/defines.hpp" #include "../include/deserializer.hpp" @@ -46,7 +48,7 @@ service_discovery_impl::service_discovery_impl(service_discovery_host *_host) smallest_ttl_ = std::chrono::duration_cast<std::chrono::milliseconds>(smallest_ttl); // TODO: cleanup start condition! - next_subscription_expiration_ = std::chrono::high_resolution_clock::now() + std::chrono::hours(24); + next_subscription_expiration_ = std::chrono::steady_clock::now() + std::chrono::hours(24); } service_discovery_impl::~service_discovery_impl() { @@ -153,28 +155,39 @@ service_discovery_impl::find_request(service_t _service, instance_t _instance) { void service_discovery_impl::subscribe(service_t _service, instance_t _instance, eventgroup_t _eventgroup, major_version_t _major, ttl_t _ttl, client_t _client, subscription_type_e _subscription_type) { - std::lock_guard<std::mutex> its_lock(subscribed_mutex_); - auto found_service = subscribed_.find(_service); - if (found_service != subscribed_.end()) { - auto found_instance = found_service->second.find(_instance); - if (found_instance != found_service->second.end()) { - auto found_eventgroup = found_instance->second.find(_eventgroup); - if (found_eventgroup != found_instance->second.end()) { - auto found_client = found_eventgroup->second.find(_client); - if (found_client != found_eventgroup->second.end()) { - if (found_client->second->get_major() == _major) { - found_client->second->set_ttl(_ttl); - found_client->second->set_expiration(std::chrono::high_resolution_clock::now() - + std::chrono::seconds(_ttl)); - } else { - VSOMEIP_ERROR - << "Subscriptions to different versions of the same " - "service instance are not supported!"; + uint8_t subscribe_count(0); + { + std::lock_guard<std::mutex> its_lock(subscribed_mutex_); + auto found_service = subscribed_.find(_service); + if (found_service != subscribed_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + auto found_eventgroup = found_instance->second.find(_eventgroup); + if (found_eventgroup != found_instance->second.end()) { + auto found_client = found_eventgroup->second.find(_client); + if (found_client != found_eventgroup->second.end()) { + if (found_client->second->get_major() == _major) { + found_client->second->set_ttl(_ttl); + found_client->second->set_expiration(std::chrono::steady_clock::now() + + std::chrono::seconds(_ttl)); + } else { + VSOMEIP_ERROR + << "Subscriptions to different versions of the same " + "service instance are not supported!"; + } + return; } - return; } } } + + const uint8_t max_parallel_subscriptions = 16; // 4Bit Counter field + subscribe_count = static_cast<uint8_t>(subscribed_[_service][_instance][_eventgroup].size()); + if (subscribe_count >= max_parallel_subscriptions) { + VSOMEIP_WARNING << "Too many parallel subscriptions (max.16) on same event group: " + << std::hex << _eventgroup << std::dec; + return; + } } std::shared_ptr < endpoint > its_unreliable; @@ -185,57 +198,54 @@ void service_discovery_impl::subscribe(service_t _service, instance_t _instance, get_subscription_endpoints(_subscription_type, its_unreliable, its_reliable, &its_address, &has_address, _service, _instance, _client); - const uint8_t max_parallel_subscriptions = 16; // 4Bit Counter field - uint8_t subscribe_count = static_cast<uint8_t>(subscribed_[_service][_instance][_eventgroup].size()); - if (subscribe_count >= max_parallel_subscriptions) { - VSOMEIP_WARNING << "Too many parallel subscriptions (max.16) on same event group: " - << std::hex << _eventgroup << std::dec; + std::shared_ptr<runtime> its_runtime = runtime_.lock(); + if (!its_runtime) { return; } + std::shared_ptr<message_impl> its_message = its_runtime->create_message(); + { + std::lock_guard<std::mutex> its_lock(subscribed_mutex_); + // New subscription + std::shared_ptr < subscription > its_subscription = std::make_shared + < subscription > (_major, _ttl, its_reliable, its_unreliable, + _subscription_type, subscribe_count, + std::chrono::steady_clock::time_point() + std::chrono::seconds(_ttl)); + subscribed_[_service][_instance][_eventgroup][_client] = its_subscription; + if (has_address) { + + if (_client != VSOMEIP_ROUTING_CLIENT) { + if (its_subscription->get_endpoint(true) && + !host_->has_identified(_client, _service, _instance, true)) { + return; + } + if (its_subscription->get_endpoint(false) && + !host_->has_identified(_client, _service, _instance, false)) { + return; + } + } - // New subscription - std::shared_ptr < subscription > its_subscription = std::make_shared - < subscription > (_major, _ttl, its_reliable, its_unreliable, - _subscription_type, subscribe_count, - std::chrono::high_resolution_clock::time_point() + std::chrono::seconds(_ttl)); - subscribed_[_service][_instance][_eventgroup][_client] = its_subscription; - if (has_address) { - std::shared_ptr<runtime> its_runtime = runtime_.lock(); - if (!its_runtime) - return; - - if (_client != VSOMEIP_ROUTING_CLIENT) { - if (its_subscription->get_endpoint(true) && - !host_->has_identified(_client, _service, _instance, true)) { - return; + if (its_subscription->get_endpoint(true) + && its_subscription->get_endpoint(true)->is_connected()) { + insert_subscription(its_message, + _service, _instance, + _eventgroup, + its_subscription, true, true); + } else { + // don't insert reliable endpoint option if the + // TCP client endpoint is not yet connected + insert_subscription(its_message, + _service, _instance, + _eventgroup, + its_subscription, false, true); + its_subscription->set_tcp_connection_established(false); } - if (its_subscription->get_endpoint(false) && - !host_->has_identified(_client, _service, _instance, false)) { - return; + if(0 < its_message->get_entries().size()) { + its_subscription->set_acknowledged(false); } } - - std::shared_ptr<message_impl> its_message - = its_runtime->create_message(); - if (its_subscription->get_endpoint(true) - && its_subscription->get_endpoint(true)->is_connected()) { - insert_subscription(its_message, - _service, _instance, - _eventgroup, - its_subscription, true, true); - } else { - // don't insert reliable endpoint option if the - // TCP client endpoint is not yet connected - insert_subscription(its_message, - _service, _instance, - _eventgroup, - its_subscription, false, true); - its_subscription->set_tcp_connection_established(false); - } - if(0 < its_message->get_entries().size()) { - serialize_and_send(its_message, its_address); - its_subscription->set_acknowledged(false); - } + } + if(has_address && 0 < its_message->get_entries().size()) { + serialize_and_send(its_message, its_address); } } @@ -304,44 +314,48 @@ void service_discovery_impl::get_subscription_endpoints( void service_discovery_impl::unsubscribe(service_t _service, instance_t _instance, eventgroup_t _eventgroup, client_t _client) { - - std::lock_guard<std::mutex> its_lock(subscribed_mutex_); - std::shared_ptr < subscription > its_subscription; - auto found_service = subscribed_.find(_service); - if (found_service != subscribed_.end()) { - auto found_instance = found_service->second.find(_instance); - if (found_instance != found_service->second.end()) { - auto found_eventgroup = found_instance->second.find(_eventgroup); - if (found_eventgroup != found_instance->second.end()) { - auto found_client = found_eventgroup->second.find(_client); - if (found_client != found_eventgroup->second.end()) { - its_subscription = found_client->second; - its_subscription->set_ttl(0); - std::shared_ptr < runtime > its_runtime = runtime_.lock(); - if (its_runtime) { - boost::asio::ip::address its_address; + std::shared_ptr < runtime > its_runtime = runtime_.lock(); + if(!its_runtime) { + return; + } + std::shared_ptr < message_impl > its_message = its_runtime->create_message(); + boost::asio::ip::address its_address; + bool has_address(false); + { + std::lock_guard<std::mutex> its_lock(subscribed_mutex_); + std::shared_ptr < subscription > its_subscription; + auto found_service = subscribed_.find(_service); + if (found_service != subscribed_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + auto found_eventgroup = found_instance->second.find(_eventgroup); + if (found_eventgroup != found_instance->second.end()) { + auto found_client = found_eventgroup->second.find(_client); + if (found_client != found_eventgroup->second.end()) { + its_subscription = found_client->second; + its_subscription->set_ttl(0); + found_eventgroup->second.erase(_client); auto endpoint = its_subscription->get_endpoint(false); if (endpoint) { - endpoint->get_remote_address(its_address); + has_address = endpoint->get_remote_address(its_address); } else { endpoint = its_subscription->get_endpoint(true); if (endpoint) { - endpoint->get_remote_address(its_address); + has_address = endpoint->get_remote_address(its_address); } else { return; } } - std::shared_ptr < message_impl > its_message = its_runtime->create_message(); insert_subscription(its_message, _service, _instance, _eventgroup, its_subscription, true, true); - serialize_and_send(its_message, its_address); - - found_eventgroup->second.erase(_client); } } } } } + if (has_address && 0 < its_message->get_entries().size()) { + serialize_and_send(its_message, its_address); + } } void service_discovery_impl::unsubscribe_all(service_t _service, instance_t _instance) { @@ -378,8 +392,8 @@ void service_discovery_impl::increment_session( auto found_session = sessions_sent_.find(_address); if (found_session != sessions_sent_.end()) { found_session->second.first++; - if (found_session->second.first == 0) { // Wrap --> change the reboot flag! - found_session->second = { 1, !found_session->second.second }; + if (found_session->second.first == 0) { + found_session->second = { 1, false }; } } } @@ -464,7 +478,7 @@ std::shared_ptr<option_impl> service_discovery_impl::find_existing_option( opt->get_type() == _option_type && std::static_pointer_cast<ip_option_impl>(opt)->get_layer_four_protocol() == _protocol && std::static_pointer_cast<ip_option_impl>(opt)->get_port() == _port && - std::static_pointer_cast<ip_option_impl>(opt)->is_multicast() == is_multicast && + std::static_pointer_cast<Option>(opt)->is_multicast() == is_multicast && std::static_pointer_cast<Option>(opt)->get_address() == _address) { return opt; } @@ -578,7 +592,7 @@ void service_discovery_impl::insert_find_entries( for (auto its_instance : its_service.second) { auto its_request = its_instance.second; uint8_t its_sent_counter = its_request->get_sent_counter(); - if (its_sent_counter != default_->get_repetition_max()) { + if (its_sent_counter != default_->get_repetition_max() + 1) { if (i >= _start) { if (its_size + VSOMEIP_SOMEIP_SD_ENTRY_SIZE <= max_message_size_) { std::shared_ptr < serviceentry_impl > its_entry = @@ -591,8 +605,8 @@ void service_discovery_impl::insert_find_entries( its_entry->set_minor_version(its_request->get_minor()); its_entry->set_ttl(its_request->get_ttl()); its_size += VSOMEIP_SOMEIP_SD_ENTRY_SIZE; - its_sent_counter++; + its_request->set_sent_counter(its_sent_counter); } else { VSOMEIP_ERROR << "Failed to create service entry!"; @@ -802,15 +816,20 @@ bool service_discovery_impl::send(bool _is_announcing, bool _is_find) { // Serialize and send bool has_sent(false); - for (auto m : its_messages) { - if (m->get_entries().size() > 0) { - std::pair<session_t, bool> its_session = get_session(unicast_); - m->set_session(its_session.first); - m->set_reboot_flag(its_session.second); - if (host_->send(VSOMEIP_SD_CLIENT, m, true)) { - increment_session(unicast_); + { + // lock serialize mutex here as well even if we don't use the + // serializer as it's used to guard access to the sessions_sent_ map + std::lock_guard<std::mutex> its_lock(serialize_mutex_); + for (auto m : its_messages) { + if (m->get_entries().size() > 0) { + std::pair<session_t, bool> its_session = get_session(unicast_); + m->set_session(its_session.first); + m->set_reboot_flag(its_session.second); + if (host_->send(VSOMEIP_SD_CLIENT, m, true)) { + increment_session(unicast_); + } + has_sent = true; } - has_sent = true; } } return has_sent; @@ -993,7 +1012,7 @@ void service_discovery_impl::process_serviceentry( } else { std::shared_ptr<request> its_request = find_request(its_service, its_instance); if (its_request) - its_request->set_sent_counter(default_->get_repetition_max()); + its_request->set_sent_counter((uint8_t) (default_->get_repetition_max() + 1)); unsubscribe_all(its_service, its_instance); host_->del_routing_info(its_service, its_instance, @@ -1015,9 +1034,9 @@ void service_discovery_impl::process_offerservice_serviceentry( std::shared_ptr<request> its_request = find_request(_service, _instance); if (its_request) - its_request->set_sent_counter(default_->get_repetition_max()); + its_request->set_sent_counter((uint8_t) (default_->get_repetition_max() + 1)); - host_->add_routing_info(_service, _instance, + smallest_ttl_ = host_->add_routing_info(_service, _instance, _major, _minor, _ttl, _reliable_address, _reliable_port, _unreliable_address, _unreliable_port); @@ -1032,18 +1051,6 @@ void service_discovery_impl::process_offerservice_serviceentry( = its_runtime->create_message(); for (auto its_eventgroup : found_instance->second) { for (auto its_client : its_eventgroup.second) { - if (its_client.first != VSOMEIP_ROUTING_CLIENT) { - if (its_client.second->get_endpoint(true) && - !host_->has_identified(its_client.first, _service, - _instance, true)) { - continue; - } - if (its_client.second->get_endpoint(false) && - !host_->has_identified(its_client.first, _service, - _instance, false)) { - continue; - } - } std::shared_ptr<subscription> its_subscription(its_client.second); std::shared_ptr<endpoint> its_unreliable; std::shared_ptr<endpoint> its_reliable; @@ -1056,6 +1063,18 @@ void service_discovery_impl::process_offerservice_serviceentry( its_client.first); its_subscription->set_endpoint(its_reliable, true); its_subscription->set_endpoint(its_unreliable, false); + if (its_client.first != VSOMEIP_ROUTING_CLIENT) { + if (its_client.second->get_endpoint(true) && + !host_->has_identified(its_client.first, _service, + _instance, true)) { + continue; + } + if (its_client.second->get_endpoint(false) && + !host_->has_identified(its_client.first, _service, + _instance, false)) { + continue; + } + } if (its_subscription->is_acknowledged()) { if (its_subscription->get_endpoint(true) && its_subscription->get_endpoint(true)->is_connected()) { @@ -1096,6 +1115,7 @@ void service_discovery_impl::process_offerservice_serviceentry( } if (its_target) { + std::lock_guard<std::mutex> its_lock(serialize_mutex_); its_message->set_session(its_session.first); its_message->set_reboot_flag(its_session.second); serializer_->serialize(its_message.get()); @@ -1144,17 +1164,19 @@ void service_discovery_impl::send_unicast_offer_service( instance_t _instance, major_version_t _major, minor_version_t _minor) { if (_major == ANY_MAJOR || _major == _info->get_major()) { if (_minor == 0xFFFFFFFF || _minor <= _info->get_minor()) { - std::shared_ptr<runtime> its_runtime = runtime_.lock(); - if (!its_runtime) { - return; - } - std::shared_ptr<message_impl> its_message = - its_runtime->create_message(); - - uint32_t its_size(max_message_size_); - insert_offer_service(its_message, _service, _instance, _info, its_size); + if (_info->get_endpoint(false) || _info->get_endpoint(true)) { + std::shared_ptr<runtime> its_runtime = runtime_.lock(); + if (!its_runtime) { + return; + } + std::shared_ptr<message_impl> its_message = + its_runtime->create_message(); + uint32_t its_size(max_message_size_); + insert_offer_service(its_message, _service, _instance, _info, + its_size); - serialize_and_send(its_message, get_current_remote_address()); + serialize_and_send(its_message, get_current_remote_address()); + } } } } @@ -1164,22 +1186,27 @@ void service_discovery_impl::send_multicast_offer_service( instance_t _instance, major_version_t _major, minor_version_t _minor) { if (_major == ANY_MAJOR || _major == _info->get_major()) { if (_minor == 0xFFFFFFFF || _minor <= _info->get_minor()) { - std::shared_ptr<runtime> its_runtime = runtime_.lock(); - if (!its_runtime) { - return; - } - std::shared_ptr<message_impl> its_message = - its_runtime->create_message(); - - uint32_t its_size(max_message_size_); - insert_offer_service(its_message, _service, _instance, _info, its_size); - - if (its_message->get_entries().size() > 0) { - std::pair<session_t, bool> its_session = get_session(unicast_); - its_message->set_session(its_session.first); - its_message->set_reboot_flag(its_session.second); - if (host_->send(VSOMEIP_SD_CLIENT, its_message, true)) { - increment_session(unicast_); + if (_info->get_endpoint(false) || _info->get_endpoint(true)) { + std::shared_ptr<runtime> its_runtime = runtime_.lock(); + if (!its_runtime) { + return; + } + std::shared_ptr<message_impl> its_message = + its_runtime->create_message(); + + uint32_t its_size(max_message_size_); + insert_offer_service(its_message, _service, _instance, _info, + its_size); + + if (its_message->get_entries().size() > 0) { + std::lock_guard<std::mutex> its_lock(serialize_mutex_); + std::pair<session_t, bool> its_session = get_session( + unicast_); + its_message->set_session(its_session.first); + its_message->set_reboot_flag(its_session.second); + if (host_->send(VSOMEIP_SD_CLIENT, its_message, true)) { + increment_session(unicast_); + } } } } @@ -1197,61 +1224,63 @@ void service_discovery_impl::on_reliable_endpoint_connected( // send out subscriptions for services where the tcp connection // wasn't established at time of subscription - std::lock_guard<std::mutex> its_lock(subscribed_mutex_); + std::shared_ptr<message_impl> its_message(its_runtime->create_message()); + bool has_address(false); + boost::asio::ip::address its_address; - auto found_service = subscribed_.find(_service); - if (found_service != subscribed_.end()) { - auto found_instance = found_service->second.find(_instance); - if (found_instance != found_service->second.end()) { - if(0 < found_instance->second.size()) { - std::shared_ptr<message_impl> its_message(its_runtime->create_message()); - bool has_address(false); - boost::asio::ip::address its_address; + { + std::lock_guard<std::mutex> its_lock(subscribed_mutex_); + auto found_service = subscribed_.find(_service); + if (found_service != subscribed_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + if(0 < found_instance->second.size()) { - for(const auto &its_eventgroup : found_instance->second) { - for(const auto &its_client : its_eventgroup.second) { - if (its_client.first != VSOMEIP_ROUTING_CLIENT) { - if (its_client.second->get_endpoint(true) && - !host_->has_identified(its_client.first, _service, - _instance, true)) { - continue; + for(const auto &its_eventgroup : found_instance->second) { + for(const auto &its_client : its_eventgroup.second) { + if (its_client.first != VSOMEIP_ROUTING_CLIENT) { + if (its_client.second->get_endpoint(true) && + !host_->has_identified(its_client.first, _service, + _instance, true)) { + continue; + } } - } - std::shared_ptr<subscription> its_subscription(its_client.second); - if(its_subscription && !its_subscription->is_tcp_connection_established()) { - const std::shared_ptr<const endpoint> its_endpoint( - its_subscription->get_endpoint(true)); - if(its_endpoint && its_endpoint->is_connected()) { - if(its_endpoint.get() == _endpoint.get()) { - // mark as established - its_subscription->set_tcp_connection_established(true); - - std::shared_ptr<endpoint> its_unreliable; - std::shared_ptr<endpoint> its_reliable; - get_subscription_endpoints( - its_subscription->get_subscription_type(), - its_unreliable, its_reliable, &its_address, - &has_address, _service, _instance, - its_client.first); - its_subscription->set_endpoint(its_reliable, true); - its_subscription->set_endpoint(its_unreliable, false); - // only insert reliable subscriptions as unreliable - // ones are immediately sent out - insert_subscription(its_message, _service, - _instance, its_eventgroup.first, - its_subscription, true, false); - its_subscription->set_acknowledged(false); + std::shared_ptr<subscription> its_subscription(its_client.second); + if(its_subscription && !its_subscription->is_tcp_connection_established()) { + const std::shared_ptr<const endpoint> its_endpoint( + its_subscription->get_endpoint(true)); + if(its_endpoint && its_endpoint->is_connected()) { + if(its_endpoint.get() == _endpoint.get()) { + // mark as established + its_subscription->set_tcp_connection_established(true); + + std::shared_ptr<endpoint> its_unreliable; + std::shared_ptr<endpoint> its_reliable; + get_subscription_endpoints( + its_subscription->get_subscription_type(), + its_unreliable, its_reliable, &its_address, + &has_address, _service, _instance, + its_client.first); + its_subscription->set_endpoint(its_reliable, true); + its_subscription->set_endpoint(its_unreliable, false); + // only insert reliable subscriptions as unreliable + // ones are immediately sent out + insert_subscription(its_message, _service, + _instance, its_eventgroup.first, + its_subscription, true, false); + its_subscription->set_acknowledged(false); + } } } } } } - if (0 < its_message->get_entries().size() && has_address) { - serialize_and_send(its_message, its_address); - } } } } + if (has_address && 0 < its_message->get_entries().size()) { + serialize_and_send(its_message, its_address); + } } bool service_discovery_impl::insert_offer_service( @@ -1359,8 +1388,10 @@ void service_discovery_impl::process_eventgroupentry( if (_entry->get_owning_message()->get_return_code() != return_code) { VSOMEIP_ERROR << "Invalid return code in SD header"; - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); + if(its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } return; } @@ -1368,14 +1399,18 @@ void service_discovery_impl::process_eventgroupentry( if (_entry->get_num_options(1) == 0 && _entry->get_num_options(2) == 0) { VSOMEIP_ERROR << "Invalid number of options in SubscribeEventGroup entry"; - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); + if(its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } return; } if(_entry->get_owning_message()->get_options_length() < 12) { VSOMEIP_ERROR << "Invalid options length in SD message"; - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); + if(its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } return; } if (_options.size() @@ -1385,8 +1420,18 @@ void service_discovery_impl::process_eventgroupentry( (_entry->get_num_options(1)) + (_entry->get_num_options(2)))) { VSOMEIP_ERROR << "Fewer options in SD message than " "referenced in EventGroup entry or malformed option received"; - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); + if(its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } + return; + } + if(_entry->get_owning_message()->get_someip_length() < _entry->get_owning_message()->get_length() + && its_ttl > 0) { + VSOMEIP_ERROR << std::dec << "SomeIP length field in SubscribeEventGroup message header: [" + << _entry->get_owning_message()->get_someip_length() + << "] bytes, is shorter than length of deserialized message: [" + << (uint32_t) _entry->get_owning_message()->get_length() << "] bytes."; return; } } @@ -1410,7 +1455,7 @@ void service_discovery_impl::process_eventgroupentry( VSOMEIP_ERROR << "Fewer options in SD message than " "referenced in EventGroup entry for " "option run number: " << i; - if (entry_type_e::SUBSCRIBE_EVENTGROUP == its_type) { + if (entry_type_e::SUBSCRIBE_EVENTGROUP == its_type && its_ttl > 0) { insert_subscription_nack(its_message_response, its_service, its_instance, its_eventgroup, its_counter, its_major, its_reserved); } @@ -1426,8 +1471,10 @@ void service_discovery_impl::process_eventgroupentry( boost::asio::ip::address_v4 its_ipv4_address( its_ipv4_option->get_address()); if (!check_layer_four_protocol(its_ipv4_option)) { - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); + if( its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } return; } @@ -1439,9 +1486,11 @@ void service_discovery_impl::process_eventgroupentry( if(!check_ipv4_address(its_first_address) || 0 == its_first_port) { - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); - VSOMEIP_ERROR << "Invalid port or IP address in first endpoint option specified!"; + if(its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } + VSOMEIP_ERROR << "Invalid port or IP address in first IPv4 endpoint option specified!"; return; } } else @@ -1453,9 +1502,11 @@ void service_discovery_impl::process_eventgroupentry( if(!check_ipv4_address(its_second_address) || 0 == its_second_port) { - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); - VSOMEIP_ERROR << "Invalid port or IP address in second endpoint option specified!"; + if(its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } + VSOMEIP_ERROR << "Invalid port or IP address in second IPv4 endpoint option specified!"; return; } } else { @@ -1476,8 +1527,11 @@ void service_discovery_impl::process_eventgroupentry( boost::asio::ip::address_v6 its_ipv6_address( its_ipv6_option->get_address()); if (!check_layer_four_protocol(its_ipv6_option)) { - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); + if(its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } + VSOMEIP_ERROR << "Invalid layer 4 protocol type in IPv6 endpoint option specified!"; return; } @@ -1557,9 +1611,10 @@ void service_discovery_impl::process_eventgroupentry( case option_type_e::UNKNOWN: default: VSOMEIP_WARNING << "Unsupported eventgroup option"; - insert_subscription_nack(its_message_response, its_service, its_instance, - its_eventgroup, its_counter, its_major, its_reserved); - + if(its_ttl > 0) { + insert_subscription_nack(its_message_response, its_service, its_instance, + its_eventgroup, its_counter, its_major, its_reserved); + } break; } } @@ -1604,10 +1659,23 @@ void service_discovery_impl::handle_eventgroup_subscription(service_t _service, // Could not find eventgroup or wrong version if (!its_info || _major != its_info->get_major()) { // Create a temporary info object with TTL=0 --> send NACK + if( its_info && (_major != its_info->get_major())) { + VSOMEIP_ERROR << "Requested major version:[" << (uint32_t) _major + << "] in subscription to service:[" << _service + << "] instance:[" << _instance + << "] eventgroup:[" << _eventgroup + << "], does not match with services major version:[" << (uint32_t) its_info->get_major() + << "]"; + } else { + VSOMEIP_ERROR << "Requested eventgroup:[" << _eventgroup + << "] not found for subscription to service:[" + << _service << "] instance:[" << _instance << "]"; + } its_info = std::make_shared < eventgroupinfo > (_major, 0); - is_nack = true; - insert_subscription_nack(its_message, _service, _instance, - _eventgroup, _counter, _major, _reserved); + if(_ttl > 0) { + insert_subscription_nack(its_message, _service, _instance, + _eventgroup, _counter, _major, _reserved); + } return; } else { boost::asio::ip::address its_first_address, its_second_address; @@ -1622,10 +1690,14 @@ void service_discovery_impl::handle_eventgroup_subscription(service_t _service, } else if(_is_first_reliable) { // tcp unicast its_first_target = its_first_subscriber; // check if TCP connection is established by client - if( !is_tcp_connected(_service, _instance, its_first_target) ) { - is_nack = true; + if( !is_tcp_connected(_service, _instance, its_first_target) && _ttl > 0) { insert_subscription_nack(its_message, _service, _instance, _eventgroup, _counter, _major, _reserved); + VSOMEIP_ERROR << "TCP connection to target1: [" << its_first_target->get_address().to_string() + << ":" << its_first_target->get_port() + << "] not established for subscription to service:[" << _service + << "] instance:[" << _instance + << "] eventgroup:[" << _eventgroup << "]"; return; } } else { // udp unicast @@ -1642,10 +1714,14 @@ void service_discovery_impl::handle_eventgroup_subscription(service_t _service, } else if (_is_second_reliable) { // tcp unicast its_second_target = its_second_subscriber; // check if TCP connection is established by client - if( !is_tcp_connected(_service, _instance, its_second_target) ) { - is_nack = true; + if( !is_tcp_connected(_service, _instance, its_second_target) && _ttl > 0) { insert_subscription_nack(its_message, _service, _instance, _eventgroup, _counter, _major, _reserved); + VSOMEIP_ERROR << "TCP connection to target2 : [" << its_second_target->get_address().to_string() + << ":" << its_second_target->get_port() + << "] not established for subscription to service:[" << _service + << "] instance:[" << _instance + << "] eventgroup:[" << _eventgroup << "]"; return; } } else { // udp unicast @@ -1664,8 +1740,8 @@ void service_discovery_impl::handle_eventgroup_subscription(service_t _service, return; } - std::chrono::high_resolution_clock::time_point its_expiration - = std::chrono::high_resolution_clock::now() + std::chrono::seconds(_ttl); + std::chrono::steady_clock::time_point its_expiration + = std::chrono::steady_clock::now() + std::chrono::seconds(_ttl); if (its_first_target) { if(!host_->on_subscribe_accepted(_service, _instance, _eventgroup, @@ -1807,6 +1883,7 @@ bool service_discovery_impl::is_tcp_connected(service_t _service, void service_discovery_impl::serialize_and_send( std::shared_ptr<message_impl> _message, const boost::asio::ip::address &_address) { + std::lock_guard<std::mutex> its_lock(serialize_mutex_); std::pair<session_t, bool> its_session = get_session(_address); _message->set_session(its_session.first); _message->set_reboot_flag(its_session.second); @@ -1873,27 +1950,6 @@ bool service_discovery_impl::check_static_header_fields( return true; } -void service_discovery_impl::send_eventgroup_subscription_nack( - service_t _service, instance_t _instance,eventgroup_t _eventgroup, - major_version_t _major, uint8_t _counter, uint16_t _reserved) { - std::shared_ptr<runtime> its_runtime = runtime_.lock(); - if (!its_runtime) { - return; - } - std::shared_ptr<message_impl> its_message = its_runtime->create_message(); - if (its_message) { - std::shared_ptr<eventgroupinfo> its_info = host_->find_eventgroup( - _service, _instance, _eventgroup); - if(!its_info) { - // Create a temporary info object with TTL=0 - its_info = std::make_shared < eventgroupinfo > (_major, 0); - } - insert_subscription_nack(its_message, _service, _instance, _eventgroup, - _counter, _major, _reserved); - serialize_and_send(its_message, get_current_remote_address()); - } -} - bool service_discovery_impl::check_layer_four_protocol( const std::shared_ptr<const ip_option_impl> _ip_option) const { if (_ip_option->get_layer_four_protocol() == layer_four_protocol_e::UNKNOWN) { @@ -1909,46 +1965,68 @@ void service_discovery_impl::send_subscriptions(service_t _service, instance_t _ if (!its_runtime) { return; } - std::lock_guard<std::mutex> its_lock(subscribed_mutex_); - auto found_service = subscribed_.find(_service); - if (found_service != subscribed_.end()) { - auto found_instance = found_service->second.find(_instance); - if (found_instance != found_service->second.end()) { - for (auto found_eventgroup : found_instance->second) { - auto found_client = found_eventgroup.second.find(_client); - if (found_client != found_eventgroup.second.end()) { - boost::asio::ip::address its_address; - auto endpoint = found_client->second->get_endpoint(_reliable); - if (endpoint) { - endpoint->get_remote_address(its_address); - std::shared_ptr<message_impl> its_message - = its_runtime->create_message(); - - if(_reliable) { - if(endpoint->is_connected()) { + std::forward_list<std::pair<std::shared_ptr<message_impl>, + const boost::asio::ip::address>> subscription_messages; + { + std::lock_guard<std::mutex> its_lock(subscribed_mutex_); + auto found_service = subscribed_.find(_service); + if (found_service != subscribed_.end()) { + auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + for (auto found_eventgroup : found_instance->second) { + auto found_client = found_eventgroup.second.find(_client); + if (found_client != found_eventgroup.second.end()) { + std::shared_ptr<endpoint> its_unreliable; + std::shared_ptr<endpoint> its_reliable; + bool has_address(false); + boost::asio::ip::address its_address; + get_subscription_endpoints( + found_client->second->get_subscription_type(), + its_unreliable, its_reliable, &its_address, + &has_address, _service, _instance, + found_client->first); + std::shared_ptr<endpoint> endpoint; + if (_reliable) { + endpoint = its_reliable; + found_client->second->set_endpoint(its_reliable, true); + } else { + endpoint = its_unreliable; + found_client->second->set_endpoint(its_unreliable, false); + } + if (endpoint) { + endpoint->get_remote_address(its_address); + std::shared_ptr<message_impl> its_message + = its_runtime->create_message(); + + if(_reliable) { + if(endpoint->is_connected()) { + insert_subscription(its_message, _service, + _instance, found_eventgroup.first, + found_client->second, _reliable, !_reliable); + found_client->second->set_tcp_connection_established(true); + } else { + // don't insert reliable endpoint option if the + // TCP client endpoint is not yet connected + found_client->second->set_tcp_connection_established(false); + } + } else { insert_subscription(its_message, _service, _instance, found_eventgroup.first, found_client->second, _reliable, !_reliable); - found_client->second->set_tcp_connection_established(true); - } else { - // don't insert reliable endpoint option if the - // TCP client endpoint is not yet connected - found_client->second->set_tcp_connection_established(false); } - } else { - insert_subscription(its_message, _service, - _instance, found_eventgroup.first, - found_client->second, _reliable, !_reliable); - } - if(0 < its_message->get_entries().size()) { - serialize_and_send(its_message, its_address); - found_client->second->set_acknowledged(false); + if(0 < its_message->get_entries().size()) { + subscription_messages.push_front({its_message, its_address}); + found_client->second->set_acknowledged(false); + } } } } } } } + for (const auto s : subscription_messages) { + serialize_and_send(s.first, s.second); + } } void service_discovery_impl::start_subscription_expiration_timer() { diff --git a/implementation/service_discovery/src/serviceentry_impl.cpp b/implementation/service_discovery/src/serviceentry_impl.cpp index 9f82b83..28da730 100755 --- a/implementation/service_discovery/src/serviceentry_impl.cpp +++ b/implementation/service_discovery/src/serviceentry_impl.cpp @@ -41,15 +41,15 @@ bool serviceentry_impl::serialize(vsomeip::serializer *_to) const { bool serviceentry_impl::deserialize(vsomeip::deserializer *_from) {
bool is_successful = entry_impl::deserialize(_from);
- uint8_t tmp_major_version;
+ uint8_t tmp_major_version(0);
is_successful = is_successful && _from->deserialize(tmp_major_version);
major_version_ = static_cast<major_version_t>(tmp_major_version);
- uint32_t tmp_ttl;
+ uint32_t tmp_ttl(0);
is_successful = is_successful && _from->deserialize(tmp_ttl, true);
ttl_ = static_cast<ttl_t>(tmp_ttl);
- uint32_t tmp_minor_version;
+ uint32_t tmp_minor_version(0);
is_successful = is_successful && _from->deserialize(tmp_minor_version);
minor_version_ = static_cast<minor_version_t>(tmp_minor_version);
diff --git a/implementation/service_discovery/src/subscription.cpp b/implementation/service_discovery/src/subscription.cpp index 402304c..06741cf 100644 --- a/implementation/service_discovery/src/subscription.cpp +++ b/implementation/service_discovery/src/subscription.cpp @@ -13,7 +13,7 @@ subscription::subscription(major_version_t _major, ttl_t _ttl, std::shared_ptr<endpoint> _unreliable, subscription_type_e _subscription_type, uint8_t _counter, - std::chrono::high_resolution_clock::time_point _expiration) + std::chrono::steady_clock::time_point _expiration) : major_(_major), ttl_(_ttl), reliable_(_reliable), unreliable_(_unreliable), is_acknowledged_(true), @@ -73,11 +73,11 @@ uint8_t subscription::get_counter() const { return counter_; } -std::chrono::high_resolution_clock::time_point subscription::get_expiration() const { +std::chrono::steady_clock::time_point subscription::get_expiration() const { return expiration_; } -void subscription::set_expiration(std::chrono::high_resolution_clock::time_point _expiration) { +void subscription::set_expiration(std::chrono::steady_clock::time_point _expiration) { expiration_ = _expiration; } diff --git a/implementation/tracing/include/trace_connector.hpp b/implementation/tracing/include/trace_connector.hpp index 95803c3..81b2e38 100644 --- a/implementation/tracing/include/trace_connector.hpp +++ b/implementation/tracing/include/trace_connector.hpp @@ -48,7 +48,12 @@ public: VSOMEIP_EXPORT void reset(); VSOMEIP_EXPORT void set_enabled(const bool _enabled); - VSOMEIP_EXPORT bool is_enabled(); + VSOMEIP_EXPORT bool is_enabled() const; + + VSOMEIP_EXPORT void set_sd_enabled(const bool _enabled); + VSOMEIP_EXPORT bool is_sd_enabled() const; + + VSOMEIP_EXPORT bool is_sd_message(const byte_t *_data, uint16_t _data_size) const; VSOMEIP_EXPORT bool add_channel(const trace_channel_t &_id,const std::string &_name); VSOMEIP_EXPORT bool remove_channel(const trace_channel_t &_id); @@ -80,6 +85,7 @@ private: const byte_t *_data, const uint16_t _data_size); bool is_enabled_; + bool is_sd_enabled_; bool is_initialized_; channels_t channels_; diff --git a/implementation/tracing/src/trace_connector.cpp b/implementation/tracing/src/trace_connector.cpp index dc01aa2..8abd0f1 100644 --- a/implementation/tracing/src/trace_connector.cpp +++ b/implementation/tracing/src/trace_connector.cpp @@ -20,6 +20,7 @@ std::shared_ptr<trace_connector> trace_connector::get() { trace_connector::trace_connector() : is_enabled_(false), + is_sd_enabled_(false), is_initialized_(false), channels_(), filter_rules_() @@ -33,12 +34,14 @@ trace_connector::~trace_connector() { void trace_connector::init() { #ifdef USE_DLT - // register channels/contexts - std::lock_guard<std::mutex> lock(dlt_contexts_mutex); - for(auto it = channels_.begin(); it != channels_.end(); ++it) { - DltContext *dlt_context = new DltContext(); - dlt_contexts_.insert(std::make_pair(it->first, dlt_context)); - DLT_REGISTER_CONTEXT_LL_TS(*dlt_context, it->first.c_str(), it->second.c_str(), DLT_LOG_DEBUG, DLT_TRACE_STATUS_ON); + if(!is_initialized_) { + // register channels/contexts + std::lock_guard<std::mutex> lock(dlt_contexts_mutex); + for(auto it = channels_.begin(); it != channels_.end(); ++it) { + DltContext *dlt_context = new DltContext(); + dlt_contexts_.insert(std::make_pair(it->first, dlt_context)); + DLT_REGISTER_CONTEXT_LL_TS(*dlt_context, it->first.c_str(), it->second.c_str(), DLT_LOG_DEBUG, DLT_TRACE_STATUS_ON); + } } #endif is_initialized_ = true; @@ -53,7 +56,9 @@ void trace_connector::reset() { // unregister channels/contexts for(auto it = dlt_contexts_.begin(); it != dlt_contexts_.end(); ++it) { DLT_UNREGISTER_CONTEXT(*it->second); + delete it->second; } + dlt_contexts_.clear(); #endif // reset to default @@ -62,16 +67,34 @@ void trace_connector::reset() { channels_.insert(std::make_pair(VSOMEIP_TC_DEFAULT_CHANNEL_ID, VSOMEIP_TC_DEFAULT_CHANNEL_NAME)); filter_rules_.clear(); + + is_initialized_ = false; } void trace_connector::set_enabled(const bool _enabled) { is_enabled_ = _enabled; } -bool trace_connector::is_enabled() { +bool trace_connector::is_enabled() const { return is_enabled_; } +void trace_connector::set_sd_enabled(const bool _sd_enabled) { + is_sd_enabled_ = _sd_enabled; +} + +bool trace_connector::is_sd_enabled() const { + return is_sd_enabled_; +} + +bool trace_connector::is_sd_message(const byte_t *_data, uint16_t _data_size) const { + if (VSOMEIP_METHOD_POS_MAX < _data_size) { + return (_data[VSOMEIP_SERVICE_POS_MIN] == 0xFF && _data[VSOMEIP_SERVICE_POS_MAX] == 0xFF && + _data[VSOMEIP_METHOD_POS_MIN] == 0x81 && _data[VSOMEIP_METHOD_POS_MAX] == 0x00); + } + return false; +} + bool trace_connector::add_channel(const trace_channel_t &_id, const std::string &_name) { std::lock_guard<std::mutex> its_lock_channels(channels_mutex_); std::lock_guard<std::mutex> its_lock_dlt_contexts(dlt_contexts_mutex); @@ -207,6 +230,9 @@ void trace_connector::trace(const byte_t *_header, uint16_t _header_size, if (_data_size == 0) return; // no data + if (is_sd_message(_data, _data_size) && !is_sd_enabled_) + return; // tracing of service discovery messages is disabled! + // check if filter rules match std::vector<trace_channel_t> its_channels; if (apply_filter_rules(_data, _data_size, its_channels)) { diff --git a/implementation/tracing/src/trace_header.cpp b/implementation/tracing/src/trace_header.cpp index 6609412..2f293ee 100644 --- a/implementation/tracing/src/trace_header.cpp +++ b/implementation/tracing/src/trace_header.cpp @@ -17,11 +17,8 @@ bool trace_header::prepare(const std::shared_ptr<endpoint> &_endpoint, bool _is_ } bool trace_header::prepare(const endpoint *_endpoint, bool _is_sending) { - if (!_endpoint) - return false; - boost::asio::ip::address its_address; - if (_endpoint->get_remote_address(its_address)) { + if (_endpoint && _endpoint->get_remote_address(its_address)) { if (its_address.is_v6()) return false; diff --git a/implementation/utility/include/criticalsection.hpp b/implementation/utility/include/criticalsection.hpp new file mode 100644 index 0000000..6e247a3 --- /dev/null +++ b/implementation/utility/include/criticalsection.hpp @@ -0,0 +1,44 @@ +#ifndef VSOMEIP_CRITICALSECTION_HPP +#define VSOMEIP_CRITICALSECTION_HPP + +#include <memory> +#include <mutex> + + +namespace vsomeip { + +#ifdef WIN32 + + // Windows: CriticalSection uses win32 CRITICAL_SECTION. + // Interface mimics std::mutex so we can use it in + // conjunction with std::unique_lock. + class CriticalSection final { + public: + CriticalSection(); + ~CriticalSection(); + + // prevent copying + CriticalSection(const CriticalSection&) = delete; + CriticalSection& operator=(const CriticalSection&) = delete; + + void lock(); + void unlock(); + bool try_lock(); + + private: + struct Impl; + std::unique_ptr<Impl> m_impl; + }; + +#else + + // Linux: CriticalSection is a type alias for std::mutex. + using CriticalSection = std::mutex; + +#endif + +} // namespace vsomeip + + + +#endif //VSOMEIP_CRITICALSECTION_HPP diff --git a/implementation/utility/include/utility.hpp b/implementation/utility/include/utility.hpp index 9f3a7e3..0831bdb 100644 --- a/implementation/utility/include/utility.hpp +++ b/implementation/utility/include/utility.hpp @@ -11,6 +11,7 @@ #include <vsomeip/enumeration_types.hpp> #include <vsomeip/message.hpp> +#include "criticalsection.hpp" namespace vsomeip { @@ -72,17 +73,34 @@ public: static bool is_file(const std::string &_path); static bool is_folder(const std::string &_path); + static CriticalSection its_local_configuration_mutex__; + static struct configuration_data_t *the_configuration_data__; - static bool auto_configuration_init(const std::string &_name); - static void auto_configuration_exit(); + static bool auto_configuration_init(); + static void auto_configuration_exit(client_t _client); - static bool is_routing_manager_host__; - static bool is_routing_manager_host(); + static bool is_routing_manager_host(client_t _client); + static void set_routing_manager_host(client_t _client); static bool is_used_client_id(client_t _client); - static client_t request_client_id(client_t _client); + static client_t request_client_id(const std::string &_name, + client_t _client); static void release_client_id(client_t _client); + static inline bool is_valid_message_type(message_type_e _type) { + return (_type == message_type_e::MT_REQUEST + || _type == message_type_e::MT_REQUEST_NO_RETURN + || _type == message_type_e::MT_NOTIFICATION + || _type == message_type_e::MT_REQUEST_ACK + || _type == message_type_e::MT_REQUEST_NO_RETURN_ACK + || _type == message_type_e::MT_NOTIFICATION_ACK + || _type == message_type_e::MT_RESPONSE + || _type == message_type_e::MT_ERROR + || _type == message_type_e::MT_RESPONSE_ACK + || _type == message_type_e::MT_ERROR_ACK + || _type == message_type_e::MT_UNKNOWN); + } + static uint16_t its_configuration_refs__; }; diff --git a/implementation/utility/src/criticalsection.cpp b/implementation/utility/src/criticalsection.cpp new file mode 100644 index 0000000..edf7deb --- /dev/null +++ b/implementation/utility/src/criticalsection.cpp @@ -0,0 +1,38 @@ +#include "../include/criticalsection.hpp" + + +#ifdef WIN32 + +#include <Windows.h> + +namespace vsomeip { + + struct CriticalSection::Impl final { + CRITICAL_SECTION m_criticalSection; + }; + + + CriticalSection::CriticalSection() + : m_impl(new CriticalSection::Impl()) { + InitializeCriticalSection(&m_impl->m_criticalSection); + } + + CriticalSection::~CriticalSection() { + DeleteCriticalSection(&m_impl->m_criticalSection); + } + + void CriticalSection::lock() { + EnterCriticalSection(&m_impl->m_criticalSection); + } + + bool CriticalSection::try_lock() { + return (TryEnterCriticalSection(&m_impl->m_criticalSection) != 0); + } + + void CriticalSection::unlock(){ + LeaveCriticalSection(&m_impl->m_criticalSection); + } + +} // namespace vsomeip + +#endif diff --git a/implementation/utility/src/utility.cpp b/implementation/utility/src/utility.cpp index c711320..62dcf59 100644 --- a/implementation/utility/src/utility.cpp +++ b/implementation/utility/src/utility.cpp @@ -8,6 +8,7 @@ #else #include <dlfcn.h> #include <sys/mman.h> + #include <thread> #endif #include <sys/stat.h> @@ -114,56 +115,141 @@ bool utility::is_folder(const std::string &_path) { return false; } +// pointer to shared memory configuration_data_t *utility::the_configuration_data__(nullptr); -bool utility::is_routing_manager_host__(false); +// critical section to protect shared memory pointers, handles and ref count in this process +CriticalSection utility::its_local_configuration_mutex__; +// number of times auto_configuration_init() has been called in this process uint16_t utility::its_configuration_refs__(0); #ifdef WIN32 -HANDLE its_descriptor; +// global (inter-process) mutex +static HANDLE configuration_data_mutex(INVALID_HANDLE_VALUE); +// memory mapping handle +static HANDLE its_descriptor(INVALID_HANDLE_VALUE); #endif -bool utility::auto_configuration_init(const std::string &_name) { +bool utility::auto_configuration_init() { + std::unique_lock<CriticalSection> its_lock(its_local_configuration_mutex__); std::shared_ptr<configuration> its_config(configuration::get()); + #ifdef WIN32 - its_descriptor = CreateFileMapping( - INVALID_HANDLE_VALUE, // use paging file - NULL, // default security - PAGE_READWRITE, // read/write access - 0, // maximum object size (high-order DWORD) - sizeof(configuration_data_t), // maximum object size (low-order DWORD) - VSOMEIP_SHM_NAME); // name of mapping object - - if (its_descriptor != NULL) { - void *its_segment = (LPTSTR)MapViewOfFile(its_descriptor, // handle to map object - FILE_MAP_ALL_ACCESS, // read/write permission - 0, - 0, - sizeof(configuration_data_t)); - the_configuration_data__ - = reinterpret_cast<configuration_data_t *>(its_segment); - if (the_configuration_data__ != nullptr && is_routing_manager_host__) { - the_configuration_data__->mutex_ = CreateMutex( - NULL, // default security attributes - true, // initially owned - "vSomeIP\\SharedMemoryLock"); // named mutex - - the_configuration_data__->client_base_ - = static_cast<unsigned short>(its_config->get_diagnosis_address() << 8); - the_configuration_data__->used_client_ids_[0] - = the_configuration_data__->client_base_; - the_configuration_data__->client_base_++; - the_configuration_data__->max_used_client_ids_index_ = 1; - - if (its_config->get_routing_host() == "" || - its_config->get_routing_host() == _name) - is_routing_manager_host__ = true; - - its_configuration_refs__++; - - ReleaseMutex(the_configuration_data__->mutex_); - } + if (its_configuration_refs__ > 0) { + assert(configuration_data_mutex != INVALID_HANDLE_VALUE); + assert(its_descriptor != INVALID_HANDLE_VALUE); + assert(the_configuration_data__ != nullptr); + + ++its_configuration_refs__; } else { - // TODO: Error + configuration_data_mutex = CreateMutex( + NULL, // default security attributes + true, // initially owned + "vSomeIP_SharedMemoryLock"); // named mutex + if (configuration_data_mutex) { + + if (GetLastError() == ERROR_ALREADY_EXISTS) { + VSOMEIP_DEBUG << "utility::auto_configuration_init: use existing shared memory"; + + // mapping already exists, wait for mutex release and map in + DWORD waitResult = WaitForSingleObject(configuration_data_mutex, INFINITE); + if (waitResult == WAIT_OBJECT_0) { + + its_descriptor = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + sizeof(configuration_data_t), // maximum object size (low-order DWORD) + VSOMEIP_SHM_NAME); // name of mapping object + + if (its_descriptor && GetLastError() == ERROR_ALREADY_EXISTS) { + + void *its_segment = (LPTSTR)MapViewOfFile(its_descriptor, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + sizeof(configuration_data_t)); + + if (its_segment) { + the_configuration_data__ + = reinterpret_cast<configuration_data_t *>(its_segment); + + ++its_configuration_refs__; + } else { + VSOMEIP_ERROR << "utility::auto_configuration_init: MapViewOfFile failed (" << GetLastError() << ")"; + } + } else { + if (its_descriptor) { + VSOMEIP_ERROR << "utility::auto_configuration_init: CreateFileMapping failed. expected existing mapping"; + } else { + VSOMEIP_ERROR << "utility::auto_configuration_init: CreateFileMapping failed (" << GetLastError() << ")"; + } + } + } else { + VSOMEIP_ERROR << "utility::auto_configuration_init: WaitForSingleObject(mutex) failed (" << GetLastError() << ")"; + } + + } else { + VSOMEIP_DEBUG << "utility::auto_configuration_init: create new shared memory"; + + // create and init new mapping + its_descriptor = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + sizeof(configuration_data_t), // maximum object size (low-order DWORD) + VSOMEIP_SHM_NAME); // name of mapping object + + if (its_descriptor) { + void *its_segment = (LPTSTR)MapViewOfFile(its_descriptor, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + sizeof(configuration_data_t)); + if (its_segment) { + the_configuration_data__ + = reinterpret_cast<configuration_data_t *>(its_segment); + + the_configuration_data__->client_base_ + = static_cast<unsigned short>(its_config->get_diagnosis_address() << 8); + the_configuration_data__->used_client_ids_[0] + = the_configuration_data__->client_base_; + the_configuration_data__->client_base_++; + the_configuration_data__->max_used_client_ids_index_ = 1; + the_configuration_data__->max_assigned_client_id_low_byte_ = 0x00; + + the_configuration_data__->routing_manager_host_ = 0x0000; + std::string its_name = its_config->get_routing_host(); + if (its_name == "") + the_configuration_data__->routing_manager_host_ = the_configuration_data__->client_base_; + + its_configuration_refs__++; + } else { + VSOMEIP_ERROR << "utility::auto_configuration_init: MapViewOfFile failed (" << GetLastError() << ")"; + } + } else { + VSOMEIP_ERROR << "utility::auto_configuration_init: CreateFileMapping failed (" << GetLastError() << ")"; + } + } + + // always release mutex + ReleaseMutex(configuration_data_mutex); + } else { + VSOMEIP_ERROR << "utility::auto_configuration_init: CreateMutex failed (" << GetLastError() << ")"; + } + + // cleanup in case of error + if (the_configuration_data__ == nullptr) { + if (its_descriptor != INVALID_HANDLE_VALUE) { + CloseHandle(its_descriptor); + its_descriptor = INVALID_HANDLE_VALUE; + } + if (configuration_data_mutex != INVALID_HANDLE_VALUE) { + CloseHandle(configuration_data_mutex); + configuration_data_mutex = INVALID_HANDLE_VALUE; + } + } } #else const mode_t previous_mask(::umask(static_cast<mode_t>(its_config->get_umask()))); @@ -184,10 +270,6 @@ bool utility::auto_configuration_init(const std::string &_name) { } else { the_configuration_data__ = reinterpret_cast<configuration_data_t *>(its_segment); - if(-1 == ::close(its_descriptor)) { - VSOMEIP_ERROR << "utility::auto_configuration_init: " - "close failed: " << std::strerror(errno); - } if (the_configuration_data__ != nullptr) { int ret; pthread_mutexattr_t attr; @@ -215,21 +297,30 @@ bool utility::auto_configuration_init(const std::string &_name) { = the_configuration_data__->client_base_; the_configuration_data__->client_base_++; the_configuration_data__->max_used_client_ids_index_ = 1; + the_configuration_data__->max_assigned_client_id_low_byte_ = 0x00; - if (its_config->get_routing_host() == "" || - its_config->get_routing_host() == _name) - is_routing_manager_host__ = true; + the_configuration_data__->routing_manager_host_ = 0x0000; + std::string its_name = its_config->get_routing_host(); + if (its_name == "") + the_configuration_data__->routing_manager_host_ = the_configuration_data__->client_base_; its_configuration_refs__++; + the_configuration_data__->initialized_ = 1; + ret = pthread_mutex_unlock(&the_configuration_data__->mutex_); if (0 != ret) { VSOMEIP_ERROR << "pthread_mutex_unlock() failed " << ret; } } + + if(-1 == ::close(its_descriptor)) { + VSOMEIP_ERROR << "utility::auto_configuration_init: " + "close failed: " << std::strerror(errno); + } } } - } else { + } else if (errno == EEXIST) { const mode_t previous_mask(::umask(static_cast<mode_t>(its_config->get_umask()))); its_descriptor = shm_open(VSOMEIP_SHM_NAME, O_RDWR, static_cast<mode_t>(its_config->get_permissions_shm())); @@ -238,30 +329,44 @@ bool utility::auto_configuration_init(const std::string &_name) { VSOMEIP_ERROR << "utility::auto_configuration_init: " "shm_open failed: " << std::strerror(errno); } else { - void *its_segment = mmap(0, sizeof(configuration_data_t), - PROT_READ | PROT_WRITE, MAP_SHARED, - its_descriptor, 0); - if(MAP_FAILED == its_segment) { + // truncate to make sure we work on valid shm; + // in case creator already called truncate, this effectively becomes a noop + if (-1 == ftruncate(its_descriptor, sizeof(configuration_data_t))) { VSOMEIP_ERROR << "utility::auto_configuration_init: " - "mmap failed: " << std::strerror(errno); + "ftruncate failed: " << std::strerror(errno); } else { - the_configuration_data__ - = reinterpret_cast<configuration_data_t *>(its_segment); - if (-1 == ::close(its_descriptor)) { + + void *its_segment = mmap(0, sizeof(configuration_data_t), + PROT_READ | PROT_WRITE, MAP_SHARED, + its_descriptor, 0); + if(MAP_FAILED == its_segment) { VSOMEIP_ERROR << "utility::auto_configuration_init: " - "close failed: " << std::strerror(errno); + "mmap failed: " << std::strerror(errno); + } else { + configuration_data_t *configuration_data + = reinterpret_cast<configuration_data_t *>(its_segment); + + // check if it is ready for use (for 3 seconds) + int retry_count = 300; + while (configuration_data->initialized_ == 0 && --retry_count > 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + if (configuration_data->initialized_ == 0) { + VSOMEIP_ERROR << "utility::auto_configuration_init: data in shm not initialized"; + } else { + the_configuration_data__ = configuration_data; + + pthread_mutex_lock(&the_configuration_data__->mutex_); + its_configuration_refs__++; + pthread_mutex_unlock(&the_configuration_data__->mutex_); + + if (-1 == ::close(its_descriptor)) { + VSOMEIP_ERROR << "utility::auto_configuration_init: " + "close failed: " << std::strerror(errno); + } + } } -#ifdef WIN32 - WaitForSingleObject(the_configuration_data__->mutex_, INFINITE); -#else - pthread_mutex_lock(&the_configuration_data__->mutex_); -#endif - its_configuration_refs__++; -#ifdef WIN32 - ReleaseMutex(the_configuration_data__->mutex_); -#else - pthread_mutex_unlock(&the_configuration_data__->mutex_); -#endif } } } @@ -269,24 +374,39 @@ bool utility::auto_configuration_init(const std::string &_name) { return (the_configuration_data__ != nullptr); } -void utility::auto_configuration_exit() { +void utility::auto_configuration_exit(client_t _client) { + std::unique_lock<CriticalSection> its_lock(its_local_configuration_mutex__); if (the_configuration_data__) { #ifdef WIN32 - WaitForSingleObject(the_configuration_data__->mutex_, INFINITE); + // not manipulating data in shared memory, no need to take global mutex + + assert(configuration_data_mutex != INVALID_HANDLE_VALUE); + assert(its_descriptor != INVALID_HANDLE_VALUE); + its_configuration_refs__--; - ReleaseMutex(the_configuration_data__->mutex_); + + if (is_routing_manager_host(_client)) { + set_routing_manager_host(0x0000); + } if (its_configuration_refs__ == 0) { UnmapViewOfFile(the_configuration_data__); - } + the_configuration_data__ = nullptr; - if (is_routing_manager_host__) { CloseHandle(its_descriptor); + its_descriptor = NULL; + + CloseHandle(configuration_data_mutex); + configuration_data_mutex = NULL; } #else - pthread_mutex_lock(&the_configuration_data__->mutex_); its_configuration_refs__--; - pthread_mutex_unlock(&the_configuration_data__->mutex_); + + bool unlink_shm = false; + if (is_routing_manager_host(_client)) { + set_routing_manager_host(0x0000); + unlink_shm = true; + } if (its_configuration_refs__ == 0) { if (-1 == ::munmap(the_configuration_data__, sizeof(configuration_data_t))) { @@ -296,10 +416,11 @@ void utility::auto_configuration_exit() { VSOMEIP_DEBUG << "utility::auto_configuration_exit: " "munmap succeeded."; the_configuration_data__ = nullptr; + if (unlink_shm) { + shm_unlink(VSOMEIP_SHM_NAME); + } } - } - if (is_routing_manager_host__) { - shm_unlink(VSOMEIP_SHM_NAME); + } #endif } @@ -309,35 +430,98 @@ bool utility::is_used_client_id(client_t _client) { for (int i = 0; i < the_configuration_data__->max_used_client_ids_index_; i++) { - if (the_configuration_data__->used_client_ids_[i] == _client) + if (the_configuration_data__->used_client_ids_[i] == _client + || _client + < (the_configuration_data__->client_base_ + + the_configuration_data__->max_assigned_client_id_low_byte_)) { return true; + } } return false; } -client_t utility::request_client_id(client_t _client) { +client_t utility::request_client_id(const std::string &_name, client_t _client) { + std::unique_lock<CriticalSection> its_lock(its_local_configuration_mutex__); + if (the_configuration_data__ != nullptr) { #ifdef WIN32 - WaitForSingleObject(the_configuration_data__->mutex_, INFINITE); + DWORD waitResult = WaitForSingleObject(configuration_data_mutex, INFINITE); + assert(waitResult == WAIT_OBJECT_0); + (void)waitResult; #else pthread_mutex_lock(&the_configuration_data__->mutex_); #endif + std::shared_ptr<configuration> its_config(configuration::get()); + const std::string its_name = its_config->get_routing_host(); + bool set_client_as_manager_host(false); + if (its_name != "" && its_name == _name) { + if (the_configuration_data__->routing_manager_host_ == 0x0000) { + set_client_as_manager_host = true; + } else { + VSOMEIP_ERROR << "Routing manager with id " << the_configuration_data__->routing_manager_host_ << " already exists."; +#ifdef WIN32 + BOOL releaseResult = ReleaseMutex(configuration_data_mutex); + assert(releaseResult); + (void)releaseResult; +#else + pthread_mutex_unlock(&the_configuration_data__->mutex_); +#endif + return VSOMEIP_DIAGNOSIS_ADDRESS; + } + } else if (its_name == "" && the_configuration_data__->routing_manager_host_ == 0x0000) { + set_client_as_manager_host = true; + } + if (the_configuration_data__->max_used_client_ids_index_ == VSOMEIP_MAX_CLIENTS) { + VSOMEIP_ERROR << "Max amount of possible concurrent active" + << " vsomeip applications reached."; +#ifdef WIN32 + BOOL releaseResult = ReleaseMutex(configuration_data_mutex); + assert(releaseResult); + (void)releaseResult; +#else + pthread_mutex_unlock(&the_configuration_data__->mutex_); +#endif return ILLEGAL_CLIENT; } + bool configured_and_not_used(false); + if (_client != ILLEGAL_CLIENT && !is_used_client_id(_client)) { + configured_and_not_used = true; + } + if (_client == ILLEGAL_CLIENT || is_used_client_id(_client)) { _client = the_configuration_data__->client_base_; } - while (is_used_client_id(_client)) _client++; + while (is_used_client_id(_client)) { + if ((_client & 0x00FF) + 1 > VSOMEIP_MAX_CLIENTS) { + _client = the_configuration_data__->client_base_; + the_configuration_data__->max_assigned_client_id_low_byte_ = 0; + } else { + _client++; + } + } + if (!configured_and_not_used) { + the_configuration_data__->max_assigned_client_id_low_byte_ = + static_cast<unsigned char>((_client & 0x00FF) + % VSOMEIP_MAX_CLIENTS); + } + + if (set_client_as_manager_host) { + the_configuration_data__->routing_manager_host_ = _client; + } the_configuration_data__->used_client_ids_[ the_configuration_data__->max_used_client_ids_index_] = _client; the_configuration_data__->max_used_client_ids_index_++; + + #ifdef WIN32 - ReleaseMutex(the_configuration_data__->mutex_); + BOOL releaseResult = ReleaseMutex(configuration_data_mutex); + assert(releaseResult); + (void)releaseResult; #else pthread_mutex_unlock(&the_configuration_data__->mutex_); #endif @@ -347,9 +531,10 @@ client_t utility::request_client_id(client_t _client) { } void utility::release_client_id(client_t _client) { + std::unique_lock<CriticalSection> its_lock(its_local_configuration_mutex__); if (the_configuration_data__ != nullptr) { #ifdef WIN32 - WaitForSingleObject(the_configuration_data__->mutex_, INFINITE); + WaitForSingleObject(configuration_data_mutex, INFINITE); #else pthread_mutex_lock(&the_configuration_data__->mutex_); #endif @@ -359,7 +544,7 @@ void utility::release_client_id(client_t _client) { i++; } - if (i < the_configuration_data__->max_used_client_ids_index_) { + if (i <= the_configuration_data__->max_used_client_ids_index_) { the_configuration_data__->max_used_client_ids_index_--; for (; i < the_configuration_data__->max_used_client_ids_index_; i++) { the_configuration_data__->used_client_ids_[i] @@ -367,15 +552,55 @@ void utility::release_client_id(client_t _client) { } } #ifdef WIN32 - ReleaseMutex(the_configuration_data__->mutex_); + ReleaseMutex(configuration_data_mutex); #else pthread_mutex_unlock(&the_configuration_data__->mutex_); #endif } } -bool utility::is_routing_manager_host() { - return is_routing_manager_host__; +bool utility::is_routing_manager_host(client_t _client) { + if (the_configuration_data__ == nullptr) { + VSOMEIP_ERROR << "utility::is_routing_manager_host: configuration data not available"; + return false; + } + +#ifdef WIN32 + WaitForSingleObject(configuration_data_mutex, INFINITE); +#else + pthread_mutex_lock(&the_configuration_data__->mutex_); +#endif + + bool is_routing_manager = (the_configuration_data__->routing_manager_host_ == _client); + +#ifdef WIN32 + ReleaseMutex(configuration_data_mutex); +#else + pthread_mutex_unlock(&the_configuration_data__->mutex_); +#endif + + return is_routing_manager; +} + +void utility::set_routing_manager_host(client_t _client) { + if (the_configuration_data__ == nullptr) { + VSOMEIP_ERROR << "utility::set_routing_manager_host: configuration data not available"; + return; + } + +#ifdef WIN32 + WaitForSingleObject(configuration_data_mutex, INFINITE); +#else + pthread_mutex_lock(&the_configuration_data__->mutex_); +#endif + + the_configuration_data__->routing_manager_host_ = _client; + +#ifdef WIN32 + ReleaseMutex(configuration_data_mutex); +#else + pthread_mutex_unlock(&the_configuration_data__->mutex_); +#endif } } // namespace vsomeip diff --git a/interface/vsomeip/application.hpp b/interface/vsomeip/application.hpp index 67b35b5..64f60ce 100644 --- a/interface/vsomeip/application.hpp +++ b/interface/vsomeip/application.hpp @@ -6,11 +6,14 @@ #ifndef VSOMEIP_APPLICATION_HPP #define VSOMEIP_APPLICATION_HPP +#include <chrono> #include <memory> #include <set> +#include <map> #include <vsomeip/primitive_types.hpp> #include <vsomeip/enumeration_types.hpp> +#include <vsomeip/function_types.hpp> #include <vsomeip/constants.hpp> #include <vsomeip/handler.hpp> @@ -78,9 +81,9 @@ public: // Send a message virtual void send(std::shared_ptr<message> _message, bool _flush = true) = 0; - // Set a field or fire an event + // Set a field or fire an event (without/with the ability to force notification for field) virtual void notify(service_t _service, instance_t _instance, - event_t _event, std::shared_ptr<payload> _payload) const = 0; + event_t _event, std::shared_ptr<payload> _payload) const = 0; virtual void notify_one(service_t _service, instance_t _instance, event_t _event, std::shared_ptr<payload> _payload, @@ -123,6 +126,27 @@ public: // Routing/SD hosted by this application!? virtual bool is_routing() const = 0; + + virtual void offer_event(service_t _service, + instance_t _instance, event_t _event, + const std::set<eventgroup_t> &_eventgroups, + bool _is_field, + std::chrono::milliseconds _cycle, + bool _change_resets_cycle, + const epsilon_change_func_t &_epsilon_change_func) = 0; + + virtual void notify(service_t _service, instance_t _instance, + event_t _event, std::shared_ptr<payload> _payload, + bool _force) const = 0; + + virtual void notify_one(service_t _service, instance_t _instance, + event_t _event, std::shared_ptr<payload> _payload, + client_t _client, bool _force) const = 0; + + typedef std::map<service_t, std::map<instance_t, std::map<major_version_t, minor_version_t >>> available_t; + virtual bool are_available(available_t &_available, + service_t _service = ANY_SERVICE, instance_t _instance = ANY_INSTANCE, + major_version_t _major = ANY_MAJOR, minor_version_t _minor = ANY_MINOR) const = 0; }; } // namespace vsomeip diff --git a/interface/vsomeip/export.hpp b/interface/vsomeip/export.hpp index ab4af7f..16b4d23 100644 --- a/interface/vsomeip/export.hpp +++ b/interface/vsomeip/export.hpp @@ -7,9 +7,17 @@ #define __EXPORT__HPP__ #if WIN32 -#define VSOMEIP_EXPORT __declspec(dllexport) + #define VSOMEIP_EXPORT __declspec(dllexport) + #define VSOMEIP_EXPORT_CLASS_EXPLICIT + + #if VSOMEIP_DLL_COMPILATION + #define VSOMEIP_IMPORT_EXPORT __declspec(dllexport) + #else + #define VSOMEIP_IMPORT_EXPORT __declspec(dllimport) + #endif #else -#define VSOMEIP_EXPORT + #define VSOMEIP_EXPORT + #define VSOMEIP_IMPORT_EXPORT #endif #endif diff --git a/interface/vsomeip/function_types.hpp b/interface/vsomeip/function_types.hpp new file mode 100644 index 0000000..7db3f1e --- /dev/null +++ b/interface/vsomeip/function_types.hpp @@ -0,0 +1,22 @@ +// Copyright (C) 2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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/. + +#ifndef VSOMEIP_FUNCTION_TYPES_HPP +#define VSOMEIP_FUNCTION_TYPES_HPP + +#include <functional> +#include <memory> + +namespace vsomeip { + +class payload; + +typedef std::function< + bool (const std::shared_ptr<payload> &, + const std::shared_ptr<payload> &) > epsilon_change_func_t; + +} // namespace vsomeip + +#endif // VSOMEIP_FUNCTION_TYPES_HPP diff --git a/interface/vsomeip/payload.hpp b/interface/vsomeip/payload.hpp index 0186d30..10a3177 100644 --- a/interface/vsomeip/payload.hpp +++ b/interface/vsomeip/payload.hpp @@ -33,6 +33,9 @@ public: VSOMEIP_EXPORT virtual length_t get_length() const = 0;
VSOMEIP_EXPORT virtual void set_capacity(length_t _length) = 0;
+
+ VSOMEIP_EXPORT virtual void set_data(
+ std::vector<byte_t> &&_data) = 0;
};
} // namespace vsomeip
diff --git a/interface/vsomeip/runtime.hpp b/interface/vsomeip/runtime.hpp index fbdbded..14ecb60 100644 --- a/interface/vsomeip/runtime.hpp +++ b/interface/vsomeip/runtime.hpp @@ -19,7 +19,7 @@ class application; class message; class payload; -class VSOMEIP_EXPORT runtime { +class VSOMEIP_IMPORT_EXPORT runtime { public: static std::string get_property(const std::string &_name); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e56ab54..358154c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1006,6 +1006,54 @@ if(NOT ${TESTS_BAT}) ${TEST_SUBSCRIBE_NOTIFY_SERVICE} ) + set(TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE + ${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_same_service_id_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/conf/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE} + ${TEST_SUBSCRIBE_NOTIFY_SERVICE} + ) + + set(TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE + ${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_same_service_id_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/conf/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE} + ${TEST_SUBSCRIBE_NOTIFY_SERVICE} + ) + + set(TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_MASTER_CONFIG_FILE + ${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_autoconfig_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/conf/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_MASTER_CONFIG_FILE} + ${TEST_SUBSCRIBE_NOTIFY_SERVICE} + ) + + set(TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_SLAVE_CONFIG_FILE + ${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_autoconfig_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/conf/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_SLAVE_CONFIG_FILE} + ${TEST_SUBSCRIBE_NOTIFY_SERVICE} + ) + # copy starter scripts into builddir set(TEST_SUBSCRIBE_NOTIFY_MASTER_STARTER ${TEST_SUBSCRIBE_NOTIFY_NAME}_master_starter.sh) copy_to_builddir(${PROJECT_SOURCE_DIR}/test/subscribe_notify_tests/${TEST_SUBSCRIBE_NOTIFY_MASTER_STARTER} @@ -1160,6 +1208,299 @@ if(NOT ${TESTS_BAT}) ) endif() +############################################################################## +# initial event tests +############################################################################## +if(NOT ${TESTS_BAT}) + set(TEST_INITIAL_EVENT_NAME initial_event_test) + set(TEST_INITIAL_EVENT_SERVICE ${TEST_INITIAL_EVENT_NAME}_service) + add_executable(${TEST_INITIAL_EVENT_SERVICE} initial_event_tests/${TEST_INITIAL_EVENT_NAME}_service.cpp) + target_link_libraries(${TEST_INITIAL_EVENT_SERVICE} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + set(TEST_INITIAL_EVENT_CLIENT ${TEST_INITIAL_EVENT_NAME}_client) + add_executable(${TEST_INITIAL_EVENT_CLIENT} initial_event_tests/${TEST_INITIAL_EVENT_NAME}_client.cpp) + target_link_libraries(${TEST_INITIAL_EVENT_CLIENT} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + set(TEST_INITIAL_EVENT_AVAILABILITY_CHECKER ${TEST_INITIAL_EVENT_NAME}_availability_checker) + add_executable(${TEST_INITIAL_EVENT_AVAILABILITY_CHECKER} initial_event_tests/${TEST_INITIAL_EVENT_NAME}_availability_checker.cpp) + target_link_libraries(${TEST_INITIAL_EVENT_AVAILABILITY_CHECKER} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + set(TEST_INITIAL_EVENT_STOP_SERVICE ${TEST_INITIAL_EVENT_NAME}_stop_service) + add_executable(${TEST_INITIAL_EVENT_STOP_SERVICE} initial_event_tests/${TEST_INITIAL_EVENT_NAME}_stop_service.cpp) + target_link_libraries(${TEST_INITIAL_EVENT_STOP_SERVICE} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + # Copy config files for test into $BUILDDIR/test + set(TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_SLAVE_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_SLAVE_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_MASTER_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_partial_same_ports_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_MASTER_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_SLAVE_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_partial_same_ports_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_SLAVE_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_MASTER_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_same_client_ids_same_ports_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_MASTER_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_SLAVE_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_same_client_ids_same_ports_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_SAME_IDS_SAME_PORTS_SLAVE_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_MASTER_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_same_client_ids_diff_ports_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_MASTER_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_same_client_ids_diff_ports_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_SAME_IDS_DIFF_PORTS_SLAVE_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_same_service_id_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + set(TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE + ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_same_service_id_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/conf/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_SLAVE_CONFIG_FILE} + ${TEST_INITIAL_EVENT_SERVICE} + ) + + # copy starter scripts into builddir + set(TEST_INITIAL_EVENT_MASTER_STARTER ${TEST_INITIAL_EVENT_NAME}_master_starter.sh) + copy_to_builddir(${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_MASTER_STARTER} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} + ${TEST_INITIAL_EVENT_SERVICE} + ) + set(TEST_INITIAL_EVENT_SLAVE_STARTER ${TEST_INITIAL_EVENT_NAME}_slave_starter.sh) + copy_to_builddir(${PROJECT_SOURCE_DIR}/test/initial_event_tests/${TEST_INITIAL_EVENT_SLAVE_STARTER} + ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_SLAVE_STARTER} + ${TEST_INITIAL_EVENT_SERVICE} + ) +endif() + +############################################################################## +# offer tests +############################################################################## +if(NOT ${TESTS_BAT}) + set(TEST_OFFER_NAME offer_test) + set(TEST_OFFER_SERVICE ${TEST_OFFER_NAME}_service) + add_executable(${TEST_OFFER_SERVICE} offer_tests/${TEST_OFFER_NAME}_service.cpp) + target_link_libraries(${TEST_OFFER_SERVICE} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + set(TEST_OFFER_CLIENT ${TEST_OFFER_NAME}_client) + add_executable(${TEST_OFFER_CLIENT} offer_tests/${TEST_OFFER_NAME}_client.cpp) + target_link_libraries(${TEST_OFFER_CLIENT} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + set(TEST_OFFER_SERVICE_EXTERNAL ${TEST_OFFER_NAME}_service_external) + add_executable(${TEST_OFFER_SERVICE_EXTERNAL} offer_tests/${TEST_OFFER_NAME}_service_external.cpp) + target_link_libraries(${TEST_OFFER_SERVICE_EXTERNAL} + vsomeip + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + set(TEST_OFFER_EXTERNAL_SD_MESSAGE_SENDER ${TEST_OFFER_NAME}_external_sd_msg_sender) + add_executable(${TEST_OFFER_EXTERNAL_SD_MESSAGE_SENDER} offer_tests/${TEST_OFFER_NAME}_external_sd_msg_sender.cpp) + target_link_libraries(${TEST_OFFER_EXTERNAL_SD_MESSAGE_SENDER} + ${Boost_LIBRARIES} + ${DL_LIBRARY} + ${TEST_LINK_LIBRARIES} + ) + + # copy starter scripts into builddir + set(TEST_OFFER_LOCAL_STARTER ${TEST_OFFER_NAME}_local_starter.sh) + copy_to_builddir(${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_LOCAL_STARTER} + ${PROJECT_BINARY_DIR}/test/${TEST_OFFER_LOCAL_STARTER} + ${TEST_OFFER_SERVICE} + ) + + # Copy config file for local test into $BUILDDIR/test + set(TEST_OFFER_LOCAL_CONFIG_FILE ${TEST_OFFER_NAME}_local.json) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_LOCAL_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_OFFER_LOCAL_CONFIG_FILE} + ${TEST_OFFER_SERVICE} + ) + + # generate and copy json files into builddir for external test + set(TEST_OFFER_SLAVE_CONFIG_FILE ${TEST_OFFER_NAME}_external_slave.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/offer_tests/conf/${TEST_OFFER_SLAVE_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_SLAVE_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_SLAVE_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_OFFER_SLAVE_CONFIG_FILE} + ${TEST_OFFER_SERVICE} + ) + + set(TEST_OFFER_MASTER_CONFIG_FILE ${TEST_OFFER_NAME}_external_master.json) + configure_file( + ${PROJECT_SOURCE_DIR}/test/offer_tests/conf/${TEST_OFFER_MASTER_CONFIG_FILE}.in + ${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_MASTER_CONFIG_FILE} + @ONLY) + copy_to_builddir( + ${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_MASTER_CONFIG_FILE} + ${PROJECT_BINARY_DIR}/test/${TEST_OFFER_MASTER_CONFIG_FILE} + ${TEST_OFFER_SERVICE} + ) + + # Copy starter scripts for external test to $BUILDDIR/test + set(TEST_OFFER_EXTERNAL_MASTER_STARTER ${TEST_OFFER_NAME}_external_master_starter.sh) + configure_file( + ${PROJECT_SOURCE_DIR}/test/offer_tests/conf/${TEST_OFFER_EXTERNAL_MASTER_STARTER}.in + ${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_EXTERNAL_MASTER_STARTER} + @ONLY) + copy_to_builddir(${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_EXTERNAL_MASTER_STARTER} + ${PROJECT_BINARY_DIR}/test/${TEST_OFFER_EXTERNAL_MASTER_STARTER} + ${TEST_OFFER_SERVICE} + ) + set(TEST_OFFER_EXTERNAL_SLAVE_STARTER ${TEST_OFFER_NAME}_external_slave_starter.sh) + copy_to_builddir(${PROJECT_SOURCE_DIR}/test/offer_tests/${TEST_OFFER_EXTERNAL_SLAVE_STARTER} + ${PROJECT_BINARY_DIR}/test/${TEST_OFFER_EXTERNAL_SLAVE_STARTER} + ${TEST_OFFER_SERVICE} + ) +endif() ############################################################################## # Add for every test a dependency to gtest @@ -1185,6 +1526,14 @@ if(NOT ${TESTS_BAT}) add_dependencies(${TEST_SUBSCRIBE_NOTIFY_ONE_SERVICE} gtest) add_dependencies(${TEST_CPU_LOAD_SERVICE} gtest) add_dependencies(${TEST_CPU_LOAD_CLIENT} gtest) + add_dependencies(${TEST_INITIAL_EVENT_SERVICE} gtest) + add_dependencies(${TEST_INITIAL_EVENT_CLIENT} gtest) + add_dependencies(${TEST_INITIAL_EVENT_AVAILABILITY_CHECKER} gtest) + add_dependencies(${TEST_INITIAL_EVENT_STOP_SERVICE} gtest) + add_dependencies(${TEST_OFFER_SERVICE} gtest) + add_dependencies(${TEST_OFFER_CLIENT} gtest) + add_dependencies(${TEST_OFFER_SERVICE_EXTERNAL} gtest) + add_dependencies(${TEST_OFFER_EXTERNAL_SD_MESSAGE_SENDER} gtest) else() add_dependencies(${TEST_LOCAL_ROUTING_SERVICE} gtest) add_dependencies(${TEST_LOCAL_ROUTING_CLIENT} gtest) @@ -1214,6 +1563,14 @@ if(NOT ${TESTS_BAT}) add_dependencies(build_tests ${TEST_SUBSCRIBE_NOTIFY_ONE_SERVICE}) add_dependencies(build_tests ${TEST_CPU_LOAD_SERVICE}) add_dependencies(build_tests ${TEST_CPU_LOAD_CLIENT}) + add_dependencies(build_tests ${TEST_INITIAL_EVENT_SERVICE}) + add_dependencies(build_tests ${TEST_INITIAL_EVENT_CLIENT}) + add_dependencies(build_tests ${TEST_INITIAL_EVENT_AVAILABILITY_CHECKER}) + add_dependencies(build_tests ${TEST_INITIAL_EVENT_STOP_SERVICE}) + add_dependencies(build_tests ${TEST_OFFER_SERVICE}) + add_dependencies(build_tests ${TEST_OFFER_CLIENT}) + add_dependencies(build_tests ${TEST_OFFER_SERVICE_EXTERNAL}) + add_dependencies(build_tests ${TEST_OFFER_EXTERNAL_SD_MESSAGE_SENDER}) else() add_dependencies(build_tests ${TEST_LOCAL_ROUTING_SERVICE}) add_dependencies(build_tests ${TEST_LOCAL_ROUTING_CLIENT}) @@ -1300,7 +1657,7 @@ if(NOT ${TESTS_BAT}) COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_CLIENT_ID_MASTER_STARTER} ${TEST_CLIENT_ID_DIFF_IDS_PARTIAL_SAME_PORTS_MASTER_CONFIG_FILE}) set_tests_properties(${TEST_CLIENT_ID_NAME}_diff_client_ids_partial_same_ports PROPERTIES TIMEOUT 60) - # subscribe notify id tests + # subscribe notify tests add_test(NAME ${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_udp COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_MASTER_STARTER} UDP ${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}) set_tests_properties(${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_udp PROPERTIES TIMEOUT 60) @@ -1345,7 +1702,15 @@ if(NOT ${TESTS_BAT}) COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_MASTER_STARTER} TCP_AND_UDP ${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_PARTIAL_SAME_PORTS_MASTER_CONFIG_FILE}) set_tests_properties(${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_partial_same_ports_both_tcp_and_udp PROPERTIES TIMEOUT 60) - # subscribe notify one id tests + add_test(NAME ${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_same_service_id_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_MASTER_STARTER} UDP ${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE} SAME_SERVICE_ID) + set_tests_properties(${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_same_service_id_udp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_autoconfig_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_MASTER_STARTER} UDP ${TEST_SUBSCRIBE_NOTIFY_DIFF_IDS_DIFF_PORTS_AUTOCONFIG_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_SUBSCRIBE_NOTIFY_NAME}_diff_client_ids_diff_ports_same_service_id_udp PROPERTIES TIMEOUT 60) + + # subscribe notify one id tests add_test(NAME ${TEST_SUBSCRIBE_NOTIFY_ONE_NAME}_diff_client_ids_diff_ports_udp COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_SUBSCRIBE_NOTIFY_ONE_MASTER_STARTER} UDP ${TEST_SUBSCRIBE_NOTIFY_ONE_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}) set_tests_properties(${TEST_SUBSCRIBE_NOTIFY_ONE_NAME}_diff_client_ids_diff_ports_udp PROPERTIES TIMEOUT 60) @@ -1371,6 +1736,63 @@ if(NOT ${TESTS_BAT}) COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_CPU_LOAD_MASTER_STARTER} ) set_tests_properties(${TEST_CPU_LOAD_NAME} PROPERTIES TIMEOUT 1500) + + # initial event tests + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} UDP ${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_udp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_tcp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} TCP ${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_tcp PROPERTIES TIMEOUT 60) +# TODO +# add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_both_tcp_and_udp +# COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} TCP_AND_UDP ${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}) +# set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_both_tcp_and_udp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_prefer_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} PREFER_UDP ${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_prefer_udp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_prefer_tcp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} PREFER_TCP ${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_prefer_tcp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} UDP ${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_udp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_tcp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} TCP ${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_tcp PROPERTIES TIMEOUT 60) +# TODO +# add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_both_tcp_and_udp +# COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} TCP_AND_UDP ${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE}) +# set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_both_tcp_and_udp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_prefer_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} PREFER_UDP ${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_prefer_udp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_prefer_tcp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} PREFER_TCP ${TEST_INITIAL_EVENT_DIFF_IDS_SAME_PORTS_MASTER_CONFIG_FILE}) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_same_ports_prefer_tcp PROPERTIES TIMEOUT 60) +# TODO +# add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_partial_same_ports_both_tcp_and_udp +# COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} TCP_AND_UDP ${TEST_INITIAL_EVENT_DIFF_IDS_PARTIAL_SAME_PORTS_MASTER_CONFIG_FILE}) +# set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_partial_same_ports_both_tcp_and_udp PROPERTIES TIMEOUT 60) + + add_test(NAME ${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_same_service_id_udp + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_INITIAL_EVENT_MASTER_STARTER} UDP ${TEST_INITIAL_EVENT_DIFF_IDS_DIFF_PORTS_SAME_SERVICEID_MASTER_CONFIG_FILE} SAME_SERVICE_ID) + set_tests_properties(${TEST_INITIAL_EVENT_NAME}_diff_client_ids_diff_ports_same_service_id_udp PROPERTIES TIMEOUT 60) + + # offer tests + add_test(NAME ${TEST_OFFER_NAME}_local + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_OFFER_LOCAL_STARTER}) + set_tests_properties(${TEST_OFFER_NAME}_local PROPERTIES TIMEOUT 90) + add_test(NAME ${TEST_OFFER_NAME}_external + COMMAND ${PROJECT_BINARY_DIR}/test/${TEST_OFFER_EXTERNAL_MASTER_STARTER}) + set_tests_properties(${TEST_OFFER_NAME}_local PROPERTIES TIMEOUT 180) else() # Routing tests add_test(NAME ${TEST_LOCAL_ROUTING_NAME} diff --git a/test/application_tests/application_test_starter.sh b/test/application_tests/application_test_starter.sh index a72c6c7..40ae927 100755 --- a/test/application_tests/application_test_starter.sh +++ b/test/application_tests/application_test_starter.sh @@ -39,6 +39,7 @@ then fi kill $DAEMON_PID +wait $DAEMON_PID export VSOMEIP_CONFIGURATION=application_test_daemon.json ../daemon/./vsomeipd & diff --git a/test/header_factory_tests/header_factory_test.cpp b/test/header_factory_tests/header_factory_test.cpp index c02fd74..e034769 100644 --- a/test/header_factory_tests/header_factory_test.cpp +++ b/test/header_factory_tests/header_factory_test.cpp @@ -69,7 +69,6 @@ TEST_F(someip_header_factory_test, create_request_and_response_test) ASSERT_EQ(response_->get_service(), request_->get_service()); ASSERT_EQ(response_->get_method(), request_->get_method()); - ASSERT_EQ(response_->get_client(), request_->get_client()); ASSERT_EQ(response_->get_session(), request_->get_session()); // length? --> gets only set if a payload is added diff --git a/test/header_factory_tests/header_factory_test_client.cpp b/test/header_factory_tests/header_factory_test_client.cpp index dc4a041..b6f3858 100644 --- a/test/header_factory_tests/header_factory_test_client.cpp +++ b/test/header_factory_tests/header_factory_test_client.cpp @@ -105,7 +105,6 @@ void header_factory_test_client::on_message(const std::shared_ptr<vsomeip::messa number_of_acknowledged_messages_++; ASSERT_EQ(_response->get_service(), vsomeip_test::TEST_SERVICE_SERVICE_ID); ASSERT_EQ(_response->get_instance(), vsomeip_test::TEST_SERVICE_INSTANCE_ID); - ASSERT_EQ(_response->get_client(), vsomeip_test::TEST_CLIENT_CLIENT_ID); ASSERT_EQ(_response->get_session(), static_cast<vsomeip::session_t>(number_of_acknowledged_messages_)); if(number_of_acknowledged_messages_ == number_of_messages_to_send_) { diff --git a/test/header_factory_tests/header_factory_test_service.cpp b/test/header_factory_tests/header_factory_test_service.cpp index 4d45d9f..0367d40 100644 --- a/test/header_factory_tests/header_factory_test_service.cpp +++ b/test/header_factory_tests/header_factory_test_service.cpp @@ -47,7 +47,7 @@ void header_factory_test_service::stop() vsomeip_test::TEST_SERVICE_INSTANCE_ID, vsomeip_test::TEST_SERVICE_METHOD_ID); app_->unregister_state_handler(); app_->stop(); - std::thread t([](){ usleep(1000000 * 5);}); + std::thread t([](){ std::this_thread::sleep_for(std::chrono::microseconds(1000000 * 5));}); t.join(); } @@ -109,9 +109,6 @@ void header_factory_test_service::on_message(const std::shared_ptr<vsomeip::mess // TR_SOMEIP_00055 ASSERT_EQ(_request->get_message_type(), vsomeip::message_type_e::MT_REQUEST); - // make sure the message was sent from the service - ASSERT_EQ(_request->get_client(), vsomeip_test::TEST_CLIENT_CLIENT_ID); - // check the session id. ASSERT_EQ(_request->get_session(), static_cast<vsomeip::session_t>(number_of_received_messages_)); @@ -146,7 +143,7 @@ void header_factory_test_service::run() while (!blocked_) condition_.wait(its_lock); - std::thread t([](){ usleep(1000000 * 5);}); + std::thread t([](){ std::this_thread::sleep_for(std::chrono::microseconds(1000000 * 5));}); t.join(); app_->stop(); } diff --git a/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_master.json.in b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_master.json.in new file mode 100644 index 0000000..845cfe4 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_master.json.in @@ -0,0 +1,75 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_one", + "id":"0x1111" + }, + { + "name":"initial_event_test_service_two", + "id":"0x2222" + }, + { + "name":"initial_event_test_service_three", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30001", + "reliable": + { + "port":"40001", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0001", + "unreliable":"30002", + "reliable": + { + "port":"40002", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x3333", + "instance":"0x0001", + "unreliable":"30003", + "reliable": + { + "port":"40003", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x8888", + "instance":"0x0001", + "unreliable":"8888" + } + ], + "routing":"initial_event_test_service_one", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_same_service_id_master.json.in b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_same_service_id_master.json.in new file mode 100644 index 0000000..4348be8 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_same_service_id_master.json.in @@ -0,0 +1,75 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_one", + "id":"0x1111" + }, + { + "name":"initial_event_test_service_two", + "id":"0x2222" + }, + { + "name":"initial_event_test_service_three", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30001", + "reliable": + { + "port":"40001", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x1111", + "instance":"0x0002", + "unreliable":"30002", + "reliable": + { + "port":"40002", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x1111", + "instance":"0x0003", + "unreliable":"30003", + "reliable": + { + "port":"40003", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x8888", + "instance":"0x0001", + "unreliable":"8888" + } + ], + "routing":"initial_event_test_service_one", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_same_service_id_slave.json.in b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_same_service_id_slave.json.in new file mode 100644 index 0000000..5760288 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_same_service_id_slave.json.in @@ -0,0 +1,76 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "diagnosis" : "0x63", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_four", + "id":"0x4444" + }, + { + "name":"initial_event_test_service_five", + "id":"0x5555" + }, + { + "name":"initial_event_test_service_six", + "id":"0x6666" + } + ], + "services": + [ + { + "service":"0x2222", + "instance":"0x0001", + "unreliable":"30004", + "reliable": + { + "port":"40004", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0002", + "unreliable":"30005", + "reliable": + { + "port":"40005", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0003", + "unreliable":"30006", + "reliable": + { + "port":"40006", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x9999", + "instance":"0x0001", + "unreliable":"9999" + } + ], + "routing":"initial_event_test_service_four", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_slave.json.in b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_slave.json.in new file mode 100644 index 0000000..e60c7b8 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_diff_ports_slave.json.in @@ -0,0 +1,76 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "diagnosis" : "0x63", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_four", + "id":"0x4444" + }, + { + "name":"initial_event_test_service_five", + "id":"0x5555" + }, + { + "name":"initial_event_test_service_six", + "id":"0x6666" + } + ], + "services": + [ + { + "service":"0x4444", + "instance":"0x0001", + "unreliable":"30004", + "reliable": + { + "port":"40004", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x5555", + "instance":"0x0001", + "unreliable":"30005", + "reliable": + { + "port":"40005", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x6666", + "instance":"0x0001", + "unreliable":"30006", + "reliable": + { + "port":"40006", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x9999", + "instance":"0x0001", + "unreliable":"9999" + } + ], + "routing":"initial_event_test_service_four", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_diff_client_ids_partial_same_ports_master.json.in b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_partial_same_ports_master.json.in new file mode 100644 index 0000000..3a7c837 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_partial_same_ports_master.json.in @@ -0,0 +1,75 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_one", + "id":"0x1111" + }, + { + "name":"initial_event_test_service_two", + "id":"0x2222" + }, + { + "name":"initial_event_test_service_three", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40001", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40002", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x3333", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40003", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x8888", + "instance":"0x0001", + "unreliable":"8888" + } + ], + "routing":"initial_event_test_service_one", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_diff_client_ids_partial_same_ports_slave.json.in b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_partial_same_ports_slave.json.in new file mode 100644 index 0000000..e296a42 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_partial_same_ports_slave.json.in @@ -0,0 +1,76 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "diagnosis" : "0x63", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_four", + "id":"0x4444" + }, + { + "name":"initial_event_test_service_five", + "id":"0x5555" + }, + { + "name":"initial_event_test_service_six", + "id":"0x6666" + } + ], + "services": + [ + { + "service":"0x4444", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40004", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x5555", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40005", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x6666", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40006", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x9999", + "instance":"0x0001", + "unreliable":"9999" + } + ], + "routing":"initial_event_test_service_four", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_diff_client_ids_same_ports_master.json.in b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_same_ports_master.json.in new file mode 100644 index 0000000..17476ab --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_same_ports_master.json.in @@ -0,0 +1,75 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_one", + "id":"0x1111" + }, + { + "name":"initial_event_test_service_two", + "id":"0x2222" + }, + { + "name":"initial_event_test_service_three", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x3333", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x8888", + "instance":"0x0001", + "unreliable":"8888" + } + ], + "routing":"initial_event_test_service_one", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_diff_client_ids_same_ports_slave.json.in b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_same_ports_slave.json.in new file mode 100644 index 0000000..2f8d7d9 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_diff_client_ids_same_ports_slave.json.in @@ -0,0 +1,76 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "diagnosis" : "0x63", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_four", + "id":"0x4444" + }, + { + "name":"initial_event_test_service_five", + "id":"0x5555" + }, + { + "name":"initial_event_test_service_six", + "id":"0x6666" + } + ], + "services": + [ + { + "service":"0x4444", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x5555", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x6666", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x9999", + "instance":"0x0001", + "unreliable":"9999" + } + ], + "routing":"initial_event_test_service_four", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_same_client_ids_diff_ports_master.json.in b/test/initial_event_tests/conf/initial_event_test_same_client_ids_diff_ports_master.json.in new file mode 100644 index 0000000..845cfe4 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_same_client_ids_diff_ports_master.json.in @@ -0,0 +1,75 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_one", + "id":"0x1111" + }, + { + "name":"initial_event_test_service_two", + "id":"0x2222" + }, + { + "name":"initial_event_test_service_three", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30001", + "reliable": + { + "port":"40001", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0001", + "unreliable":"30002", + "reliable": + { + "port":"40002", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x3333", + "instance":"0x0001", + "unreliable":"30003", + "reliable": + { + "port":"40003", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x8888", + "instance":"0x0001", + "unreliable":"8888" + } + ], + "routing":"initial_event_test_service_one", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_same_client_ids_diff_ports_slave.json.in b/test/initial_event_tests/conf/initial_event_test_same_client_ids_diff_ports_slave.json.in new file mode 100644 index 0000000..df799e2 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_same_client_ids_diff_ports_slave.json.in @@ -0,0 +1,76 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "diagnosis" : "0x63", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_four", + "id":"0x1111" + }, + { + "name":"initial_event_test_service_five", + "id":"0x2222" + }, + { + "name":"initial_event_test_service_six", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x4444", + "instance":"0x0001", + "unreliable":"30004", + "reliable": + { + "port":"40004", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x5555", + "instance":"0x0001", + "unreliable":"30005", + "reliable": + { + "port":"40005", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x6666", + "instance":"0x0001", + "unreliable":"30006", + "reliable": + { + "port":"40006", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x9999", + "instance":"0x0001", + "unreliable":"9999" + } + ], + "routing":"initial_event_test_service_four", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_same_client_ids_same_ports_master.json.in b/test/initial_event_tests/conf/initial_event_test_same_client_ids_same_ports_master.json.in new file mode 100644 index 0000000..17476ab --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_same_client_ids_same_ports_master.json.in @@ -0,0 +1,75 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_one", + "id":"0x1111" + }, + { + "name":"initial_event_test_service_two", + "id":"0x2222" + }, + { + "name":"initial_event_test_service_three", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x3333", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x8888", + "instance":"0x0001", + "unreliable":"8888" + } + ], + "routing":"initial_event_test_service_one", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/conf/initial_event_test_same_client_ids_same_ports_slave.json.in b/test/initial_event_tests/conf/initial_event_test_same_client_ids_same_ports_slave.json.in new file mode 100644 index 0000000..c8113c3 --- /dev/null +++ b/test/initial_event_tests/conf/initial_event_test_same_client_ids_same_ports_slave.json.in @@ -0,0 +1,76 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "diagnosis" : "0x63", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"initial_event_test_service_four", + "id":"0x1111" + }, + { + "name":"initial_event_test_service_five", + "id":"0x2222" + }, + { + "name":"initial_event_test_service_six", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x4444", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x5555", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x6666", + "instance":"0x0001", + "unreliable":"30000", + "reliable": + { + "port":"40000", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x9999", + "instance":"0x0001", + "unreliable":"9999" + } + ], + "routing":"initial_event_test_service_four", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/initial_event_tests/initial_event_test_availability_checker.cpp b/test/initial_event_tests/initial_event_test_availability_checker.cpp new file mode 100644 index 0000000..772df22 --- /dev/null +++ b/test/initial_event_tests/initial_event_test_availability_checker.cpp @@ -0,0 +1,160 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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 <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "initial_event_test_globals.hpp" + + +class initial_event_test_availability_checker { +public: + initial_event_test_availability_checker(int _client_number, + std::array<initial_event_test::service_info, 7> _service_infos) : + client_number_(_client_number), + service_infos_(_service_infos), + app_(vsomeip::runtime::get()->create_application()), + wait_until_registered_(true), + wait_until_other_services_available_(true), + wait_for_stop_(true), + stop_thread_(std::bind(&initial_event_test_availability_checker::wait_for_stop, this)) { + app_->init(); + app_->register_state_handler( + std::bind(&initial_event_test_availability_checker::on_state, this, + std::placeholders::_1)); + + // register availability for all other services and request their event. + for(const auto& i : service_infos_) { + if (i.service_id == 0xFFFF && i.instance_id == 0xFFFF) { + continue; + } + other_services_available_[std::make_pair(i.service_id, i.instance_id)] = false; + app_->register_availability_handler(i.service_id, i.instance_id, + std::bind(&initial_event_test_availability_checker::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service(i.service_id, i.instance_id); + } + + app_->start(); + } + + ~initial_event_test_availability_checker() { + stop_thread_.join(); + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + if(_is_available) { + auto its_service = other_services_available_.find(std::make_pair(_service, _instance)); + if(its_service != other_services_available_.end()) { + if(its_service->second != _is_available) { + its_service->second = true; + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << client_number_ << "] Service [" + << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance + << "] is available."; + + } + } + + if(std::all_of(other_services_available_.cbegin(), + other_services_available_.cend(), + [](const std::map<std::pair<vsomeip::service_t, + vsomeip::instance_t>, bool>::value_type& v) { + return v.second;})) { + + std::lock_guard<std::mutex> its_lock(stop_mutex_); + wait_for_stop_ = false; + stop_condition_.notify_one(); + } + } + } + + void wait_for_stop() { + std::unique_lock<std::mutex> its_lock(stop_mutex_); + while (wait_for_stop_) { + stop_condition_.wait(its_lock); + } + VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex + << client_number_ << "] all services are available. Going down"; + app_->stop(); + } + +private: + int client_number_; + std::array<initial_event_test::service_info, 7> service_infos_; + std::shared_ptr<vsomeip::application> app_; + std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, bool> other_services_available_; + + bool wait_until_registered_; + bool wait_until_other_services_available_; + std::mutex mutex_; + std::condition_variable condition_; + + bool wait_for_stop_; + std::mutex stop_mutex_; + std::condition_variable stop_condition_; + std::thread stop_thread_; +}; + +static int client_number; +static bool use_same_service_id; + +TEST(someip_initial_event_test, wait_for_availability_and_exit) +{ + if(use_same_service_id) { + initial_event_test_availability_checker its_sample(client_number, + initial_event_test::service_infos_same_service_id); + } else { + initial_event_test_availability_checker its_sample(client_number, + initial_event_test::service_infos); + } +} + +#ifndef WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if(argc < 2) { + std::cerr << "Please specify a client number and subscription type, like: " << argv[0] << " 2 SAME_SERVICE_ID" << std::endl; + std::cerr << "Valid client numbers are from 0 to 0xFFFF" << std::endl; + std::cerr << "If SAME_SERVICE_ID is specified as third parameter the test is run w/ multiple instances of the same service" << std::endl; + return 1; + } + + client_number = std::stoi(std::string(argv[1]), nullptr); + + if (argc >= 3 && std::string("SAME_SERVICE_ID") == std::string(argv[2])) { + use_same_service_id = true; + } else { + use_same_service_id = false; + } + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/initial_event_tests/initial_event_test_client.cpp b/test/initial_event_tests/initial_event_test_client.cpp new file mode 100644 index 0000000..5ee194e --- /dev/null +++ b/test/initial_event_tests/initial_event_test_client.cpp @@ -0,0 +1,284 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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 <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "initial_event_test_globals.hpp" + + +class initial_event_test_client { +public: + initial_event_test_client(int _client_number, + vsomeip::subscription_type_e _subscription_type, + std::array<initial_event_test::service_info, 7> _service_infos) : + client_number_(_client_number), + service_infos_(_service_infos), + subscription_type_(_subscription_type), + app_(vsomeip::runtime::get()->create_application()), + wait_until_registered_(true), + wait_until_other_services_available_(true), + wait_for_stop_(true), + stop_thread_(std::bind(&initial_event_test_client::wait_for_stop, this)) { + app_->init(); + app_->register_state_handler( + std::bind(&initial_event_test_client::on_state, this, + std::placeholders::_1)); + + app_->register_message_handler(vsomeip::ANY_SERVICE, + vsomeip::ANY_INSTANCE, vsomeip::ANY_METHOD, + std::bind(&initial_event_test_client::on_message, this, + std::placeholders::_1)); + + // register availability for all other services and request their event. + for(const auto& i : service_infos_) { + if (i.service_id == 0xFFFF && i.instance_id == 0xFFFF) { + continue; + } + app_->register_availability_handler(i.service_id, i.instance_id, + std::bind(&initial_event_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service(i.service_id, i.instance_id); + + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(i.eventgroup_id); + app_->request_event(i.service_id, i.instance_id, i.event_id, its_eventgroups, true); + + other_services_available_[std::make_pair(i.service_id, i.instance_id)] = false; + other_services_received_notification_[std::make_pair(i.service_id, i.method_id)] = 0; + + app_->subscribe(i.service_id, i.instance_id, i.eventgroup_id, + vsomeip::DEFAULT_MAJOR, subscription_type_); + } + + app_->start(); + } + + ~initial_event_test_client() { + stop_thread_.join(); + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + if(_is_available) { + auto its_service = other_services_available_.find(std::make_pair(_service, _instance)); + if(its_service != other_services_available_.end()) { + if(its_service->second != _is_available) { + its_service->second = true; + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << client_number_ << "] Service [" + << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance + << "] is available."; + + } + } + + if(std::all_of(other_services_available_.cbegin(), + other_services_available_.cend(), + [](const std::map<std::pair<vsomeip::service_t, + vsomeip::instance_t>, bool>::value_type& v) { + return v.second;})) { + VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex + << client_number_ << "] all services are available."; + } + } + } + + void on_message(const std::shared_ptr<vsomeip::message> &_message) { + if(_message->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION) { + + other_services_received_notification_[std::make_pair(_message->get_service(), + _message->get_method())]++; + + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << client_number_ << "] " + << "Received a notification with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] from Service/Method [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "/" << std::setw(4) << std::setfill('0') + << std::hex << _message->get_method() <<"] (now have: " + << std::dec << other_services_received_notification_[std::make_pair(_message->get_service(), + _message->get_method())] << ")"; + + std::shared_ptr<vsomeip::payload> its_payload(_message->get_payload()); + EXPECT_EQ(2u, its_payload->get_length()); + EXPECT_EQ((_message->get_service() & 0xFF00 ) >> 8, its_payload->get_data()[0]); + EXPECT_EQ((_message->get_service() & 0xFF), its_payload->get_data()[1]); + bool notify(false); + switch(subscription_type_) { + case vsomeip::subscription_type_e::SU_UNRELIABLE: + case vsomeip::subscription_type_e::SU_RELIABLE: + case vsomeip::subscription_type_e::SU_PREFER_UNRELIABLE: + case vsomeip::subscription_type_e::SU_PREFER_RELIABLE: + if (all_notifications_received()) { + notify = true; + } + break; + case vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE: + if (all_notifications_received_tcp_and_udp()) { + notify = true; + } + break; + } + + if(notify) { + std::lock_guard<std::mutex> its_lock(stop_mutex_); + wait_for_stop_ = false; + stop_condition_.notify_one(); + } + } + } + + bool all_notifications_received() { + return std::all_of( + other_services_received_notification_.cbegin(), + other_services_received_notification_.cend(), + [&](const std::map<std::pair<vsomeip::service_t, + vsomeip::method_t>, std::uint32_t>::value_type& v) + { + return v.second == initial_event_test::notifications_to_send; + } + ); + } + + bool all_notifications_received_tcp_and_udp() { + std::uint32_t received_twice(0); + std::uint32_t received_normal(0); + for(const auto &v : other_services_received_notification_) { + if (v.second == initial_event_test::notifications_to_send * 2) { + received_twice++; + } else if(v.second == initial_event_test::notifications_to_send) { + received_normal++; + } + } + + if( received_twice == (service_infos_.size() - 1) / 2 + && received_normal == (service_infos_.size() - 1) / 2 - 1) { + // routing manager stub receives the notification + // - twice from external nodes + // - and normal from all internal nodes + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << client_number_ << "] " + << "Received notifications:" + << " Normal: " << received_normal + << " Twice: " << received_twice; + return true; + } + return false; + } + + void wait_for_stop() { + std::unique_lock<std::mutex> its_lock(stop_mutex_); + while (wait_for_stop_) { + stop_condition_.wait(its_lock); + } + VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex + << client_number_ + << "] Received notifications from all services, going down"; + + app_->stop(); + } + +private: + int client_number_; + std::array<initial_event_test::service_info, 7> service_infos_; + vsomeip::subscription_type_e subscription_type_; + std::shared_ptr<vsomeip::application> app_; + std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, bool> other_services_available_; + std::map<std::pair<vsomeip::service_t, vsomeip::method_t>, std::uint32_t> other_services_received_notification_; + + bool wait_until_registered_; + bool wait_until_other_services_available_; + std::mutex mutex_; + std::condition_variable condition_; + + bool wait_for_stop_; + std::mutex stop_mutex_; + std::condition_variable stop_condition_; + std::thread stop_thread_; +}; + +static int client_number; +static vsomeip::subscription_type_e subscription_type; +static bool use_same_service_id; + +TEST(someip_initial_event_test, wait_for_initial_events_of_all_services) +{ + if(use_same_service_id) { + initial_event_test_client its_sample(client_number, + subscription_type, + initial_event_test::service_infos_same_service_id); + } else { + initial_event_test_client its_sample(client_number, subscription_type, + initial_event_test::service_infos); + } +} + +#ifndef WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if(argc < 3) { + std::cerr << "Please specify a client number and subscription type, like: " << argv[0] << " 2 UDP SAME_SERVICE_ID" << std::endl; + std::cerr << "Valid client numbers are from 0 to 0xFFFF" << std::endl; + std::cerr << "Valid subscription types include:" << std::endl; + std::cerr << "[TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" << std::endl; + std::cerr << "If SAME_SERVICE_ID is specified as third parameter the test is run w/ multiple instances of the same service" << std::endl; + return 1; + } + + client_number = std::stoi(std::string(argv[1]), nullptr); + + if(std::string("TCP_AND_UDP") == std::string(argv[2])) { + subscription_type = vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE; + } else if(std::string("PREFER_UDP") == std::string(argv[2])) { + subscription_type = vsomeip::subscription_type_e::SU_PREFER_UNRELIABLE; + } else if(std::string("PREFER_TCP") == std::string(argv[2])) { + subscription_type = vsomeip::subscription_type_e::SU_PREFER_RELIABLE; + } else if(std::string("UDP") == std::string(argv[2])) { + subscription_type = vsomeip::subscription_type_e::SU_UNRELIABLE; + } else if(std::string("TCP") == std::string(argv[2])) { + subscription_type = vsomeip::subscription_type_e::SU_RELIABLE; + } else { + std::cerr << "Wrong subscription type passed, exiting" << std::endl; + std::cerr << "Valid subscription types include:" << std::endl; + std::cerr << "[TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" << std::endl; + return 1; + } + + if (argc >= 4 && std::string("SAME_SERVICE_ID") == std::string(argv[3])) { + use_same_service_id = true; + } else { + use_same_service_id = false; + } + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/initial_event_tests/initial_event_test_globals.hpp b/test/initial_event_tests/initial_event_test_globals.hpp new file mode 100644 index 0000000..36f8def --- /dev/null +++ b/test/initial_event_tests/initial_event_test_globals.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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/. + +#ifndef SUBSCRIBE_NOTIFY_TEST_GLOBALS_HPP_ +#define SUBSCRIBE_NOTIFY_TEST_GLOBALS_HPP_ + +namespace initial_event_test { + +struct service_info { + vsomeip::service_t service_id; + vsomeip::instance_t instance_id; + vsomeip::method_t method_id; + vsomeip::event_t event_id; + vsomeip::eventgroup_t eventgroup_id; +}; + +static constexpr std::array<service_info, 7> service_infos = {{ + // placeholder to be consistent w/ client ids, service ids, app names + { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }, + // node 1 + { 0x1111, 0x1, 0x1111, 0x1111, 0x1000 }, + { 0x2222, 0x1, 0x2222, 0x2222, 0x2000 }, + { 0x3333, 0x1, 0x3333, 0x3333, 0x3000 }, + // node 2 + { 0x4444, 0x1, 0x4444, 0x4444, 0x4000 }, + { 0x5555, 0x1, 0x5555, 0x5555, 0x5000 }, + { 0x6666, 0x1, 0x6666, 0x6666, 0x6000 } +}}; + +static constexpr std::array<service_info, 7> service_infos_same_service_id = {{ + // placeholder to be consistent w/ client ids, service ids, app names + { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }, + // node 1 + { 0x1111, 0x1, 0x1111, 0x1111, 0x1000 }, + { 0x1111, 0x2, 0x2222, 0x2222, 0x2000 }, + { 0x1111, 0x3, 0x3333, 0x3333, 0x3000 }, + // node 2 + { 0x2222, 0x1, 0x4444, 0x4444, 0x4000 }, + { 0x2222, 0x2, 0x5555, 0x5555, 0x5000 }, + { 0x2222, 0x3, 0x6666, 0x6666, 0x6000 } +}}; + +static constexpr service_info stop_service_master = { 0x8888, 0x1, 0x8888, 0x0, 0x0 }; +static constexpr service_info stop_service_slave = { 0x9999, 0x1, 0x9999, 0x0, 0x0 }; + +static constexpr int notifications_to_send = 1; +} + +#endif /* SUBSCRIBE_NOTIFY_TEST_GLOBALS_HPP_ */ diff --git a/test/initial_event_tests/initial_event_test_master_starter.sh b/test/initial_event_tests/initial_event_test_master_starter.sh new file mode 100755 index 0000000..903104a --- /dev/null +++ b/test/initial_event_tests/initial_event_test_master_starter.sh @@ -0,0 +1,143 @@ +#!/bin/bash +# Copyright (C) 2015-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# 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/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +if [ $# -lt 2 ] +then + echo "Please pass a subscription method to this script." + echo "For example: $0 UDP initial_event_test_diff_client_ids_diff_ports_master.json" + echo "Valid subscription types include:" + echo " [TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" + echo "Please pass a json file to this script." + echo "For example: $0 UDP initial_event_test_diff_client_ids_diff_ports_master.json" + echo "To use the same service id but different instances on the node pass SAME_SERVICE_ID as third parameter" + exit 1 +fi + +PASSED_SUBSCRIPTION_TYPE=$1 +PASSED_JSON_FILE=$2 +PASSED_SAME_SERVICE_ID_FLAG=$3 + +# Make sure only valid subscription types are passed to the script +SUBSCRIPTION_TYPES="TCP_AND_UDP PREFER_UDP PREFER_TCP UDP TCP" +VALID=0 +for valid_subscription_type in $SUBSCRIPTION_TYPES +do + if [ $valid_subscription_type == $PASSED_SUBSCRIPTION_TYPE ] + then + VALID=1 + fi +done + +if [ $VALID -eq 0 ] +then + echo "Invalid subscription type passed, valid types are:" + echo " [TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" + echo "Exiting" + exit 1 +fi + +# replace master with slave to be able display the correct json file to be used +# with the slave script +MASTER_JSON_FILE=$PASSED_JSON_FILE +CLIENT_JSON_FILE=${MASTER_JSON_FILE/master/slave} + +FAIL=0 + +# Start the services +export VSOMEIP_CONFIGURATION=$PASSED_JSON_FILE + +export VSOMEIP_APPLICATION_NAME=initial_event_test_service_one +./initial_event_test_service 1 $PASSED_SAME_SERVICE_ID_FLAG & +PID_SERVICE_ONE=$! + +export VSOMEIP_APPLICATION_NAME=initial_event_test_service_two +./initial_event_test_service 2 $PASSED_SAME_SERVICE_ID_FLAG & +PID_SERVICE_TWO=$! + +export VSOMEIP_APPLICATION_NAME=initial_event_test_service_three +./initial_event_test_service 3 $PASSED_SAME_SERVICE_ID_FLAG & +PID_SERVICE_THREE=$! + +unset VSOMEIP_APPLICATION_NAME + +# Array for client pids +CLIENT_PIDS=() + +# Start some clients +for client_number in $(seq 9000 9009) +do + ./initial_event_test_client $client_number $PASSED_SUBSCRIPTION_TYPE $PASSED_SAME_SERVICE_ID_FLAG & + CLIENT_PIDS+=($!) +done + +# Start availability checker in order to wait until the services on the remote +# were started as well +./initial_event_test_availability_checker 1234 $PASSED_SAME_SERVICE_ID_FLAG & +PID_AVAILABILITY_CHECKER=$! + +sleep 1 + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** initial_event_test_slave_starter.sh $PASSED_SUBSCRIPTION_TYPE $CLIENT_JSON_FILE $PASSED_SAME_SERVICE_ID_FLAG +** from an external host to successfully complete this test. +** +** You probably will need to adapt the 'unicast' settings in +** initial_event_test_diff_client_ids_diff_ports_master.json and +** initial_event_test_diff_client_ids_diff_ports_slave.json to your personal setup. +******************************************************************************* +******************************************************************************* +End-of-message + +# wait unti the services on the remote node were started as well +wait $PID_AVAILABILITY_CHECKER + +# sleep to make sure the following started clients will have to get +# the cached event from the routing manager daemon +sleep 2 + +for client_number in $(seq 9010 9020) +do + ./initial_event_test_client $client_number $PASSED_SUBSCRIPTION_TYPE $PASSED_SAME_SERVICE_ID_FLAG & + CLIENT_PIDS+=($!) +done + + +# Wait until all clients are finished +for job in ${CLIENT_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# wait until all clients exited on slave side +./initial_event_test_stop_service MASTER & +PID_STOP_SERVICE=$! +wait $PID_STOP_SERVICE + +# kill the services +kill $PID_SERVICE_THREE +kill $PID_SERVICE_TWO +kill $PID_SERVICE_ONE + +sleep 1 +echo "" + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/initial_event_tests/initial_event_test_service.cpp b/test/initial_event_tests/initial_event_test_service.cpp new file mode 100644 index 0000000..26a8e06 --- /dev/null +++ b/test/initial_event_tests/initial_event_test_service.cpp @@ -0,0 +1,130 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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 <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "initial_event_test_globals.hpp" + + +class initial_event_test_service { +public: + initial_event_test_service(struct initial_event_test::service_info _service_info) : + service_info_(_service_info), + app_(vsomeip::runtime::get()->create_application()), + wait_until_registered_(true), + offer_thread_(std::bind(&initial_event_test_service::run, this)) { + app_->init(); + app_->register_state_handler( + std::bind(&initial_event_test_service::on_state, this, + std::placeholders::_1)); + + // offer field + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(service_info_.eventgroup_id); + app_->offer_event(service_info_.service_id, service_info_.instance_id, + service_info_.event_id, its_eventgroups, true); + + // set value to field + std::shared_ptr<vsomeip::payload> its_payload = + vsomeip::runtime::get()->create_payload(); + vsomeip::byte_t its_data[2] = {static_cast<vsomeip::byte_t>((service_info_.service_id & 0xFF00) >> 8), + static_cast<vsomeip::byte_t>((service_info_.service_id & 0xFF))}; + its_payload->set_data(its_data, 2); + app_->notify(service_info_.service_id, service_info_.instance_id, + service_info_.event_id, its_payload); + + app_->start(); + } + + ~initial_event_test_service() { + offer_thread_.join(); + } + + void offer() { + app_->offer_service(service_info_.service_id, service_info_.instance_id); + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void run() { + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Running"; + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Offering"; + offer(); + } + +private: + initial_event_test::service_info service_info_; + std::shared_ptr<vsomeip::application> app_; + + bool wait_until_registered_; + std::mutex mutex_; + std::condition_variable condition_; + std::thread offer_thread_; +}; + +static int service_number; +static bool use_same_service_id; + +TEST(someip_initial_event_test, set_field_once) +{ + if(use_same_service_id) { + initial_event_test_service its_sample( + initial_event_test::service_infos_same_service_id[service_number]); + } else { + initial_event_test_service its_sample( + initial_event_test::service_infos[service_number]); + } +} + +#ifndef WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if(argc < 2) { + std::cerr << "Please specify a service number and subscription type, like: " << argv[0] << " 2 SAME_SERVICE_ID" << std::endl; + std::cerr << "Valid service numbers are in the range of [1,6]" << std::endl; + std::cerr << "If SAME_SERVICE_ID is specified as third parameter the test is run w/ multiple instances of the same service" << std::endl; + return 1; + } + + service_number = std::stoi(std::string(argv[1]), nullptr); + + if (argc >= 3 && std::string("SAME_SERVICE_ID") == std::string(argv[2])) { + use_same_service_id = true; + } else { + use_same_service_id = false; + } + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/initial_event_tests/initial_event_test_slave_starter.sh b/test/initial_event_tests/initial_event_test_slave_starter.sh new file mode 100755 index 0000000..9bd2d78 --- /dev/null +++ b/test/initial_event_tests/initial_event_test_slave_starter.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# Copyright (C) 2015-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# 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/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +if [ $# -lt 2 ] +then + echo "Please pass a subscription method to this script." + echo "For example: $0 UDP initial_event_test_diff_client_ids_diff_ports_slave.json" + echo "Valid subscription types include:" + echo " [TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" + echo "Please pass a json file to this script." + echo "For example: $0 UDP initial_event_test_diff_client_ids_diff_ports_slave.json" + echo "To use the same service id but different instances on the node pass SAME_SERVICE_ID as third parameter" + exit 1 +fi + +PASSED_SUBSCRIPTION_TYPE=$1 +PASSED_JSON_FILE=$2 +PASSED_SAME_SERVICE_ID_FLAG=$3 + +# Make sure only valid subscription types are passed to the script +SUBSCRIPTION_TYPES="TCP_AND_UDP PREFER_UDP PREFER_TCP UDP TCP" +VALID=0 +for valid_subscription_type in $SUBSCRIPTION_TYPES +do + if [ $valid_subscription_type == $PASSED_SUBSCRIPTION_TYPE ] + then + VALID=1 + fi +done + +if [ $VALID -eq 0 ] +then + echo "Invalid subscription type passed, valid types are:" + echo " [TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" + echo "Exiting" + exit 1 +fi + + +FAIL=0 + +export VSOMEIP_CONFIGURATION=$PASSED_JSON_FILE + +# Start the services +export VSOMEIP_APPLICATION_NAME=initial_event_test_service_four +./initial_event_test_service 4 $PASSED_SAME_SERVICE_ID_FLAG & +PID_SERVICE_FOUR=$! + +export VSOMEIP_APPLICATION_NAME=initial_event_test_service_five +./initial_event_test_service 5 $PASSED_SAME_SERVICE_ID_FLAG & +PID_SERVICE_FIVE=$! + +export VSOMEIP_APPLICATION_NAME=initial_event_test_service_six +./initial_event_test_service 6 $PASSED_SAME_SERVICE_ID_FLAG & +PID_SERVICE_SIX=$! + +unset VSOMEIP_APPLICATION_NAME + +# Array for client pids +CLIENT_PIDS=() + +# Start some clients +for client_number in $(seq 9000 9009) +do + ./initial_event_test_client $client_number $PASSED_SUBSCRIPTION_TYPE $PASSED_SAME_SERVICE_ID_FLAG & + CLIENT_PIDS+=($!) +done + +# Start availability checker in order to wait until the services on the remote +# were started as well +./initial_event_test_availability_checker 1234 $PASSED_SAME_SERVICE_ID_FLAG & +PID_AVAILABILITY_CHECKER=$! + +# wait unti the services on the remote node were started as well +wait $PID_AVAILABILITY_CHECKER + +# sleep to make sure the following started clients will have to get +# the cached event from the routing manager daemon +sleep 2 + +for client_number in $(seq 9010 9020) +do + ./initial_event_test_client $client_number $PASSED_SUBSCRIPTION_TYPE $PASSED_SAME_SERVICE_ID_FLAG & + CLIENT_PIDS+=($!) +done + + +# Wait until all clients are finished +for job in ${CLIENT_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# wait until all clients exited on master side +./initial_event_test_stop_service SLAVE & +PID_STOP_SERVICE=$! +wait $PID_STOP_SERVICE + +# kill the services +kill $PID_SERVICE_SIX +kill $PID_SERVICE_FIVE +kill $PID_SERVICE_FOUR + +sleep 1 +echo "" + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/initial_event_tests/initial_event_test_stop_service.cpp b/test/initial_event_tests/initial_event_test_stop_service.cpp new file mode 100644 index 0000000..c8e519e --- /dev/null +++ b/test/initial_event_tests/initial_event_test_stop_service.cpp @@ -0,0 +1,264 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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 <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "initial_event_test_globals.hpp" + + +class initial_event_test_stop_service { +public: + initial_event_test_stop_service(struct initial_event_test::service_info _service_info, bool _is_master) : + service_info_(_service_info), + is_master_(_is_master), + app_(vsomeip::runtime::get()->create_application()), + wait_until_registered_(true), + wait_until_stop_service_other_node_available_(true), + wait_until_shutdown_method_called_(true), + offer_thread_(std::bind(&initial_event_test_stop_service::run, this)), + wait_for_stop_(true), + stop_thread_(std::bind(&initial_event_test_stop_service::wait_for_stop, this)), + called_other_node_(false) { + app_->init(); + app_->register_state_handler( + std::bind(&initial_event_test_stop_service::on_state, this, + std::placeholders::_1)); + app_->register_message_handler(service_info_.service_id, + service_info_.instance_id, service_info_.method_id, + std::bind(&initial_event_test_stop_service::on_shutdown_method_called, this, + std::placeholders::_1)); + + // register availability for all other services and request their event. + if (is_master_) { + app_->request_service( + initial_event_test::stop_service_slave.service_id, + initial_event_test::stop_service_slave.instance_id); + app_->register_availability_handler( + initial_event_test::stop_service_slave.service_id, + initial_event_test::stop_service_slave.instance_id, + std::bind(&initial_event_test_stop_service::on_availability, + this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + } else { + app_->request_service( + initial_event_test::stop_service_master.service_id, + initial_event_test::stop_service_master.instance_id); + app_->register_availability_handler( + initial_event_test::stop_service_master.service_id, + initial_event_test::stop_service_master.instance_id, + std::bind(&initial_event_test_stop_service::on_availability, + this, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + } + app_->start(); + } + + ~initial_event_test_stop_service() { + offer_thread_.join(); + stop_thread_.join(); + } + + void offer() { + if (is_master_) { + app_->offer_service( + initial_event_test::stop_service_master.service_id, + initial_event_test::stop_service_master.instance_id); + } else { + app_->offer_service( + initial_event_test::stop_service_slave.service_id, + initial_event_test::stop_service_slave.instance_id); + } + } + + void stop_offer() { + if (is_master_) { + app_->stop_offer_service( + initial_event_test::stop_service_master.service_id, + initial_event_test::stop_service_master.instance_id); + } else { + app_->stop_offer_service( + initial_event_test::stop_service_slave.service_id, + initial_event_test::stop_service_slave.instance_id); + } + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + bool notify(false); + if(_is_available) { + VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Service [" + << std::setw(4) << std::setfill('0') << std::hex << _service + << "." << _instance << "] is available."; + if(is_master_) { + if(_service == initial_event_test::stop_service_slave.service_id + && _instance == initial_event_test::stop_service_slave.instance_id) { + notify = true; + } + } else { + if(_service == initial_event_test::stop_service_master.service_id + && _instance == initial_event_test::stop_service_master.instance_id) { + notify = true; + } + } + } + if (notify) { + std::lock_guard<std::mutex> its_lock(availability_mutex_); + wait_until_stop_service_other_node_available_ = false; + availability_condition_.notify_one(); + } + } + + void on_shutdown_method_called(const std::shared_ptr<vsomeip::message> &_message) { + if(_message->get_message_type() == vsomeip::message_type_e::MT_REQUEST_NO_RETURN) { + VSOMEIP_DEBUG << "Received a request with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] shutdown method called"; + + std::lock_guard<std::mutex> its_lock(stop_mutex_); + wait_for_stop_ = false; + stop_condition_.notify_one(); + } + } + + void run() { + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Running"; + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Offering"; + offer(); + + std::unique_lock<std::mutex> its_availability_lock(availability_mutex_); + while (wait_until_stop_service_other_node_available_) { + availability_condition_.wait(its_availability_lock); + } + + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Calling shutdown method on remote side"; + + std::shared_ptr<vsomeip::message> msg(vsomeip::runtime::get()->create_request()); + msg->set_message_type(vsomeip::message_type_e::MT_REQUEST_NO_RETURN); + if(is_master_) { + msg->set_service(initial_event_test::stop_service_slave.service_id); + msg->set_instance(initial_event_test::stop_service_slave.instance_id); + msg->set_method(initial_event_test::stop_service_slave.method_id); + } else { + msg->set_service(initial_event_test::stop_service_master.service_id); + msg->set_instance(initial_event_test::stop_service_master.instance_id); + msg->set_method(initial_event_test::stop_service_master.method_id); + } + app_->send(msg); + called_other_node_ = true; + + while (wait_until_shutdown_method_called_) { + condition_.wait(its_lock); + } + } + + void wait_for_stop() { + std::unique_lock<std::mutex> its_lock(stop_mutex_); + while (wait_for_stop_) { + stop_condition_.wait(its_lock); + } + VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id + << "] shutdown method was called, going down"; + while(!called_other_node_) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + // let offer thread exit + { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_shutdown_method_called_ = false; + condition_.notify_one(); + } + + app_->stop(); + } + +private: + initial_event_test::service_info service_info_; + bool is_master_; + std::shared_ptr<vsomeip::application> app_; + std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, bool> other_services_available_; + std::map<std::pair<vsomeip::service_t, vsomeip::method_t>, std::uint32_t> other_services_received_notification_; + + bool wait_until_registered_; + bool wait_until_stop_service_other_node_available_; + bool wait_until_shutdown_method_called_; + std::mutex mutex_; + std::condition_variable condition_; + std::thread offer_thread_; + + std::mutex availability_mutex_; + std::condition_variable availability_condition_; + + bool wait_for_stop_; + std::mutex stop_mutex_; + std::condition_variable stop_condition_; + std::thread stop_thread_; + + bool called_other_node_; +}; + +static bool is_master = false; + +TEST(someip_initial_event_test, wait_for_stop_method_to_be_called) +{ + if(is_master) { + initial_event_test_stop_service its_sample(initial_event_test::stop_service_master, is_master); + } else { + initial_event_test_stop_service its_sample(initial_event_test::stop_service_slave, is_master); + } +} + +#ifndef WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if(argc < 2) { + std::cerr << "Please specify a valid type, like: " << argv[0] << " MASTER" << std::endl; + std::cerr << "Valid types are in the range of [MASTER,SLAVE]" << std::endl; + return 1; + } + + if (argc >= 2 && std::string("MASTER") == std::string(argv[1])) { + is_master = true; + } else if (argc >= 2 && std::string("SLAVE") == std::string(argv[1])){ + is_master = false; + } + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/magic_cookies_tests/magic_cookies_test_client.cpp b/test/magic_cookies_tests/magic_cookies_test_client.cpp index 1b61e60..fb335f9 100644 --- a/test/magic_cookies_tests/magic_cookies_test_client.cpp +++ b/test/magic_cookies_tests/magic_cookies_test_client.cpp @@ -137,35 +137,35 @@ public: // Test sequence its_good_payload_data[11] = 0x01; - its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_bad_payload_data[11] = 0x02; - its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_good_payload_data[11] = 0x03; - its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_bad_payload_data[11] = 0x04; - its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_bad_payload_data[11] = 0x05; - its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_good_payload_data[11] = 0x06; - its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_good_payload_data[11] = 0x07; - its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_bad_payload_data[11] = 0x08; - its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_bad_payload_data[11] = 0x09; - its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_bad_payload_data[11] = 0x0A; - its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_good_payload_data[11] = 0x0B; - its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_good_payload_data[11] = 0x0C; - its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_good_payload_data[11] = 0x0D; - its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_bad_payload_data[11] = 0x0E; - its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_bad_payload_data, sizeof(its_bad_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); its_good_payload_data[11] = 0x0F; - its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true, false); + its_routing->send(0x1343, its_good_payload_data, sizeof(its_good_payload_data), vsomeip_test::TEST_SERVICE_INSTANCE_ID, true, true); std::this_thread::sleep_for(std::chrono::milliseconds(2000)); stop(); diff --git a/test/offer_tests/conf/offer_test_external_master.json.in b/test/offer_tests/conf/offer_test_external_master.json.in new file mode 100644 index 0000000..b16a258 --- /dev/null +++ b/test/offer_tests/conf/offer_test_external_master.json.in @@ -0,0 +1,36 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30001", + "reliable": + { + "port":"40001", + "enable-magic-cookies":"false" + } + } + ], + "routing":"vsomeipd", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp", + "cyclic_offer_delay" : "500" + } +}
\ No newline at end of file diff --git a/test/offer_tests/conf/offer_test_external_master_starter.sh.in b/test/offer_tests/conf/offer_test_external_master_starter.sh.in new file mode 100755 index 0000000..9ff40e8 --- /dev/null +++ b/test/offer_tests/conf/offer_test_external_master_starter.sh.in @@ -0,0 +1,114 @@ +#!/bin/bash +# Copyright (C) 2015-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# 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/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +FAIL=0 +# Rejecting offer for which there is already a remote offer: +# * start daemon +# * start application which offers service +# * start daemon remotely +# * start same application which offers the same service again remotely +# -> should be rejected as there is already a service instance +# running in the network + +# Array for client pids +CLIENT_PIDS=() +export VSOMEIP_CONFIGURATION=offer_test_external_master.json +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! +# Start the services +./offer_test_service 2 & +PID_SERVICE_TWO=$! + +./offer_test_client SUBSCRIBE & +CLIENT_PIDS+=($!) + +sleep 1 + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** offer_test_external_slave_starter.sh +** from an external host to successfully complete this test. +** +** You probably will need to adapt the 'unicast' settings in +** offer_test_external_master.json and +** offer_test_external_slave.json to your personal setup. +******************************************************************************* +******************************************************************************* +End-of-message + + +# Wait until all clients and services are finished +for job in ${CLIENT_PIDS[*]} $PID_SERVICE_TWO +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill $PID_VSOMEIPD +sleep 1 + + +# Rejecting remote offer for which there is already a local offer +# * start application which offers service +# * send sd message trying to offer the same service instance as already +# offered locally from a remote host + +# Array for client pids +CLIENT_PIDS=() +export VSOMEIP_CONFIGURATION=offer_test_external_master.json +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! +# Start the services +./offer_test_service 2 & +PID_SERVICE_TWO=$! + +./offer_test_client SUBSCRIBE & +CLIENT_PIDS+=($!) + +sleep 1 + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** offer_test_external_sd_msg_sender @TEST_IP_MASTER@ +** (pass the correct ip address of your test master) +** from an external host to successfully complete this test. +** +******************************************************************************* +******************************************************************************* +End-of-message + +# Wait until all clients and services are finished +for job in ${CLIENT_PIDS[*]} $PID_SERVICE_TWO +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill $PID_VSOMEIPD +sleep 1 + + +# Check if everything went well +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/offer_tests/conf/offer_test_external_slave.json.in b/test/offer_tests/conf/offer_test_external_slave.json.in new file mode 100644 index 0000000..47a3e9d --- /dev/null +++ b/test/offer_tests/conf/offer_test_external_slave.json.in @@ -0,0 +1,36 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30001", + "reliable": + { + "port":"40001", + "enable-magic-cookies":"false" + } + } + ], + "routing":"vsomeipd", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp", + "cyclic_offer_delay" : "500" + } +}
\ No newline at end of file diff --git a/test/offer_tests/offer_test_client.cpp b/test/offer_tests/offer_test_client.cpp new file mode 100644 index 0000000..bb1cb84 --- /dev/null +++ b/test/offer_tests/offer_test_client.cpp @@ -0,0 +1,281 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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 <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" +#include "../../implementation/configuration/include/internal.hpp" + +#include "offer_test_globals.hpp" + +enum operation_mode_e { + SUBSCRIBE, + METHODCALL +}; + +class offer_test_client { +public: + offer_test_client(struct offer_test::service_info _service_info, operation_mode_e _mode) : + service_info_(_service_info), + operation_mode_(_mode), + app_(vsomeip::runtime::get()->create_application("offer_test_client")), + service_available_(false), + wait_until_registered_(true), + wait_until_service_available_(true), + send_thread_(std::bind(&offer_test_client::send, this)), + wait_for_stop_(true), + stop_thread_(std::bind(&offer_test_client::wait_for_stop, this)), + last_received_counter_(0), + last_received_response_(std::chrono::steady_clock::now()), + number_received_responses_(0) { + app_->init(); + app_->register_state_handler( + std::bind(&offer_test_client::on_state, this, + std::placeholders::_1)); + + app_->register_message_handler(vsomeip::ANY_SERVICE, + vsomeip::ANY_INSTANCE, vsomeip::ANY_METHOD, + std::bind(&offer_test_client::on_message, this, + std::placeholders::_1)); + + // register availability for all other services and request their event. + app_->register_availability_handler(service_info_.service_id, + service_info_.instance_id, + std::bind(&offer_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service(service_info_.service_id, + service_info_.instance_id); + + if (operation_mode_ == operation_mode_e::SUBSCRIBE) { + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(service_info_.eventgroup_id); + app_->request_event(service_info_.service_id, + service_info_.instance_id, service_info_.event_id, + its_eventgroups, false); + + app_->subscribe(service_info_.service_id, service_info_.instance_id, + service_info_.eventgroup_id, vsomeip::DEFAULT_MAJOR, + vsomeip::subscription_type_e::SU_RELIABLE_AND_UNRELIABLE); + } + + app_->start(); + } + + ~offer_test_client() { + send_thread_.join(); + stop_thread_.join(); + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + VSOMEIP_INFO << "Service [" << std::setw(4) + << std::setfill('0') << std::hex << _service << "." << _instance + << "] is " << (_is_available ? "available":"not available") << "."; + std::lock_guard<std::mutex> its_lock(mutex_); + if(_is_available) { + wait_until_service_available_ = false; + condition_.notify_one(); + } else { + wait_until_service_available_ = true; + condition_.notify_one(); + } + } + + void on_message(const std::shared_ptr<vsomeip::message> &_message) { + if(_message->get_message_type() == vsomeip::message_type_e::MT_NOTIFICATION) { + on_notification(_message); + } else if (_message->get_message_type() == vsomeip::message_type_e::MT_RESPONSE) { + on_response(_message); + } + } + + void on_notification(const std::shared_ptr<vsomeip::message> &_message) { + std::shared_ptr<vsomeip::payload> its_payload(_message->get_payload()); + EXPECT_EQ(4u, its_payload->get_length()); + vsomeip::byte_t *d = its_payload->get_data(); + static std::uint32_t number_received_notifications(0); + std::uint32_t counter(0); + counter |= d[0] << 24; + counter |= d[1] << 16; + counter |= d[2] << 8; + counter |= d[3]; + + VSOMEIP_DEBUG + << "Received a notification with Client/Session [" << std::setw(4) + << std::setfill('0') << std::hex << _message->get_client() << "/" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_session() << "] from Service/Method [" + << std::setw(4) << std::setfill('0') << std::hex + << _message->get_service() << "/" << std::setw(4) << std::setfill('0') + << std::hex << _message->get_method() <<"] got:" << std::dec << counter; + + ASSERT_GT(counter, last_received_counter_); + last_received_counter_ = counter; + ++number_received_notifications; + + if(number_received_notifications >= 250) { + std::lock_guard<std::mutex> its_lock(stop_mutex_); + wait_for_stop_ = false; + VSOMEIP_INFO << "going down"; + stop_condition_.notify_one(); + } + } + + void on_response(const std::shared_ptr<vsomeip::message> &_message) { + ++number_received_responses_; + static bool first(true); + if (first) { + first = false; + last_received_response_ = std::chrono::steady_clock::now(); + return; + } + EXPECT_EQ(service_info_.service_id, _message->get_service()); + EXPECT_EQ(service_info_.method_id, _message->get_method()); + EXPECT_EQ(service_info_.instance_id, _message->get_instance()); + ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - last_received_response_).count(), + (std::chrono::milliseconds(VSOMEIP_DEFAULT_WATCHDOG_TIMEOUT) + + std::chrono::milliseconds(1000)).count()); + last_received_response_ = std::chrono::steady_clock::now(); + std::cout << "."; + std::cout.flush(); + } + + void send() { + if (operation_mode_ != operation_mode_e::METHODCALL) { + return; + } + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + + while (wait_until_service_available_) { + condition_.wait(its_lock); + } + its_lock.unlock(); + its_lock.release(); + + for (int var = 0; var < offer_test::number_of_messages_to_send; ++var) { + bool send(false); + { + std::lock_guard<std::mutex> its_lock(mutex_); + send = !wait_until_service_available_; + } + if (send) { + std::shared_ptr<vsomeip::message> its_req = vsomeip::runtime::get()->create_request(); + its_req->set_service(service_info_.service_id); + its_req->set_instance(service_info_.instance_id); + its_req->set_method(service_info_.method_id); + app_->send(its_req); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + { + std::lock_guard<std::mutex> its_lock(stop_mutex_); + wait_for_stop_ = false; + VSOMEIP_INFO << "going down. Sent " << offer_test::number_of_messages_to_send + << " requests and received " << number_received_responses_ + << " responses"; + stop_condition_.notify_one(); + } + } + + void wait_for_stop() { + std::unique_lock<std::mutex> its_lock(stop_mutex_); + while (wait_for_stop_) { + stop_condition_.wait(its_lock); + } + VSOMEIP_INFO << "going down"; + + app_->stop(); + } + +private: + struct offer_test::service_info service_info_; + operation_mode_e operation_mode_; + std::shared_ptr<vsomeip::application> app_; + bool service_available_; + + bool wait_until_registered_; + bool wait_until_service_available_; + std::mutex mutex_; + std::condition_variable condition_; + std::thread send_thread_; + + bool wait_for_stop_; + std::mutex stop_mutex_; + std::condition_variable stop_condition_; + std::thread stop_thread_; + + std::uint32_t last_received_counter_; + std::chrono::steady_clock::time_point last_received_response_; + std::uint32_t number_received_responses_; +}; + +static operation_mode_e passed_mode = operation_mode_e::SUBSCRIBE; + +TEST(someip_offer_test, subscribe_or_call_method_at_service) +{ + offer_test_client its_sample(offer_test::service, passed_mode); +} + +#ifndef WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if(argc < 2) { + std::cerr << "Please specify a operation mode, like: " << argv[0] << " SUBSCRIBE" << std::endl; + std::cerr << "Valid operation modes are SUBSCRIBE and METHODCALL" << std::endl; + return 1; + } + + if (std::string("SUBSCRIBE") == std::string(argv[1])) { + passed_mode = operation_mode_e::SUBSCRIBE; + } else if (std::string("METHODCALL") == std::string(argv[1])) { + passed_mode = operation_mode_e::METHODCALL; + } else { + std::cerr << "Wrong operation mode passed, exiting" << std::endl; + std::cerr << "Please specify a operation mode, like: " << argv[0] << " SUBSCRIBE" << std::endl; + std::cerr << "Valid operation modes are SUBSCRIBE and METHODCALL" << std::endl; + return 1; + } + +#if 0 + if (argc >= 4 && std::string("SAME_SERVICE_ID") == std::string(argv[3])) { + use_same_service_id = true; + } else { + use_same_service_id = false; + } +#endif + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/offer_tests/offer_test_external_sd_msg_sender.cpp b/test/offer_tests/offer_test_external_sd_msg_sender.cpp new file mode 100644 index 0000000..16481aa --- /dev/null +++ b/test/offer_tests/offer_test_external_sd_msg_sender.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2015-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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 <iostream> + +#include <gtest/gtest.h> + +#include <boost/asio.hpp> + +static char* passed_address; + +TEST(someip_offer_test, send_offer_service_sd_message) +{ + try { + boost::asio::io_service io; + boost::asio::ip::udp::socket::endpoint_type target_sd( + boost::asio::ip::address::from_string(std::string(passed_address)), + 30490); + boost::asio::ip::udp::socket udp_socket(io, + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 30490)); + std::uint8_t its_offer_service_message[] = { + 0xff, 0xff, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, + 0x01, 0x00, 0x00, 0x20, + 0x11, 0x11, 0x00, 0x01, + 0x00, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, + 0x00, 0x09, 0x04, 0x00, + 0x0a, 0x00, 0x03, 0x01, + 0x00, 0x06, 0x9c, 0x41, + 0x00, 0x09, 0x04, 0x00, + 0x0a, 0x00, 0x03, 0x7D, // slave address + 0x00, 0x11, 0x75, 0x31 + }; + for (int var = 0; var < 15; ++var) { + udp_socket.send_to(boost::asio::buffer(its_offer_service_message), target_sd); + ++its_offer_service_message[11]; + } + + // call shutdown method + std::uint8_t shutdown_call[] = { + 0x11, 0x11, 0x14, 0x04, + 0x00, 0x00, 0x00, 0x08, + 0x22, 0x22, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x00 }; + boost::asio::ip::udp::socket::endpoint_type target_service( + boost::asio::ip::address::from_string(std::string(passed_address)), + 30001); + udp_socket.send_to(boost::asio::buffer(shutdown_call), target_service); + } catch (...) { + ASSERT_FALSE(true); + } +} + + +#ifndef WIN32 +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + if(argc < 2) { + std::cout << "Please pass an target IP address to this binary like: " + << argv[0] << " 10.0.3.1" << std::endl; + exit(1); + } + passed_address = argv[1]; + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/offer_tests/offer_test_external_slave_starter.sh b/test/offer_tests/offer_test_external_slave_starter.sh new file mode 100755 index 0000000..cdff0d8 --- /dev/null +++ b/test/offer_tests/offer_test_external_slave_starter.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# Copyright (C) 2015-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# 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/. + +FAIL=0 +# Rejecting offer for which there is already a remote offer: +# * start daemon +# * start application which offers service +# * start daemon remotely +# * start same application which offers the same service again remotely +# -> should be rejected as there is already a service instance +# running in the network + +export VSOMEIP_CONFIGURATION=offer_test_external_slave.json +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! +sleep 1 +# Start the services +./offer_test_service_external 2 & +PID_SERVICE_TWO=$! +sleep 1 + +# Wait until all clients and services are finished +for job in $PID_SERVICE_TWO +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill $PID_VSOMEIPD +sleep 1 + + + +# Check if everything went well +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/offer_tests/offer_test_globals.hpp b/test/offer_tests/offer_test_globals.hpp new file mode 100644 index 0000000..7163741 --- /dev/null +++ b/test/offer_tests/offer_test_globals.hpp @@ -0,0 +1,25 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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/. + +#ifndef SUBSCRIBE_NOTIFY_TEST_GLOBALS_HPP_ +#define SUBSCRIBE_NOTIFY_TEST_GLOBALS_HPP_ + +namespace offer_test { + +struct service_info { + vsomeip::service_t service_id; + vsomeip::instance_t instance_id; + vsomeip::method_t method_id; + vsomeip::event_t event_id; + vsomeip::eventgroup_t eventgroup_id; + vsomeip::method_t shutdown_method_id; +}; + +struct service_info service = { 0x1111, 0x1, 0x1111, 0x1111, 0x1000, 0x1404 }; + +static constexpr int number_of_messages_to_send = 150; +} + +#endif /* SUBSCRIBE_NOTIFY_TEST_GLOBALS_HPP_ */ diff --git a/test/offer_tests/offer_test_local.json b/test/offer_tests/offer_test_local.json new file mode 100644 index 0000000..a54049c --- /dev/null +++ b/test/offer_tests/offer_test_local.json @@ -0,0 +1,20 @@ +{ + "unicast":"127.0.0.1", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "routing":"vsomeipd", + "service-discovery": + { + "enable":"false" + } +} + diff --git a/test/offer_tests/offer_test_local_starter.sh b/test/offer_tests/offer_test_local_starter.sh new file mode 100755 index 0000000..69b4821 --- /dev/null +++ b/test/offer_tests/offer_test_local_starter.sh @@ -0,0 +1,298 @@ +#!/bin/bash +# Copyright (C) 2015-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# 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/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +FAIL=0 + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Running first test +******************************************************************************* +******************************************************************************* +End-of-message + +# Rejecting offer of service instance whose hosting application is still +# alive: +# * start application which offers service +# * start two clients which continuously exchanges messages with the service +# * start application which offers the same service again -> should be +# rejected and an error message should be printed. +# * Message exchange with client application should not be interrupted. + +# Array for client pids +CLIENT_PIDS=() +export VSOMEIP_CONFIGURATION=offer_test_local.json +# Start the services +./offer_test_service 1 & +PID_SERVICE_ONE=$! +./offer_test_client SUBSCRIBE & +CLIENT_PIDS+=($!) +./offer_test_client SUBSCRIBE & +CLIENT_PIDS+=($!) + +./offer_test_service 2 & +PID_SERVICE_TWO=$! + +# Wait until all clients are finished +for job in ${CLIENT_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill $PID_SERVICE_TWO +kill $PID_SERVICE_ONE +sleep 1 + + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Running second test +******************************************************************************* +******************************************************************************* +End-of-message + +# Rejecting offer of service instance whose hosting application is still +# alive with daemon: +# * start daemon (needed as he has to ping the offering client) +# * start application which offers service +# * start two clients which continuously exchanges messages with the service +# * start application which offers the same service again -> should be +# rejected and an error message should be printed. +# * Message exchange with client application should not be interrupted. + +# Array for client pids +CLIENT_PIDS=() +export VSOMEIP_CONFIGURATION=offer_test_local.json +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! + +# Start the services +./offer_test_service 2 & +PID_SERVICE_TWO=$! +./offer_test_client SUBSCRIBE & +CLIENT_PIDS+=($!) +./offer_test_client SUBSCRIBE & +CLIENT_PIDS+=($!) + +./offer_test_service 3 & +PID_SERVICE_THREE=$! + +# Wait until all clients are finished +for job in ${CLIENT_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill $PID_SERVICE_THREE +kill $PID_SERVICE_TWO +sleep 1 +kill $PID_VSOMEIPD +sleep 1 + + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Running third test +******************************************************************************* +******************************************************************************* +End-of-message + +# Accepting offer of service instance whose hosting application crashed +# with (send SIGKILL) +# * start daemon +# * start application which offers service +# * start client which exchanges messages with the service +# * kill application with SIGKILL +# * start application which offers the same service again -> should be +# accepted. +# * start another client which exchanges messages with the service +# * Client should now communicate with new offerer. + +# Array for client pids +CLIENT_PIDS=() +export VSOMEIP_CONFIGURATION=offer_test_local.json +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! +# Start the service +./offer_test_service 2 & +PID_SERVICE_TWO=$! + +# Start a client +./offer_test_client METHODCALL & +CLIENT_PIDS+=($!) + +# Kill the service +sleep 1 +kill -KILL $PID_SERVICE_TWO + +# reoffer the service +./offer_test_service 3 & +PID_SERVICE_THREE=$! + +# Start another client +./offer_test_client METHODCALL & +CLIENT_PIDS+=($!) + +# Wait until all clients are finished +for job in ${CLIENT_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill $PID_SERVICE_THREE +kill $PID_VSOMEIPD +sleep 1 + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Running fourth test +******************************************************************************* +******************************************************************************* +End-of-message + +# Accepting offer of service instance whose hosting application became +# unresponsive (SIGSTOP) +# * start daemon +# * start application which offers service +# * Send a SIGSTOP to the service to make it unresponsive +# * start application which offers the same service again -> should be +# marked as PENDING_OFFER and a ping should be sent to the paused +# application. +# * After the timeout passed the new offer should be accepted. +# * start client which exchanges messages with the service +# * Client should now communicate with new offerer. + +# Array for client pids +CLIENT_PIDS=() +export VSOMEIP_CONFIGURATION=offer_test_local.json +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! +# Start the service +./offer_test_service 2 & +PID_SERVICE_TWO=$! + +# Start a client +./offer_test_client METHODCALL & +CLIENT_PIDS+=($!) + +# Pause the service +sleep 1 +kill -STOP $PID_SERVICE_TWO + +# reoffer the service +./offer_test_service 3 & +PID_SERVICE_THREE=$! + +# Start another client +./offer_test_client METHODCALL & +CLIENT_PIDS+=($!) + +# Wait until all clients are finished +for job in ${CLIENT_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill -CONT $PID_SERVICE_TWO +kill $PID_SERVICE_TWO +kill $PID_SERVICE_THREE +kill $PID_VSOMEIPD +sleep 1 + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Running fifth test +******************************************************************************* +******************************************************************************* +End-of-message + +# Rejecting offers for which there is already a pending offer +# * start daemon +# * start application which offers service +# * Send a SIGSTOP to the service to make it unresponsive +# * start application which offers the same service again -> should be +# marked as PENDING_OFFER and a ping should be sent to the paused +# application. +# * start application which offers the same service again -> should be +# rejected as there is already a PENDING_OFFER pending. +# * After the timeout passed the new offer should be accepted. +# * start client which exchanges messages with the service +# * Client should now communicate with new offerer. + +# Array for client pids +CLIENT_PIDS=() +export VSOMEIP_CONFIGURATION=offer_test_local.json +# start daemon +../daemon/./vsomeipd & +PID_VSOMEIPD=$! +# Start the service +./offer_test_service 2 & +PID_SERVICE_TWO=$! + +# Start a client +./offer_test_client METHODCALL & +CLIENT_PIDS+=($!) + +# Pause the service +sleep 1 +kill -STOP $PID_SERVICE_TWO + +# reoffer the service +./offer_test_service 3 & +PID_SERVICE_THREE=$! + +# reoffer the service again to provoke rejecting as there is +# already a pending offer +./offer_test_service 4 & +PID_SERVICE_FOUR=$! + +# Start another client +./offer_test_client METHODCALL & +CLIENT_PIDS+=($!) + +# Wait until all clients are finished +for job in ${CLIENT_PIDS[*]} +do + # Fail gets incremented if a client exits with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# kill the services +kill -CONT $PID_SERVICE_TWO +kill $PID_SERVICE_TWO +kill $PID_SERVICE_THREE +kill $PID_SERVICE_FOUR +kill $PID_VSOMEIPD + + +# Check if everything went well +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/offer_tests/offer_test_service.cpp b/test/offer_tests/offer_test_service.cpp new file mode 100644 index 0000000..a734ed3 --- /dev/null +++ b/test/offer_tests/offer_test_service.cpp @@ -0,0 +1,165 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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 <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "offer_test_globals.hpp" + +static std::string service_number; + +class offer_test_service { +public: + offer_test_service(struct offer_test::service_info _service_info) : + service_info_(_service_info), + // service with number 1 uses "vsomeipd" as application name + // this way the same json file can be reused for all local tests + // including the ones with vsomeipd + app_(vsomeip::runtime::get()->create_application( + (service_number == "1") ? "vsomeipd" : + "offer_test_service" + service_number)), + counter_(0), + wait_until_registered_(true), + offer_thread_(std::bind(&offer_test_service::run, this)), + shutdown_method_called_(false) { + app_->init(); + app_->register_state_handler( + std::bind(&offer_test_service::on_state, this, + std::placeholders::_1)); + + // offer field + std::set<vsomeip::eventgroup_t> its_eventgroups; + its_eventgroups.insert(service_info_.eventgroup_id); + app_->offer_event(service_info_.service_id, service_info_.instance_id, + service_info_.event_id, its_eventgroups, false); + + inc_counter_and_notify(); + + app_->register_message_handler(service_info_.service_id, + service_info_.instance_id, service_info_.method_id, + std::bind(&offer_test_service::on_request, this, + std::placeholders::_1)); + + app_->register_message_handler(service_info_.service_id, + service_info_.instance_id, service_info_.shutdown_method_id, + std::bind(&offer_test_service::on_shutdown_method_called, this, + std::placeholders::_1)); + app_->start(); + } + + ~offer_test_service() { + offer_thread_.join(); + } + + void offer() { + app_->offer_service(service_info_.service_id, service_info_.instance_id); + // this is allowed + app_->offer_service(service_info_.service_id, service_info_.instance_id); + // this is not allowed and will be rejected + app_->offer_service(service_info_.service_id, service_info_.instance_id, 33, 4711); + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_request(const std::shared_ptr<vsomeip::message> &_message) { + app_->send(vsomeip::runtime::get()->create_response(_message)); + } + + void on_shutdown_method_called(const std::shared_ptr<vsomeip::message> &_message) { + (void)_message; + shutdown_method_called_ = true; + // this is will trigger a warning + app_->stop_offer_service(service_info_.service_id, service_info_.instance_id, 44, 4711); + app_->stop_offer_service(service_info_.service_id, service_info_.instance_id); + app_->stop(); + } + + void run() { + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Running"; + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Offering"; + offer(); + + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Notifying"; + while(!shutdown_method_called_) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + inc_counter_and_notify(); + } + } + + void inc_counter_and_notify() { + ++counter_; + // set value to field + const std::shared_ptr<vsomeip::payload> its_payload(vsomeip::runtime::get()->create_payload()); + std::vector<vsomeip::byte_t> its_data; + its_data.push_back(static_cast<vsomeip::byte_t>((counter_ & 0xFF000000) >> 24)); + its_data.push_back(static_cast<vsomeip::byte_t>((counter_ & 0xFF0000) >> 16)); + its_data.push_back(static_cast<vsomeip::byte_t>((counter_ & 0xFF00) >> 8)); + its_data.push_back(static_cast<vsomeip::byte_t>((counter_ & 0xFF))); + its_payload->set_data(its_data); + app_->notify(service_info_.service_id, service_info_.instance_id, + service_info_.event_id, its_payload); + } + +private: + struct offer_test::service_info service_info_; + std::shared_ptr<vsomeip::application> app_; + std::uint32_t counter_; + + bool wait_until_registered_; + std::mutex mutex_; + std::condition_variable condition_; + std::thread offer_thread_; + + bool shutdown_method_called_; +}; + +TEST(someip_offer_test, notify_increasing_counter) +{ + offer_test_service its_sample(offer_test::service); +} + + +#ifndef WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if(argc < 2) { + std::cerr << "Please specify a service number, like: " << argv[0] << " 2" << std::endl; + return 1; + } + + service_number = std::string(argv[1]); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/offer_tests/offer_test_service_external.cpp b/test/offer_tests/offer_test_service_external.cpp new file mode 100644 index 0000000..d14103f --- /dev/null +++ b/test/offer_tests/offer_test_service_external.cpp @@ -0,0 +1,149 @@ +// Copyright (C) 2014-2016 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// 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 <chrono> +#include <condition_variable> +#include <iomanip> +#include <iostream> +#include <sstream> +#include <thread> +#include <map> +#include <algorithm> + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> +#include "../../implementation/logging/include/logger.hpp" + +#include "offer_test_globals.hpp" + +static std::string service_number; + +class offer_test_service { +public: + offer_test_service(struct offer_test::service_info _service_info) : + service_info_(_service_info), + // service with number 1 uses "vsomeipd" as application name + // this way the same json file can be reused for all local tests + // including the ones with vsomeipd + app_(vsomeip::runtime::get()->create_application( + (service_number == "1") ? "vsomeipd" : + "offer_test_service" + service_number)), + wait_until_registered_(true), + wait_until_service_available_(true), + offer_thread_(std::bind(&offer_test_service::run, this)) { + app_->init(); + app_->register_state_handler( + std::bind(&offer_test_service::on_state, this, + std::placeholders::_1)); + + app_->register_availability_handler(service_info_.service_id, + service_info_.instance_id, + std::bind(&offer_test_service::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + app_->request_service(service_info_.service_id, + service_info_.instance_id); + app_->start(); + } + + ~offer_test_service() { + offer_thread_.join(); + } + + void offer() { + app_->offer_service(service_info_.service_id, service_info_.instance_id); + // this is allowed + app_->offer_service(service_info_.service_id, service_info_.instance_id); + // this is not allowed and will be rejected + app_->offer_service(service_info_.service_id, service_info_.instance_id, 33, 4711); + } + + void on_state(vsomeip::state_type_e _state) { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? + "registered." : "deregistered."); + + if (_state == vsomeip::state_type_e::ST_REGISTERED) { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_until_registered_ = false; + condition_.notify_one(); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + VSOMEIP_INFO << "Service [" << std::setw(4) + << std::setfill('0') << std::hex << _service << "." << _instance + << "] is " << (_is_available ? "available":"not available") << "."; + std::lock_guard<std::mutex> its_lock(mutex_); + if(_is_available) { + wait_until_service_available_ = false; + condition_.notify_one(); + } else { + wait_until_service_available_ = true; + condition_.notify_one(); + } + } + + void run() { + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Running"; + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_until_registered_) { + condition_.wait(its_lock); + } + + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Offering"; + offer(); + + while(wait_until_service_available_) { + condition_.wait(its_lock); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Calling stop method"; + std::shared_ptr<vsomeip::message> msg(vsomeip::runtime::get()->create_request()); + msg->set_service(service_info_.service_id); + msg->set_instance(service_info_.instance_id); + msg->set_method(service_info_.shutdown_method_id); + msg->set_message_type(vsomeip::message_type_e::MT_REQUEST_NO_RETURN); + app_->send(msg); + std::this_thread::sleep_for(std::chrono::seconds(2)); + app_->stop(); + } + +private: + struct offer_test::service_info service_info_; + std::shared_ptr<vsomeip::application> app_; + + bool wait_until_registered_; + bool wait_until_service_available_; + std::mutex mutex_; + std::condition_variable condition_; + std::thread offer_thread_; +}; + +TEST(someip_offer_test, notify_increasing_counter) +{ + offer_test_service its_sample(offer_test::service); +} + + +#ifndef WIN32 +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + if(argc < 2) { + std::cerr << "Please specify a service number, like: " << argv[0] << " 2" << std::endl; + return 1; + } + + service_number = std::string(argv[1]); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/payload_tests/payload_test_client.cpp b/test/payload_tests/payload_test_client.cpp index bea774e..60529fe 100644 --- a/test/payload_tests/payload_test_client.cpp +++ b/test/payload_tests/payload_test_client.cpp @@ -131,7 +131,6 @@ void payload_test_client::on_message(const std::shared_ptr<vsomeip::message>& _r ASSERT_EQ(_response->get_service(), vsomeip_test::TEST_SERVICE_SERVICE_ID); ASSERT_EQ(_response->get_instance(), vsomeip_test::TEST_SERVICE_INSTANCE_ID); - ASSERT_EQ(_response->get_client(), vsomeip_test::TEST_CLIENT_CLIENT_ID); if(call_service_sync_) { @@ -217,10 +216,10 @@ void payload_test_client::run() blocked_ = false; stop(); - std::thread t1([](){ usleep(1000000 * 5);}); + std::thread t1([](){ std::this_thread::sleep_for(std::chrono::microseconds(1000000 * 5));}); t1.join(); app_->stop(); - std::thread t([](){ usleep(1000000 * 5);}); + std::thread t([](){ std::this_thread::sleep_for(std::chrono::microseconds(1000000 * 5));}); t.join(); } diff --git a/test/payload_tests/payload_test_service.cpp b/test/payload_tests/payload_test_service.cpp index 90fd476..6773743 100644 --- a/test/payload_tests/payload_test_service.cpp +++ b/test/payload_tests/payload_test_service.cpp @@ -115,9 +115,6 @@ void payload_test_service::on_message(const std::shared_ptr<vsomeip::message>& _ // TR_SOMEIP_00055 ASSERT_EQ(_request->get_message_type(), vsomeip::message_type_e::MT_REQUEST); - // make sure the message was sent from the service - ASSERT_EQ(_request->get_client(), vsomeip_test::TEST_CLIENT_CLIENT_ID); - if (check_payload) { std::shared_ptr<vsomeip::payload> pl = _request->get_payload(); vsomeip::byte_t* pl_ptr = pl->get_data(); diff --git a/test/readme.txt b/test/readme.txt index 2e8c240..9e127b4 100644 --- a/test/readme.txt +++ b/test/readme.txt @@ -390,3 +390,124 @@ complete system while doing 890 methodcalls. Automatic start from the build directory (example): ctest -V -R cpu_load_test + + +Initial event tests +---------------------- +This tests tests initial event mechanism over two nodes with multiple services +on both nodes. + +The test setup is as followed: +* There are six services offering one event each. +* Three of the services run on node 1. +* Three of the services run on node 2. +* All of the services initially set their event to their service id and notify + once +* On each node there are 20 client applications which subscribe to all of the + services events which are started at different times +* Each client waits until it received one notification (the initial one) from + all services and then exits. +* If all clients exited, the services are killed as well + +Automatic start from the build directory (example): + +ctest -V -R initial_event_test_diff_client_ids_diff_ports_udp + +Manual start from sub folder test of build directory: +./initial_event_test_master_starter.sh UDP initial_event_test_diff_client_ids_diff_ports_master.json + +There are multiple versions of this test which differ in the used subscription +method and port setup (use ctest -N to see all). For manual start the desired +description method has to be passed to the starter script as first parameter. + +Offer tests +----------- +This tests test various cases of offering a service and error recovery +after an application became unresponsive + +* Rejecting offer of service instance whose hosting application is + still alive. +* Rejecting offer of service instance whose hosting application is + still alive with daemon +* Accepting offer of service instance whose hosting application + crashed with (send SIGKILL) +* Accepting offer of service instance whose hosting application became + unresponsive (SIGSTOP) +* Rejecting offers for which there is already a pending offer +* Rejecting remote offer for which there is already a local offer +* Rejecting a local offer for which there is already a remote offer + +Automatic start from the build directory (example): + +ctest -V -R offer_tests + +Manual start from sub folder test of build directory: +./offer_test_local_starter +./offer_test_external_master_starter.sh + +Tests in detail: +Rejecting offer of service instance whose hosting application is still +alive: +* start application which offers service +* start client which continuously exchanges messages with the service +* start application which offers the same service again -> should be + rejected and an error message should be printed. +* Message exchange with client application should not be interrupted. + +Rejecting offer of service instance whose hosting application is still +alive with daemon +* start daemon (needed as he has to ping the offering client) +* start application which offers service +* start client which continuously exchanges messages with the service +* start application which offers the same service again -> should be + rejected and an error message should be printed. +* Message exchange with client application should not be interrupted. + +Accepting offer of service instance whose hosting application crashed +with (send SIGKILL) +* start daemon +* start application which offers service +* start client which exchanges messages with the service +* kill application with SIGKILL +* start application which offers the same service again -> should be + accepted. +* start another client which exchanges messages with the service +* Client should now communicate with new offerer. + +Accepting offer of service instance whose hosting application became +unresponsive (SIGSTOP) +* start daemon +* start application which offers service +* Send a SIGSTOP to the service to make it unresponsive +* start application which offers the same service again -> should be + marked as PENDING_OFFER and a ping should be sent to the paused + application. +* After the timeout passed the new offer should be accepted. +* start client which exchanges messages with the service +* Client should now communicate with new offerer. + +Rejecting offers for which there is already a pending offer +* start daemon +* start application which offers service +* Send a SIGSTOP to the service to make it unresponsive +* start application which offers the same service again -> should be + marked as PENDING_OFFER and a ping should be sent to the paused + application. +* start application which offers the same service again -> should be + rejected as there is already a PENDING_OFFER pending. +* After the timeout passed the new offer should be accepted. +* start client which exchanges messages with the service +* Client should now communicate with new offerer. + +Rejecting a local offer for which there is already a remote offer: +* start daemon +* start application which offers service +* start daemon remotely +* start same application which offers the same service again remotely + -> should be rejected as there is already a service instance + running in the network + +Rejecting remote offer for which there is already a local offer +* start application which offers service +* send SD message trying to offer the same service instance as already + offered locally from a remote host -> should be rejected diff --git a/test/routing_tests/external_local_routing_test_service.cpp b/test/routing_tests/external_local_routing_test_service.cpp index 745435f..e8d8891 100644 --- a/test/routing_tests/external_local_routing_test_service.cpp +++ b/test/routing_tests/external_local_routing_test_service.cpp @@ -159,7 +159,7 @@ void external_local_routing_test_service::run() condition_.wait(its_lock); } - std::thread t2([](){ usleep(1000000 * 2);}); + std::thread t2([](){ std::this_thread::sleep_for(std::chrono::microseconds(1000000 * 2));}); t2.join(); app_->stop(); } diff --git a/test/routing_tests/local_routing_test_client.json b/test/routing_tests/local_routing_test_client.json index a5991c2..4509768 100644 --- a/test/routing_tests/local_routing_test_client.json +++ b/test/routing_tests/local_routing_test_client.json @@ -26,7 +26,7 @@ [ ], - "routing" : "local_routing_test_service", + "routing" : "vsomeipd", "service-discovery" : { "enable" : "false", diff --git a/test/routing_tests/local_routing_test_service.cpp b/test/routing_tests/local_routing_test_service.cpp index 90e7eed..1f879dc 100644 --- a/test/routing_tests/local_routing_test_service.cpp +++ b/test/routing_tests/local_routing_test_service.cpp @@ -104,9 +104,6 @@ void local_routing_test_service::on_message(const std::shared_ptr<vsomeip::messa // TR_SOMEIP_00055 ASSERT_EQ(_request->get_message_type(), vsomeip::message_type_e::MT_REQUEST); - // make sure the message was sent from the service - ASSERT_EQ(_request->get_client(), vsomeip_test::TEST_CLIENT_CLIENT_ID); - // check the session id. ASSERT_EQ(_request->get_session(), static_cast<vsomeip::session_t>(number_of_received_messages_)); @@ -141,7 +138,7 @@ void local_routing_test_service::run() while (!blocked_) condition_.wait(its_lock); - std::thread t2([](){ usleep(1000000 * 5);}); + std::thread t2([](){ std::this_thread::sleep_for(std::chrono::microseconds(1000000 * 5));}); t2.join(); app_->stop(); } diff --git a/test/routing_tests/local_routing_test_service.json b/test/routing_tests/local_routing_test_service.json index 528074f..edd9be6 100644 --- a/test/routing_tests/local_routing_test_service.json +++ b/test/routing_tests/local_routing_test_service.json @@ -29,7 +29,7 @@ } ], - "routing" : "local_routing_test_service", + "routing" : "vsomeipd", "service-discovery" : { "enable" : "false", diff --git a/test/subscribe_notify_one_tests/subscribe_notify_one_test_service.cpp b/test/subscribe_notify_one_tests/subscribe_notify_one_test_service.cpp index 744a322..9641690 100644 --- a/test/subscribe_notify_one_tests/subscribe_notify_one_test_service.cpp +++ b/test/subscribe_notify_one_tests/subscribe_notify_one_test_service.cpp @@ -34,7 +34,6 @@ public: offer_thread_(std::bind(&subscribe_notify_one_test_service::run, this)), wait_for_stop_(true), stop_thread_(std::bind(&subscribe_notify_one_test_service::wait_for_stop, this)), - //number_of_subscribers_(0), wait_for_notify_(true), notify_thread_(std::bind(&subscribe_notify_one_test_service::notify_one, this)) { app_->init(); @@ -90,7 +89,6 @@ public: ~subscribe_notify_one_test_service() { offer_thread_.join(); stop_thread_.join(); - notify_thread_.join(); } void offer() { @@ -120,7 +118,8 @@ public: if(its_service != other_services_available_.end()) { if(its_service->second != _is_available) { its_service->second = true; - VSOMEIP_INFO << "Service [" + VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex + << service_info_.service_id << "] Service [" << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance << "] is available."; @@ -140,11 +139,8 @@ public: } bool on_subscription(vsomeip::client_t _client, bool _subscribed) { - (void)(_client); - //_subscribed ? number_of_subscribers_++ : number_of_subscribers_--; // check if all other services have subscribed: - // -1 for placeholder in array - + // -1 for placeholder in array and -1 for the service itself if (subscribers_.size() == subscribe_notify_one_test::service_infos.size() - 2) { return true; } @@ -160,10 +156,6 @@ public: << " subscribed, now have " << std::dec << subscribers_.size() << " subscribers" ; - VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex - << service_info_.service_id << "] " << "Client: " << _client - << " subscribed, now have " << std::dec << subscribers_.size() - << " subscribers" ; if(subscribers_.size() == subscribe_notify_one_test::service_infos.size() - 2) { // notify the notify thread to start sending out notifications @@ -359,6 +351,9 @@ public: << service_info_.service_id << "] Received notifications from all other services, going down"; + // wait until all notifications have been sent out + notify_thread_.join(); + // let offer thread exit { std::lock_guard<std::mutex> its_lock(mutex_); @@ -389,7 +384,6 @@ private: std::condition_variable stop_condition_; std::thread stop_thread_; - //std::uint32_t number_of_subscribers_; bool wait_for_notify_; std::mutex notify_mutex_; std::condition_variable notify_condition_; diff --git a/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_master.json.in b/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_master.json.in new file mode 100644 index 0000000..9c64a13 --- /dev/null +++ b/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_master.json.in @@ -0,0 +1,54 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30001", + "reliable": + { + "port":"40001", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0001", + "unreliable":"30002", + "reliable": + { + "port":"40002", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x3333", + "instance":"0x0001", + "unreliable":"30003", + "reliable": + { + "port":"40003", + "enable-magic-cookies":"false" + } + } + ], + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_slave.json.in b/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_slave.json.in new file mode 100644 index 0000000..2d62dbe --- /dev/null +++ b/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_autoconfig_slave.json.in @@ -0,0 +1,54 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "services": + [ + { + "service":"0x4444", + "instance":"0x0001", + "unreliable":"30004", + "reliable": + { + "port":"40004", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x5555", + "instance":"0x0001", + "unreliable":"30005", + "reliable": + { + "port":"40005", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x6666", + "instance":"0x0001", + "unreliable":"30006", + "reliable": + { + "port":"40006", + "enable-magic-cookies":"false" + } + } + ], + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_master.json.in b/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_master.json.in new file mode 100644 index 0000000..38dadb8 --- /dev/null +++ b/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_master.json.in @@ -0,0 +1,70 @@ +{ + "unicast":"@TEST_IP_MASTER@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"subscribe_notify_test_service_one", + "id":"0x1111" + }, + { + "name":"subscribe_notify_test_service_two", + "id":"0x2222" + }, + { + "name":"subscribe_notify_test_service_three", + "id":"0x3333" + } + ], + "services": + [ + { + "service":"0x1111", + "instance":"0x0001", + "unreliable":"30001", + "reliable": + { + "port":"40001", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x1111", + "instance":"0x0002", + "unreliable":"30002", + "reliable": + { + "port":"40002", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x1111", + "instance":"0x0003", + "unreliable":"30003", + "reliable": + { + "port":"40003", + "enable-magic-cookies":"false" + } + } + ], + "routing":"subscribe_notify_test_service_one", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_slave.json.in b/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_slave.json.in new file mode 100644 index 0000000..6abc1e3 --- /dev/null +++ b/test/subscribe_notify_tests/conf/subscribe_notify_test_diff_client_ids_diff_ports_same_service_id_slave.json.in @@ -0,0 +1,70 @@ +{ + "unicast":"@TEST_IP_SLAVE@", + "logging": + { + "level":"info", + "console":"true", + "file": + { + "enable":"false", + "path":"/tmp/vsomeip.log" + }, + "dlt":"false" + }, + "applications": + [ + { + "name":"subscribe_notify_test_service_four", + "id":"0x4444" + }, + { + "name":"subscribe_notify_test_service_five", + "id":"0x5555" + }, + { + "name":"subscribe_notify_test_service_six", + "id":"0x6666" + } + ], + "services": + [ + { + "service":"0x2222", + "instance":"0x0001", + "unreliable":"30004", + "reliable": + { + "port":"40004", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0002", + "unreliable":"30005", + "reliable": + { + "port":"40005", + "enable-magic-cookies":"false" + } + }, + { + "service":"0x2222", + "instance":"0x0003", + "unreliable":"30006", + "reliable": + { + "port":"40006", + "enable-magic-cookies":"false" + } + } + ], + "routing":"subscribe_notify_test_service_four", + "service-discovery": + { + "enable":"true", + "multicast":"224.0.0.1", + "port":"30490", + "protocol":"udp" + } +}
\ No newline at end of file diff --git a/test/subscribe_notify_tests/subscribe_notify_test_globals.hpp b/test/subscribe_notify_tests/subscribe_notify_test_globals.hpp index 0bf42d5..6e24a20 100644 --- a/test/subscribe_notify_tests/subscribe_notify_test_globals.hpp +++ b/test/subscribe_notify_tests/subscribe_notify_test_globals.hpp @@ -29,6 +29,19 @@ static constexpr std::array<service_info, 7> service_infos = {{ { 0x6666, 0x1, 0x6666, 0x6666, 0x6000 } }}; +static constexpr std::array<service_info, 7> service_infos_same_service_id = {{ + // placeholder to be consistent w/ client ids, service ids, app names + { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }, + // node 1 + { 0x1111, 0x1, 0x1111, 0x1111, 0x1000 }, + { 0x1111, 0x2, 0x2222, 0x2222, 0x2000 }, + { 0x1111, 0x3, 0x3333, 0x3333, 0x3000 }, + // node 2 + { 0x2222, 0x1, 0x4444, 0x4444, 0x4000 }, + { 0x2222, 0x2, 0x5555, 0x5555, 0x5000 }, + { 0x2222, 0x3, 0x6666, 0x6666, 0x6000 } +}}; + static constexpr int notifications_to_send = 10; } diff --git a/test/subscribe_notify_tests/subscribe_notify_test_master_starter.sh b/test/subscribe_notify_tests/subscribe_notify_test_master_starter.sh index 0d0f920..bbb48ea 100755 --- a/test/subscribe_notify_tests/subscribe_notify_test_master_starter.sh +++ b/test/subscribe_notify_tests/subscribe_notify_test_master_starter.sh @@ -18,6 +18,7 @@ then echo " [TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" echo "Please pass a json file to this script." echo "For example: $0 UDP subscribe_notify_test_diff_client_ids_diff_ports_master.json" + echo "To use the same service id but different instances on the node pass SAME_SERVICE_ID as third parameter" exit 1 fi @@ -50,15 +51,15 @@ FAIL=0 # Start the services export VSOMEIP_APPLICATION_NAME=subscribe_notify_test_service_one export VSOMEIP_CONFIGURATION=$2 -./subscribe_notify_test_service 1 $1 & +./subscribe_notify_test_service 1 $1 $3 & export VSOMEIP_APPLICATION_NAME=subscribe_notify_test_service_two export VSOMEIP_CONFIGURATION=$2 -./subscribe_notify_test_service 2 $1 & +./subscribe_notify_test_service 2 $1 $3 & export VSOMEIP_APPLICATION_NAME=subscribe_notify_test_service_three export VSOMEIP_CONFIGURATION=$2 -./subscribe_notify_test_service 3 $1 & +./subscribe_notify_test_service 3 $1 $3 & sleep 1 @@ -66,7 +67,7 @@ cat <<End-of-message ******************************************************************************* ******************************************************************************* ** Please now run: -** subscribe_notify_test_slave_starter.sh $1 $CLIENT_JSON_FILE +** subscribe_notify_test_slave_starter.sh $1 $CLIENT_JSON_FILE $3 ** from an external host to successfully complete this test. ** ** You probably will need to adapt the 'unicast' settings in diff --git a/test/subscribe_notify_tests/subscribe_notify_test_service.cpp b/test/subscribe_notify_tests/subscribe_notify_test_service.cpp index 9c967ef..a07f00c 100644 --- a/test/subscribe_notify_tests/subscribe_notify_test_service.cpp +++ b/test/subscribe_notify_tests/subscribe_notify_test_service.cpp @@ -23,8 +23,10 @@ class subscribe_notify_test_service { public: subscribe_notify_test_service(struct subscribe_notify_test::service_info _service_info, - vsomeip::subscription_type_e _subscription_type) : + vsomeip::subscription_type_e _subscription_type, + std::array<subscribe_notify_test::service_info, 7> _service_infos) : service_info_(_service_info), + service_infos_(_service_infos), subscription_type_(_subscription_type), app_(vsomeip::runtime::get()->create_application()), wait_until_registered_(true), @@ -33,7 +35,6 @@ public: offer_thread_(std::bind(&subscribe_notify_test_service::run, this)), wait_for_stop_(true), stop_thread_(std::bind(&subscribe_notify_test_service::wait_for_stop, this)), - number_of_subscribers_(0), wait_for_notify_(true), notify_thread_(std::bind(&subscribe_notify_test_service::notify, this)) { app_->init(); @@ -45,7 +46,7 @@ public: std::bind(&subscribe_notify_test_service::on_request, this, std::placeholders::_1)); app_->register_message_handler(vsomeip::ANY_SERVICE, - service_info_.instance_id, vsomeip::ANY_METHOD, + vsomeip::ANY_INSTANCE, vsomeip::ANY_METHOD, std::bind(&subscribe_notify_test_service::on_message, this, std::placeholders::_1)); @@ -57,12 +58,13 @@ public: // register availability for all other services and request their event. - for(const auto& i : subscribe_notify_test::service_infos) { + for(const auto& i : service_infos_) { if ((i.service_id == service_info_.service_id && i.instance_id == service_info_.instance_id) || (i.service_id == 0xFFFF && i.instance_id == 0xFFFF)) { continue; } + app_->request_service(i.service_id, i.instance_id); app_->register_availability_handler(i.service_id, i.instance_id, std::bind(&subscribe_notify_test_service::on_availability, this, std::placeholders::_1, std::placeholders::_2, @@ -89,7 +91,6 @@ public: ~subscribe_notify_test_service() { offer_thread_.join(); stop_thread_.join(); - notify_thread_.join(); } void offer() { @@ -140,22 +141,30 @@ public: } bool on_subscription(vsomeip::client_t _client, bool _subscribed) { - (void)(_client); - _subscribed ? number_of_subscribers_++ : number_of_subscribers_--; + static bool notified(false); + if (_subscribed) { + subscribers_.insert(_client); + } else { + subscribers_.erase(_client); + } + VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex - << service_info_.service_id << "] " << "Client: " << _client - << " subscribed, now have " << std::dec << number_of_subscribers_ + << service_info_.service_id << "] " << "Client: " + << std::setw(4) << std::setfill('0') << std::hex << _client + << (_subscribed ? " subscribed" : " unsubscribed") + << ", now have " << std::dec << subscribers_.size() << " subscribers" ; // check if all other services have subscribed: // -1 for placeholder in array // divide by two because we only receive once subscription per remote node // no matter how many clients subscribed to this eventgroup on the remote node - if(number_of_subscribers_ == (subscribe_notify_test::service_infos.size() - 1) / 2 ) + if(!notified && subscribers_.size() == (service_infos_.size() - 1) / 2 ) { // notify the notify thread to start sending out notifications std::lock_guard<std::mutex> its_lock(notify_mutex_); wait_for_notify_ = false; notify_condition_.notify_one(); + notified = true; } return true; } @@ -238,8 +247,8 @@ public: } } - if( received_twice == (subscribe_notify_test::service_infos.size() - 1) / 2 - && received_normal == (subscribe_notify_test::service_infos.size() - 1) / 2 - 1) { + if( received_twice == (service_infos_.size() - 1) / 2 + && received_normal == (service_infos_.size() - 1) / 2 - 1) { // routing manager stub receives the notification // - twice from external nodes // - and normal from all internal nodes @@ -273,7 +282,7 @@ public: VSOMEIP_DEBUG << "[" << std::setw(4) << std::setfill('0') << std::hex << service_info_.service_id << "] Subscribing"; // subscribe to events of other services - for(const subscribe_notify_test::service_info& i: subscribe_notify_test::service_infos) { + for(const subscribe_notify_test::service_info& i: service_infos_) { if ((i.service_id == service_info_.service_id && i.instance_id == service_info_.instance_id) || (i.service_id == 0xFFFF && i.instance_id == 0xFFFF)) { @@ -304,7 +313,7 @@ public: // successfully subscribed as we only receive once subscription per // remote node no matter how many clients subscribed to this eventgroup // on the remote node - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex << service_info_.service_id << "] Starting to notify"; @@ -322,7 +331,7 @@ public: << service_info_.service_id << "] Notifying: " << i+1; app_->notify(service_info_.service_id, service_info_.instance_id, service_info_.event_id, its_payload); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } @@ -331,6 +340,10 @@ public: while (wait_for_stop_) { stop_condition_.wait(its_lock); } + + // wait until all notifications have been sent out + notify_thread_.join(); + VSOMEIP_INFO << "[" << std::setw(4) << std::setfill('0') << std::hex << service_info_.service_id << "] Received notifications from all other services, going down"; @@ -342,12 +355,13 @@ public: condition_.notify_one(); } - std::this_thread::sleep_for(std::chrono::seconds(6)); + std::this_thread::sleep_for(std::chrono::seconds(1)); app_->stop(); } private: subscribe_notify_test::service_info service_info_; + std::array<subscribe_notify_test::service_info, 7> service_infos_; vsomeip::subscription_type_e subscription_type_; std::shared_ptr<vsomeip::application> app_; std::map<std::pair<vsomeip::service_t, vsomeip::instance_t>, bool> other_services_available_; @@ -365,7 +379,7 @@ private: std::condition_variable stop_condition_; std::thread stop_thread_; - std::uint32_t number_of_subscribers_; + std::set<vsomeip::client_t> subscribers_; bool wait_for_notify_; std::mutex notify_mutex_; std::condition_variable notify_condition_; @@ -374,11 +388,20 @@ private: static int service_number; static vsomeip::subscription_type_e subscription_type; +static bool use_same_service_id; TEST(someip_subscribe_notify_test, send_ten_notifications_to_service) { - subscribe_notify_test_service its_sample( - subscribe_notify_test::service_infos[service_number], subscription_type); + if(use_same_service_id) { + subscribe_notify_test_service its_sample( + subscribe_notify_test::service_infos_same_service_id[service_number], + subscription_type, + subscribe_notify_test::service_infos_same_service_id); + } else { + subscribe_notify_test_service its_sample( + subscribe_notify_test::service_infos[service_number], subscription_type, + subscribe_notify_test::service_infos); + } } #ifndef WIN32 @@ -386,10 +409,11 @@ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); if(argc < 3) { - std::cerr << "Please specify a service number and subscription type, like: " << argv[0] << " 2 UDP" << std::endl; + std::cerr << "Please specify a service number and subscription type, like: " << argv[0] << " 2 UDP SAME_SERVICE_ID" << std::endl; std::cerr << "Valid service numbers are in the range of [1,6]" << std::endl; std::cerr << "Valid subscription types include:" << std::endl; std::cerr << "[TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" << std::endl; + std::cerr << "If SAME_SERVICE_ID is specified as third parameter the test is run w/ multiple instances of the same service" << std::endl; return 1; } @@ -411,6 +435,12 @@ int main(int argc, char** argv) std::cerr << "[TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" << std::endl; return 1; } + + if (argc >= 4 && std::string("SAME_SERVICE_ID") == std::string(argv[3])) { + use_same_service_id = true; + } else { + use_same_service_id = false; + } return RUN_ALL_TESTS(); } #endif diff --git a/test/subscribe_notify_tests/subscribe_notify_test_slave_starter.sh b/test/subscribe_notify_tests/subscribe_notify_test_slave_starter.sh index 887e2e4..b37a781 100755 --- a/test/subscribe_notify_tests/subscribe_notify_test_slave_starter.sh +++ b/test/subscribe_notify_tests/subscribe_notify_test_slave_starter.sh @@ -18,6 +18,7 @@ then echo " [TCP_AND_UDP, PREFER_UDP, PREFER_TCP, UDP, TCP]" echo "Please pass a json file to this script." echo "For example: $0 UDP subscribe_notify_test_diff_client_ids_diff_ports_slave.json" + echo "To use the same service id but different instances on the node pass SAME_SERVICE_ID as third parameter" exit 1 fi @@ -45,15 +46,15 @@ FAIL=0 # Start the services export VSOMEIP_APPLICATION_NAME=subscribe_notify_test_service_four export VSOMEIP_CONFIGURATION=$2 -./subscribe_notify_test_service 4 $1 & +./subscribe_notify_test_service 4 $1 $3 & export VSOMEIP_APPLICATION_NAME=subscribe_notify_test_service_five export VSOMEIP_CONFIGURATION=$2 -./subscribe_notify_test_service 5 $1 & +./subscribe_notify_test_service 5 $1 $3 & export VSOMEIP_APPLICATION_NAME=subscribe_notify_test_service_six export VSOMEIP_CONFIGURATION=$2 -./subscribe_notify_test_service 6 $1 & +./subscribe_notify_test_service 6 $1 $3 & # Wait until all applications are finished for job in $(jobs -p) diff --git a/tools/vsomeip_ctrl.cpp b/tools/vsomeip_ctrl.cpp index 84faf00..947caf8 100644 --- a/tools/vsomeip_ctrl.cpp +++ b/tools/vsomeip_ctrl.cpp @@ -5,7 +5,7 @@ #include <vsomeip/vsomeip.hpp> #include "../implementation/configuration/include/internal.hpp" -#include "../../implementation/logging/include/logger.hpp" +#include "../implementation/logging/include/logger.hpp" #include "../implementation/service_discovery/include/constants.hpp" #include "../implementation/utility/include/byteorder.hpp" @@ -91,7 +91,7 @@ public: "registered." : "deregistered."); if (_state == vsomeip::state_type_e::ST_REGISTERED) { - app_->request_service(service_id_, vsomeip::ANY_INSTANCE); + app_->request_service(service_id_, instance_); } } @@ -112,23 +112,25 @@ public: << "." << std::setw(4) << std::setfill('0') << std::hex << _response->get_instance() << "]:"; VSOMEIP_INFO << "########## begin message"; - VSOMEIP_INFO << std::setw(4) << std::setfill('0') << std::hex - << _response->get_service() << std::setw(4) << std::setfill('0') - << std::hex << _response->get_method() + VSOMEIP_INFO << std::hex << std::setw(4) << std::setfill('0') + << _response->get_service() + << std::hex << std::setw(4) << std::setfill('0') + << _response->get_method() << " # service id / instance id"; - VSOMEIP_INFO << std::setw(8) << std::setfill('0') << std::hex + VSOMEIP_INFO << std::hex << std::setw(8) << std::setfill('0') << _response->get_length() << " # length"; - VSOMEIP_INFO << std::setw(4) << std::setfill('0') << std::hex - << _response->get_client() << std::setw(4) << std::setfill('0') - << std::hex << _response->get_session() + VSOMEIP_INFO << std::hex << std::setw(4) << std::setfill('0') + << _response->get_client() + << std::hex << std::setw(4) << std::setfill('0') + << _response->get_session() << " # client id / session id"; - VSOMEIP_INFO << std::setw(2) << std::setfill('0') << std::hex + VSOMEIP_INFO << std::hex << std::setw(2) << std::setfill('0') << static_cast<std::uint16_t>(_response->get_protocol_version()) - << std::setw(2) << std::setfill('0') << std::hex + << std::hex << std::setw(2) << std::setfill('0') << static_cast<std::uint16_t>(_response->get_interface_version()) - << std::setw(2) << std::setfill('0') << std::hex + << std::hex << std::setw(2) << std::setfill('0') << static_cast<std::uint16_t>(_response->get_message_type()) - << std::setw(2) << std::setfill('0') << std::hex + << std::hex << std::setw(2) << std::setfill('0') << static_cast<std::uint16_t>(_response->get_return_code()) << " # protocol version / interface version / " << "message type / return code"; |