diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index f8ad7a506a..a31850bb7a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -118,7 +118,7 @@ body: Platform : x64 ------------------------- CONFIGURATION -------------------------- - Default INI : C:\ProgramData\eCAL\ecal.ini + Default YAML : C:\ProgramData\eCAL\ecal.yaml ------------------------- NETWORK -------------------------------- Host name : FRLBJRXW diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 0910d05e63..4bdd2c21b2 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -11,32 +11,21 @@ jobs: runs-on: macos-13 steps: - - name: Downgrade Python version - uses: actions/setup-python@v4 - id: cp39 - with: - python-version: '3.9' + # XProtect can cause random failures if it decides that the DMG we create + # during the packaging phase is malware. + # See https://github.com/actions/runner-images/issues/7522 and https://github.com/servo/servo/pull/30779 + - name: Kill XProtectBehaviorService + run: | + echo Killing XProtect.; sudo pkill -9 XProtect >/dev/null || true; + + - name: Install latest Python3 via brew + run: brew install python3 || brew link --overwrite python@3.12 - - name: Install Qt - uses: jurplel/install-qt-action@v3 - with: - setup-python: false - version: '5.15.2' - target: 'desktop' + - name: Install latest Qt5 via brew + run: brew install qt@5 - name: Install Dependencies - run: brew install ninja doxygen graphviz protobuf hdf5@1.10 pkg-config - - - name: Install Cap’n Proto - run: | - mkdir "${{ runner.workspace }}/capnp" - cd "${{ runner.workspace }}/capnp" - curl -O https://capnproto.org/capnproto-c++-0.9.0.tar.gz - tar zxf capnproto-c++-0.9.0.tar.gz - cd capnproto-c++-0.9.0 - ./configure - make -j - sudo make install + run: brew install ninja doxygen graphviz protobuf hdf5@1.10 pkg-config capnp - name: Checkout uses: actions/checkout@v4 @@ -49,7 +38,7 @@ jobs: - name: Install Python requirements run: | - sudo pip3 install -r "$GITHUB_WORKSPACE/doc/requirements.txt" + sudo pip3 install -r "$GITHUB_WORKSPACE/doc/requirements.txt" --break-system-packages - name: CMake run: | @@ -84,20 +73,23 @@ jobs: -DECAL_THIRDPARTY_BUILD_RECYCLE=ON \ -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON \ -DECAL_THIRDPARTY_BUILD_QWT=ON \ - -DECAL_THIRDPARTY_BUILD_YAML-CPP=ON \ -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_PREFIX_PATH=/usr/local/opt/hdf5@1.10 \ + -DCMAKE_PREFIX_PATH="/usr/local/opt/hdf5@1.10;/usr/local/opt/qt@5" \ -DCMAKE_CXX_STANDARD=17 \ -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON \ -DPython_FIND_STRATEGY=LOCATION \ - -DPython_FIND_REGISTRY=NEVER - sudo mkdir /etc/ecal - sudo cp "$GITHUB_WORKSPACE/ecal/core/cfg/ecal.ini" /etc/ecal + -DPython_FIND_REGISTRY=NEVER shell: bash - name: Build Release run: cmake --build . --config Release working-directory: ${{ runner.workspace }}/_build + + - name: Create config path and copy ecal.yaml + run: | + sudo mkdir /etc/ecal + sudo cp "./ecal/core/cfg/gen/ecal.yaml" /etc/ecal + working-directory: ${{ runner.workspace }}/_build # - name: Build Documentation C # run: cmake --build . --target documentation_c diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index e86dbffe8c..483a83d74d 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -135,21 +135,24 @@ jobs: -DECAL_THIRDPARTY_BUILD_RECYCLE=ON \ -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON \ -DECAL_THIRDPARTY_BUILD_QWT=ON \ - -DECAL_THIRDPARTY_BUILD_YAML-CPP=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_SYSCONFDIR=/etc \ -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_INSTALL_LOCALSTATEDIR=/var \ -DCMAKE_INSTALL_LIBDIR=lib/x86_64-linux-gnu \ -DPython_FIND_VIRTUALENV=ONLY - - sudo mkdir /etc/ecal - sudo cp "$GITHUB_WORKSPACE/ecal/core/cfg/ecal.ini" /etc/ecal shell: bash - name: Build Release run: cmake --build . --parallel -- -k 0 working-directory: ${{ runner.workspace }}/_build + + - name: Create config path and copy ecal.yaml + run: | + sudo mkdir /etc/ecal + sudo cp "./ecal/core/cfg/gen/ecal.yaml" /etc/ecal + working-directory: ${{ runner.workspace }}/_build + shell: bash - name: Run Tests run: ctest -V diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 4fef21a949..7c9f2b411e 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -90,7 +90,6 @@ jobs: -DECAL_THIRDPARTY_BUILD_RECYCLE=ON ^ -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON ^ -DECAL_THIRDPARTY_BUILD_QWT=OFF ^ - -DECAL_THIRDPARTY_BUILD_YAML-CPP=OFF ^ -DECAL_THIRDPARTY_BUILD_UDPCAP=ON ^ -DBUILD_SHARED_LIBS=OFF ^ -DCMAKE_PREFIX_PATH="%ProgramFiles%/Cap'n Proto/lib/cmake/CapnProto" ^ @@ -131,14 +130,11 @@ jobs: -DECAL_THIRDPARTY_BUILD_RECYCLE=ON ^ -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON ^ -DECAL_THIRDPARTY_BUILD_QWT=ON ^ - -DECAL_THIRDPARTY_BUILD_YAML-CPP=ON ^ -DECAL_THIRDPARTY_BUILD_UDPCAP=ON ^ -DBUILD_SHARED_LIBS=OFF ^ -DCMAKE_PREFIX_PATH="%ProgramFiles%/Cap'n Proto/lib/cmake/CapnProto" ^ -DCMAKE_BUILD_TYPE=Release ^ - -DCPACK_PACK_WITH_INNOSETUP=ON - mkdir "%ALLUSERSPROFILE%\eCAL" - copy "%GITHUB_WORKSPACE%\ecal\core\cfg\ecal.ini" "%ALLUSERSPROFILE%\eCAL" + -DCPACK_PACK_WITH_INNOSETUP=ON shell: cmd - name: Build SDK @@ -149,6 +145,13 @@ jobs: run: cmake --build . --config Release working-directory: ${{ runner.workspace }}/_build/complete + - name: Create config path and copy ecal.yaml + run: | + mkdir "%ALLUSERSPROFILE%\eCAL" + copy ".\ecal\core\cfg\gen\ecal.yaml" "%ALLUSERSPROFILE%\eCAL" + working-directory: ${{ runner.workspace }}/_build/complete + shell: cmd + - name: Run Tests run: ctest -C Release -V working-directory: ${{ runner.workspace }}/_build/complete diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 6d5c4fee7c..9955278e1e 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -68,7 +68,6 @@ jobs: -DECAL_THIRDPARTY_BUILD_RECYCLE=ON \ -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON \ -DECAL_THIRDPARTY_BUILD_QWT=OFF \ - -DECAL_THIRDPARTY_BUILD_YAML-CPP=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_SYSCONFDIR=/etc \ -DCMAKE_INSTALL_PREFIX=/usr \ diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 28fde18a8e..56f1883f56 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -73,7 +73,6 @@ jobs: -DECAL_THIRDPARTY_BUILD_RECYCLE=ON \ -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON \ -DECAL_THIRDPARTY_BUILD_QWT=OFF \ - -DECAL_THIRDPARTY_BUILD_YAML-CPP=OFF \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_SYSCONFDIR=/etc \ -DCMAKE_INSTALL_PREFIX=/usr \ diff --git a/CMakeLists.txt b/CMakeLists.txt index b8f2bf1d18..4f6c1bb03e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,7 +103,7 @@ option(ECAL_USE_CLOCKLOCK_MUTEX "Use native mutex with monotonic set(ECAL_CORE_HAS_PROTOBUF ON) set(ECAL_CORE_BUILD_SAMPLES ${BUILD_SAMPLES}) set(ECAL_CORE_BUILD_TESTS ${BUILD_ECAL_TESTS}) -set(ECAL_CORE_CONFIG_INIFILE ON) +set(ECAL_CORE_CONFIGURATION ON) set(ECAL_CORE_COMMAND_LINE ON) set(ECAL_CORE_REGISTRATION ON) set(ECAL_CORE_MONITORING ON) diff --git a/CMakePresets.json b/CMakePresets.json index 50d83f4edc..2c42f0b6d8 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -50,13 +50,13 @@ "HAS_FTXUI": "OFF", "BUILD_APPS": "OFF", "BUILD_SAMPLES": "OFF", + "ECAL_CORE_CONFIGURATION": "OFF", "ECAL_INSTALL_SAMPLE_SOURCES": "OFF", "ECAL_THIRDPARTY_BUILD_FINEFTP": "OFF", "ECAL_THIRDPARTY_BUILD_FTXUI": "OFF", "ECAL_THIRDPARTY_BUILD_SPDLOG": "OFF", "ECAL_THIRDPARTY_BUILD_TERMCOLOR": "OFF", "ECAL_THIRDPARTY_BUILD_TINYXML2": "OFF", - "ECAL_THIRDPARTY_BUILD_YAML-CPP": "OFF", "ECAL_THIRDPARTY_BUILD_CURL": "OFF", "ECAL_THIRDPARTY_BUILD_HDF5": "OFF" } @@ -100,7 +100,6 @@ "ECAL_THIRDPARTY_BUILD_SPDLOG": "ON", "ECAL_THIRDPARTY_BUILD_TERMCOLOR": "ON", "ECAL_THIRDPARTY_BUILD_TINYXML2": "ON", - "ECAL_THIRDPARTY_BUILD_YAML-CPP": null, "ECAL_THIRDPARTY_BUILD_CURL": null, "ECAL_THIRDPARTY_BUILD_HDF5": null } diff --git a/app/meas_cutter/CMakeLists.txt b/app/meas_cutter/CMakeLists.txt index 4209e6c380..637b0783c8 100644 --- a/app/meas_cutter/CMakeLists.txt +++ b/app/meas_cutter/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,13 +22,8 @@ find_package(Threads REQUIRED) find_package(tclap REQUIRED) find_package(yaml-cpp REQUIRED) -#compatibility with yaml-cpp < 0.8.0 -if (NOT TARGET yaml-cpp::yaml-cpp AND TARGET yaml-cpp) - # ALIASing a imported non-global library requires CMake 3.18 so we do this - add_library(yaml-cpp::yaml-cpp INTERFACE IMPORTED) - target_link_libraries(yaml-cpp::yaml-cpp INTERFACE yaml-cpp) -endif() - +include(${ECAL_PROJECT_ROOT}/thirdparty/yaml-cpp/compatibility-yaml-cpp.cmake) +yaml_cpp_create_compatibility_targets() set(meas_cutter_src src/main.cpp diff --git a/app/mon/mon_cli/src/ecal_mon_cli.cpp b/app/mon/mon_cli/src/ecal_mon_cli.cpp index 39925f7f45..e27fd7ed20 100644 --- a/app/mon/mon_cli/src/ecal_mon_cli.cpp +++ b/app/mon/mon_cli/src/ecal_mon_cli.cpp @@ -353,7 +353,7 @@ void ProcProto(const std::string& topic_name, int msg_count) // get topic type eCAL::SDataTypeInformation topic_info; - eCAL::Util::GetTopicDataTypeInformation(topic_name, topic_info); + eCAL::Registration::GetTopicDataTypeInformation(topic_name, topic_info); if(topic_info.name.empty()) { std::cout << "could not get type name for topic " << topic_name << std::endl; diff --git a/app/mon/mon_gui/src/ecalmon.cpp b/app/mon/mon_gui/src/ecalmon.cpp index 2abed6fd10..e059e2975e 100644 --- a/app/mon/mon_gui/src/ecalmon.cpp +++ b/app/mon/mon_gui/src/ecalmon.cpp @@ -102,21 +102,21 @@ Ecalmon::Ecalmon(QWidget *parent) if (multicast_ttl <= 0) { - network_mode_widget_->setToolTip("ERROR: Network enabled but TTL is " + QString::number(multicast_ttl) + ". Change via ecal.ini"); + network_mode_widget_->setToolTip("ERROR: Network enabled but TTL is " + QString::number(multicast_ttl) + ". Change via ecal.yaml"); network_mode_widget_->setStyleSheet("background-color: rgb(255, 128, 128); color: black"); network_mode_warning_icon_->setVisible(true); } else { network_mode_widget_->setStyleSheet("background-color: rgb(80, 255, 120); color: black"); - network_mode_widget_->setToolTip("Change via ecal.ini"); + network_mode_widget_->setToolTip("Change via ecal.yaml"); } } else { network_mode_label_->setText("Network mode: Local"); network_mode_widget_->setStyleSheet("background-color: rgb(44, 148, 255); color: black"); - network_mode_widget_->setToolTip("Change via ecal.ini"); + network_mode_widget_->setToolTip("Change via ecal.yaml"); } error_label_ = new QLabel(this); diff --git a/app/mon/mon_gui/src/widgets/ecalmon_tree_widget/topic_widget.cpp b/app/mon/mon_gui/src/widgets/ecalmon_tree_widget/topic_widget.cpp index 5f2d2741f6..690df3d401 100644 --- a/app/mon/mon_gui/src/widgets/ecalmon_tree_widget/topic_widget.cpp +++ b/app/mon/mon_gui/src/widgets/ecalmon_tree_widget/topic_widget.cpp @@ -192,11 +192,11 @@ void TopicWidget::loadRegExpLists() include_string = QString::fromStdString(eCAL::Config::GetMonitoringFilterIncludeList()); //ini.GetValue(MON_SECTION_S, MON_FILTER_INCL_S); - // The ecal.ini defines a very strange regex format: A filter consists of + // The ecal.yaml defines a very strange regex format: A filter consists of // several regular expressions divided by "," or ";". Thus we have to // split the string into 'actual' regular expressions. This will break every // regular expression that properly uses a ",". We cannot do anything about - // that without changing the ecal.ini specification. + // that without changing the ecal.yaml specification. #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QList const exclude_string_list = exclude_string.split(QRegularExpression("[\\,,;]"), Qt::SplitBehaviorFlags::SkipEmptyParts); QList const include_string_list = include_string.split(QRegularExpression("[\\,,;]"), Qt::SplitBehaviorFlags::SkipEmptyParts); diff --git a/app/mon/mon_gui/src/widgets/models/topic_tree_item.cpp b/app/mon/mon_gui/src/widgets/models/topic_tree_item.cpp index eb2e68e00c..bcc172f34a 100644 --- a/app/mon/mon_gui/src/widgets/models/topic_tree_item.cpp +++ b/app/mon/mon_gui/src/widgets/models/topic_tree_item.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -95,7 +95,7 @@ QVariant TopicTreeItem::data(Columns column, Qt::ItemDataRole role) const if (!monitor_topic_name.empty()) { eCAL::SDataTypeInformation topic_info; - eCAL::Util::GetTopicDataTypeInformation(monitor_topic_name, topic_info); + eCAL::Registration::GetTopicDataTypeInformation(monitor_topic_name, topic_info); return topic_info.encoding.c_str(); } } @@ -115,7 +115,7 @@ QVariant TopicTreeItem::data(Columns column, Qt::ItemDataRole role) const if (!monitor_topic_name.empty()) { eCAL::SDataTypeInformation topic_info; - eCAL::Util::GetTopicDataTypeInformation(monitor_topic_name, topic_info); + eCAL::Registration::GetTopicDataTypeInformation(monitor_topic_name, topic_info); return topic_info.name.c_str(); } } @@ -209,7 +209,7 @@ QVariant TopicTreeItem::data(Columns column, Qt::ItemDataRole role) const for (const auto& layer : layer_pb) { QString this_layer_string; - if (layer.confirmed()) + if (layer.active()) { switch (layer.type()) { diff --git a/app/mon/mon_tui/src/model/monitor.hpp b/app/mon/mon_tui/src/model/monitor.hpp index 63170a3ab6..1edea9818b 100644 --- a/app/mon/mon_tui/src/model/monitor.hpp +++ b/app/mon/mon_tui/src/model/monitor.hpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -204,7 +204,7 @@ class MonitorModel topic.type_descriptor = std::move(*t.mutable_tdatatype()->mutable_desc()); for(auto &tl: t.tlayer()) { - if (tl.confirmed()) + if (tl.active()) { topic.transport_layers.emplace_back(TopicTransportLayer(tl.type())); } diff --git a/app/rec/rec_client_core/src/job/record_job.cpp b/app/rec/rec_client_core/src/job/record_job.cpp index b0715a568a..4016c70f08 100644 --- a/app/rec/rec_client_core/src/job/record_job.cpp +++ b/app/rec/rec_client_core/src/job/record_job.cpp @@ -164,7 +164,7 @@ namespace eCAL } } - // Copy ecal.ini + // Copy ecal.yaml #ifdef CopyFile #define CopyFile_6376c040f4f54106b205ef6ddbb2090a CopyFile #undef CopyFile @@ -174,28 +174,28 @@ namespace eCAL if (ecal_ini_original_path.empty()) { - const std::string error_message = "Error copying ecal.ini: The system information does not contain \"Default INI\" identifier."; + const std::string error_message = "Error copying ecal.yaml: The system information does not contain \"Default INI\" identifier."; info_ = { false, error_message }; EcalRecLogger::Instance()->error(error_message); } if (EcalUtils::Filesystem::IsFile(ecal_ini_original_path)) { - std::string ecal_ini_dest_path = EcalUtils::Filesystem::ToNativeSeperators(hostname_dir + "/ecal.ini"); - EcalRecLogger::Instance()->info("Copying ecal.ini from \"" + ecal_ini_original_path + "\" to \"" + ecal_ini_dest_path + "\""); + std::string ecal_ini_dest_path = EcalUtils::Filesystem::ToNativeSeperators(hostname_dir + "/ecal.yaml"); + EcalRecLogger::Instance()->info("Copying ecal.yaml from \"" + ecal_ini_original_path + "\" to \"" + ecal_ini_dest_path + "\""); if (!EcalUtils::Filesystem::CopyFile(ecal_ini_original_path, ecal_ini_dest_path, EcalUtils::Filesystem::OsStyle::Current)) { - const std::string error_message = "Error copying ecal.ini file to \"" + ecal_ini_dest_path + "\""; + const std::string error_message = "Error copying ecal.yaml file to \"" + ecal_ini_dest_path + "\""; info_ = { false, error_message }; EcalRecLogger::Instance()->error(error_message); } } else { - const std::string error_message = "Warning: ecal.ini does not exist and will not be copied."; + const std::string error_message = "Warning: ecal.yaml does not exist and will not be copied."; info_ = { false, error_message }; - EcalRecLogger::Instance()->warn("Warning: ecal.ini does not exist and will not be copied."); + EcalRecLogger::Instance()->warn("Warning: ecal.yaml does not exist and will not be copied."); } } #ifdef CopyFile_6376c040f4f54106b205ef6ddbb2090a diff --git a/build_win/win_make_cmake.bat b/build_win/win_make_cmake.bat index 535a40285d..d3d7c9d121 100644 --- a/build_win/win_make_cmake.bat +++ b/build_win/win_make_cmake.bat @@ -32,7 +32,6 @@ set CMAKE_OPTIONS_COMPLETE=-DCMAKE_INSTALL_PREFIX=_install ^ -DECAL_THIRDPARTY_BUILD_RECYCLE=ON ^ -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON ^ -DECAL_THIRDPARTY_BUILD_QWT=ON ^ --DECAL_THIRDPARTY_BUILD_YAML-CPP=ON ^ -DECAL_THIRDPARTY_BUILD_UDPCAP=ON ^ -DBUILD_SHARED_LIBS=OFF ^ -DCMAKE_BUILD_TYPE=Release ^ @@ -66,7 +65,6 @@ set CMAKE_OPTIONS_SDK=-DCMAKE_INSTALL_PREFIX=_install ^ -DECAL_THIRDPARTY_BUILD_RECYCLE=ON ^ -DECAL_THIRDPARTY_BUILD_TCP_PUBSUB=ON ^ -DECAL_THIRDPARTY_BUILD_QWT=OFF ^ --DECAL_THIRDPARTY_BUILD_YAML-CPP=OFF ^ -DECAL_THIRDPARTY_BUILD_UDPCAP=ON ^ -DBUILD_SHARED_LIBS=OFF ^ -DCMAKE_BUILD_TYPE=Debug ^ diff --git a/cpack/innosetup/ecal_setup.iss.in b/cpack/innosetup/ecal_setup.iss.in index 78ef6be139..84cd6117ea 100644 --- a/cpack/innosetup/ecal_setup.iss.in +++ b/cpack/innosetup/ecal_setup.iss.in @@ -1,6 +1,6 @@ ; ========================= eCAL LICENSE ================================= ; -; Copyright (C) 2016 - 2019 Continental Corporation +; Copyright (C) 2016 - 2024 Continental Corporation ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. @@ -75,9 +75,9 @@ Source: "{#ComponentStagingDir}\runtime\*"; DestDir: "{app}"; Fla Source: "{#ComponentStagingDir}\Unspecified\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: runtime Source: "{#ComponentStagingDir}\libraries\bin\*"; DestDir: "{app}\bin\"; Flags: ignoreversion recursesubdirs; Components: runtime -Source: "{#ComponentStagingDir}\configuration\cfg\ecal.ini"; DestDir: "{commonappdata}\eCAL\"; Tasks: not replaceconf; Flags: ignoreversion confirmoverwrite; Components: runtime -Source: "{#ComponentStagingDir}\configuration\cfg\ecal.ini"; DestDir: "{commonappdata}\eCAL\"; Tasks: replaceconf; Flags: ignoreversion; Components: runtime -Source: "{#ComponentStagingDir}\configuration\cfg\ecaltime.ini"; DestDir: "{commonappdata}\eCAL\"; Flags: ignoreversion; Components: runtime +Source: "{#ComponentStagingDir}\_configuration\cfg\ecal.yaml"; DestDir: "{commonappdata}\eCAL\"; Tasks: not replaceconf; Flags: ignoreversion confirmoverwrite; Components: runtime +Source: "{#ComponentStagingDir}\_configuration\cfg\ecal.yaml"; DestDir: "{commonappdata}\eCAL\"; Tasks: replaceconf; Flags: ignoreversion; Components: runtime +Source: "{#ComponentStagingDir}\_configuration\cfg\ecaltime.ini"; DestDir: "{commonappdata}\eCAL\"; Flags: ignoreversion; Components: runtime ; applications Source: "{#ComponentStagingDir}\app\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: applications @@ -118,11 +118,11 @@ Source: "{#DebugSdkStagingDir}\protobuf-export\*"; DestDir: "{app}"; ; Source: "{#DebugSdkStagingDir}\protoc\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\protobuf ; sdk\hdf5 -Source: "{#ComponentStagingDir}\configinstall\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 +Source: "{#ComponentStagingDir}\_configinstall\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 Source: "{#ComponentStagingDir}\headers\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 Source: "{#ComponentStagingDir}\libraries\lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 -Source: "{#DebugSdkStagingDir}\configinstall\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 +Source: "{#DebugSdkStagingDir}\_configinstall\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 Source: "{#DebugSdkStagingDir}\libraries\lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 Source: "{#DebugSdkStagingDir}\libraries\bin\*"; DestDir: "{app}\bin\"; Flags: ignoreversion recursesubdirs; Components: sdk\hdf5 @@ -132,7 +132,7 @@ Name: "{group}\eCAL Monitor"; Filename: "{app}\bin\ecal_ Name: "{group}\eCAL Recorder"; Filename: "{app}\bin\ecal_rec_gui.exe"; Components: applications Name: "{group}\eCAL Player"; Filename: "{app}\bin\ecal_play_gui.exe"; Components: applications Name: "{group}\eCAL Sys"; Filename: "{app}\bin\ecal_sys_gui.exe"; Components: applications -Name: "{group}\Configuration\Edit ecal.ini"; Filename: "{commonappdata}\eCAL\ecal.ini"; Components: runtime +Name: "{group}\Configuration\Edit ecal.yaml"; Filename: "{commonappdata}\eCAL\ecal.yaml"; Components: runtime Name: "{group}\{cm:UninstallProgram,{#AppName}}"; Filename: "{uninstallexe}" Name: "{commondesktop}\eCAL Launcher"; Filename: "{app}\bin\ecal_launcher.exe"; Tasks: desktopicon; Components: applications diff --git a/doc/rst/advanced/ecal_in_docker.rst b/doc/rst/advanced/ecal_in_docker.rst index 39c8167e45..0b03698740 100644 --- a/doc/rst/advanced/ecal_in_docker.rst +++ b/doc/rst/advanced/ecal_in_docker.rst @@ -193,10 +193,10 @@ Seamless IPC-Communication across host borders .. important:: This will work with eCAL 5.12 and higher. - Older versions lack the ability to utilize the ``host_group_name`` in the :file:`ecal.ini` file, thus it won't work. + Older versions lack the ability to utilize the ``host_group_name`` in the :file:`ecal.yaml` file, thus it won't work. -In eCAL, you are able to set host belonging over network borders by utilizing the :file:`ecal.ini` configuration file with the same ``host_group_name`` - in the following steps, you will learn how to set this up. +In eCAL, you are able to set host belonging over network borders by utilizing the :file:`ecal.yaml` configuration file with the same ``host_group_name`` - in the following steps, you will learn how to set this up. .. note:: If we don't set the same ``host_group_name`` on our Host and our Containers, an IPC-Communication across host borders is not available with different host names. @@ -207,28 +207,28 @@ In eCAL, you are able to set host belonging over network borders by utilizing th sudo docker network create --driver=bridge --subnet=10.0.10.0/24 my_network -#. Edit your :file:`ecal.ini` and run your Container within the newly created docker network +#. Edit your :file:`ecal.yaml` and run your Container within the newly created docker network * You will use our previously discussed :ref:`ecal-runtime-image` for the next step. - * First, open :file:`/etc/ecal/ecal.ini` from your preferred editor. + * First, open :file:`/etc/ecal/ecal.yaml` from your preferred editor. - * Search for the line ``network_enabled`` and set it to ``true``. + * Search for the line ``registration->network_enabled`` and set it to ``true``. - * Search for the line ``host_group_name`` and write your preferred name. + * Search for the line ``registration->host_group_name`` and write your preferred name. - * Save and close the :file:`ecal.ini` file. + * Save and close the :file:`ecal.yaml` file. - * Now your :file:`ecal.ini` file is prepared. - We want to use it not only for our Host-System but also for our Container, so we don't need to edit the :file:`ecal.ini` in our Container again. + * Now your :file:`ecal.yaml` file is prepared. + We want to use it not only for our Host-System but also for our Container, so we don't need to edit the :file:`ecal.yaml` in our Container again. To achieve that, run following command to start your container: .. code-block:: bash - sudo docker run --rm -it --ipc=host --pid=host --network=my_network --name=container1 -h=container1 --ip=10.0.10.10 -v /etc/ecal/ecal.ini:/etc/ecal/ecal.ini ecal-runtime + sudo docker run --rm -it --ipc=host --pid=host --network=my_network --name=container1 -h=container1 --ip=10.0.10.10 -v /etc/ecal/ecal.yaml:/etc/ecal/ecal.yaml ecal-runtime - You should now be inside the root shell of your Container. - Check if your :file:`ecal.ini` file is correct. + Check if your :file:`ecal.yaml` file is correct. - Now your Container is prepared and configured correctly, so we are ready to start an eCAL example. diff --git a/doc/rst/advanced/layers/shm.rst b/doc/rst/advanced/layers/shm.rst index b43f0f8169..5ad1e038e9 100644 --- a/doc/rst/advanced/layers/shm.rst +++ b/doc/rst/advanced/layers/shm.rst @@ -51,16 +51,20 @@ To support one to many publisher/subscriber connections, the publisher creates o Configuration ============= -The SHM Layer is set to ``auto`` (= 2) by default. -This means, that it is used automatically for all messages that need to be transmitted inside a single host. +The SHM Layer is set to ``enable`` by default. -The system-configuration-parameters in the :file:`ecal.ini` are: +The system-configuration-parameters in the :file:`ecal.yaml` are: -.. code-block:: ini - - [publisher] - use_shm = 2 +.. code-block:: yaml + # Publisher specific base settings + publisher: + layer: + # Base configuration for shared memory publisher + shm: + # Enable layer + enable: true + [..] There are a few options for tweaking the communication. Those options are explained below. @@ -76,13 +80,20 @@ If subscribers are too slow to process incoming messages then the overall softwa There may still be cases where it could make sense to synchronize the transfer of the payload from a publisher to a subscriber by using an additional handshake event. This event is signaled by a subscriber back to the sending publisher to confirm the complete payload transmission. -The handshake mechanism can be activated in the :file:`ecal.ini`: +The handshake mechanism can be activated in the :file:`ecal.yaml`: -.. code-block:: ini +.. code-block:: yaml - [publisher] - ; activate synchronization via memory transfer acknowledge signal with a timeout of 100 milliseconds - memfile_ack_timeout = 100 + # Publisher specific base settings + publisher: + layer: + # Base configuration for shared memory publisher + shm: + [..] + # Force connected subscribers to send acknowledge event after processing the message. + # The publisher send call is blocked on this event with this timeout (0 == no handshake). + acknowledge_timeout_ms: 5 + [..] If the parameter is set to a non-zero timeout, the publisher will create an additional event and inform the subscriber to fire this event when the transmission of the payload is completed. @@ -147,12 +158,18 @@ You can activate the feature in the following ways. - **Use multi-buffering as system-default**: - Edit your :file:`ecal.ini` and set a buffer count greater than 1: + Edit your :file:`ecal.yaml` and set a buffer count greater than 1: - .. code-block:: ini + .. code-block:: yaml - [publisher] - memfile_buffer_count = 3 + # Publisher specific base settings + publisher: + layer: + # Base configuration for shared memory publisher + shm: + [..] + # Maximum number of used buffers (needs to be greater than 0, default = 1) + memfile_buffer_count: 3 - **Use multi-buffering for a single publisher (from your code):** @@ -160,11 +177,21 @@ You can activate the feature in the following ways. .. code-block:: cpp - // Create a publisher (topic name "person") - eCAL::protobuf::CPublisher pub("person"); + #include + + ... + + // Create a publisher configuration object + eCAL::Publisher::Configuration pub_config; + + // Set the option for buffer count in layer->shm->memfile_buffer_count to 3, so it will create 3 SHM files + pub_config.layer.shm.memfile_buffer_count = 3; + + // Create a publisher (topic name "person") and pass the configuration object + eCAL::protobuf::CPublisher pub("person", pub_config); + + ... - // Set multi-buffering to 3, so it will create 3 SHM files - pub.ShmSetBufferCount(3); Combining the zero-copy feature with an increased number of memory buffer files (like 2 or 3) could be a nice setup allowing the subscriber to work on the memory file content without copying its content and nevertheless not blocking the publisher to write new data. Using Multibuffering however will force each Send operation to re-write the entire memory file and disable partial updates. \ No newline at end of file diff --git a/doc/rst/advanced/layers/shm_zerocopy.rst b/doc/rst/advanced/layers/shm_zerocopy.rst index 3fc03b0b7f..20d922d5c6 100644 --- a/doc/rst/advanced/layers/shm_zerocopy.rst +++ b/doc/rst/advanced/layers/shm_zerocopy.rst @@ -20,24 +20,40 @@ Enabling eCAL Zero Copy - **Use zero-copy as system-default:** - Zero Copy can be enabled as system default from the :file:`ecal.ini` file like follows: + Zero Copy can be enabled as system default from the :file:`ecal.yaml` file like follows: - .. code-block:: ini + .. code-block:: yaml + + # Publisher specific base settings + publisher: + layer: + # Base configuration for shared memory publisher + shm: + [..] + # Enable zero copy shared memory transport mode + zero_copy_mode: true - [publisher] - memfile_zero_copy = 1 - **Use zero-copy for a single publisher (from your code):** - Zero-copy can be activated (or deactivated) for a single publisher from the eCAL API: + Zero-copy can be activated (or deactivated) for a single publisher by using a specific publisher configuration: .. code-block:: cpp + + #include + + ... + + // Create a publisher configuration object + eCAL::Publisher::Configuration pub_config; + + // Set the option for zero copy mode in layer->shm->zero_copy_mode to true + pub_config.layer.shm.zero_copy_mode = true; - // Create a publisher (topic name "person") - eCAL::protobuf::CPublisher pub("person"); + // Create a publisher (topic name "person") and pass the configuration object + eCAL::protobuf::CPublisher pub("person", pub_config); - // Enable zero-copy for this publisher - pub.ShmEnableZeroCopy(true); + ... Keep in mind, that using protobuf for serialization will still: @@ -292,16 +308,21 @@ To send this payload you just need a few lines of code: .. code-block:: cpp + #include + int main(int argc, char** argv) { // initialize eCAL API eCAL::Initialize(argc, argv, "binary_payload_snd"); - // publisher for topic "simple_struct" - eCAL::CPublisher pub("simple_struct"); + // Create a publisher configuration object + eCAL::Publisher::Configuration pub_config; - // turn zero copy mode on - pub.ShmEnableZeroCopy(true); + // Set the option for zero copy mode in layer->shm->zero_copy_mode to true + pub_config.layer.shm.zero_copy_mode = true; + + // publisher for topic "simple_struct" + eCAL::CPublisher pub("simple_struct", pub_config); // create the simple struct payload CStructPayload struct_payload; @@ -369,6 +390,6 @@ Default eCAL SHM vs. Full Zero Copy SHM Combining Zero Copy and Multibuffering ====================================== -For technical reasons the Full Zero Copy mode described above is turned of if the Multibuffering option ``CPublisher::ShmSetBufferCount`` is activated. +For technical reasons the Full Zero Copy mode described above is turned off if the Multibuffering option ``CPublisher::ShmSetBufferCount`` is activated. Default (subscriber side) Zero Copy is working in combination with Multibuffering as described. diff --git a/doc/rst/advanced/layers/tcp.rst b/doc/rst/advanced/layers/tcp.rst index 6f784c79d6..b284f69ccb 100644 --- a/doc/rst/advanced/layers/tcp.rst +++ b/doc/rst/advanced/layers/tcp.rst @@ -63,13 +63,28 @@ You can activate TCP in the following ways: #. **Use TCP as system default (only recommended for testing):** - Modify the :file:`ecal.ini` and change the following: - - .. code-block:: ini - - [publisher] - use_tcp = 2 - use_udp_mc = 0 + Modify the :file:`ecal.yaml` and change the following: + + .. code-block:: yaml + + # Publisher specific base settings + publisher: + layer: + # Base configuration for shared memory publisher + shm: + # Enable layer + enable: true + [..] + + # Base configuration for UDP publisher + udp: + # Enable layer + enable: false + + # Base configuration for TCP publisher + tcp: + # Enable layer + enable: true This will @@ -88,19 +103,27 @@ You can activate TCP in the following ways: .. code-block:: cpp - // Create a publisher (topic name "person") - eCAL::protobuf::CPublisher pub("person"); + #include + + ... - // Switch UDP Multicast layer off - pub.SetLayerMode(eCAL::TLayer::tlayer_udp_mc, eCAL::TLayer::smode_off); + // Create a publisher configuration object + eCAL::Publisher::Configuration pub_config; + + // Set enable shm and tcp, disable udp layer + pub_config.layer.shm.enable = true; + pub_config.layer.udp.enable = false; + pub_config.layer.tcp.enable = true; + + // Create a publisher (topic name "person") and pass the configuration object + eCAL::protobuf::CPublisher pub("person"); - // Switch TCP layer on for Network connections (-> smode_uto) - pub.SetLayerMode(eCAL::TLayer::tlayer_tcp, eCAL::TLayer::smode_auto); + ... .. seealso:: Also see the ``person_snd_tcp`` sample: - https://github.com/eclipse-ecal/ecal/tree/master/samples/cpp/pubsub/protobuf/person_snd_tcp + https://github.com/eclipse-ecal/ecal/tree/master/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp diff --git a/doc/rst/advanced/message_drops.rst b/doc/rst/advanced/message_drops.rst index ac3b6b3303..3e70da4ac8 100644 --- a/doc/rst/advanced/message_drops.rst +++ b/doc/rst/advanced/message_drops.rst @@ -32,7 +32,7 @@ Dropping on Transport Layer - Having different link speeds in your network will almost always cause many drops for large messages, as you are e.g. sending with 10 Gbit to a 1Gbit port and so 90% of all packages are dropped. This has to be viewed on a traffic-per-message-basis, not on a traffic-per-second basis. For instance, you may be sending only 1 message per second from a 10Gibt link. This message is maybe sent in 10ms, while the 1 Gbit link would need 100ms to receive all the data. Consequently, the 1Gbit connection will inevitably start dropping fragments, even though the network is not even close to its capacity. - You can work around it by just connecting everything with the same link-speed, and by using the ``bandwidth_max_udp`` setting from the :ref:`ecal.ini ` + You can work around it by just connecting everything with the same link-speed, and by using the ``bandwidth_max_udp`` setting from the :ref:`ecal.yaml ` - Larger eCAL Messages are more likely to be corrupt, as they consist of more fragments. diff --git a/doc/rst/advanced/src/ecal_in_docker/ecal_runtime_container/Dockerfile b/doc/rst/advanced/src/ecal_in_docker/ecal_runtime_container/Dockerfile index 4f158beaf7..5a4768bd15 100644 --- a/doc/rst/advanced/src/ecal_in_docker/ecal_runtime_container/Dockerfile +++ b/doc/rst/advanced/src/ecal_in_docker/ecal_runtime_container/Dockerfile @@ -12,11 +12,11 @@ RUN apt-get install -y ecal # You can omit this, if you don't want to build applications in the container. RUN apt-get install -y cmake g++ libprotobuf-dev protobuf-compiler -# Set network_enabled = true in ecal.ini. +# Set network_enabled = true in ecal.yaml. # You can omit this, if you only need local communication. -RUN awk -F"=" '/^network_enabled/{$2="= true"}1' /etc/ecal/ecal.ini > /etc/ecal/ecal.tmp && \ - rm /etc/ecal/ecal.ini && \ - mv /etc/ecal/ecal.tmp /etc/ecal/ecal.ini +RUN awk -F"=" '/^network_enabled/{$2="= true"}1' /etc/ecal/ecal.yaml > /etc/ecal/ecal.tmp && \ + rm /etc/ecal/ecal.yaml && \ + mv /etc/ecal/ecal.tmp /etc/ecal/ecal.yaml # Print the eCAL config RUN ecal_config \ No newline at end of file diff --git a/doc/rst/advanced/transport_layers.rst b/doc/rst/advanced/transport_layers.rst index 536cc17753..a517fecd30 100644 --- a/doc/rst/advanced/transport_layers.rst +++ b/doc/rst/advanced/transport_layers.rst @@ -14,7 +14,7 @@ eCAL distinguished between three types of communication: - **Interhost Communication**: communication between publishers / subscribers that are located on different hosts, inside a network .. note:: - Per default, interhost communication is turned off by configuration. To configure this you need to set the ecal.ini [network/network_enabled] parameter to true (network communication mode) or false (local host only communication mode) + Per default, interhost communication is turned off by configuration. To configure this you need to set the ecal.yaml [network/network_enabled] parameter to true (network communication mode) or false (local host only communication mode) In general the latency of the data transport increases depending on the "distance" of the communication participants. The "closer" the participants, the "quicker" the transport. @@ -65,11 +65,11 @@ Configuration of transport layers In case the default configuration is not sensible enough, the eCAL user can finetune the configuration. The layers can be set up -- for a whole machine using the central configuration file (ecal.ini) +- for a whole machine using the central configuration file (ecal.yaml) - for a single eCAL process passed by command line arguments - for a single publish-subscribe connection using the C++ or python publisher API. -Every layer can set up in 3 different activation modes. Every mode can be configured as default in the ecal.ini file and can be overwritten by the C++/Python publisher API. This is the activation logic +Every layer can set up in 3 different activation modes. Every mode can be configured as default in the ecal.yaml file and can be overwritten by the C++/Python publisher API. This is the activation logic - off: layer is switched off - on: layer is always switched on (i.e. payload will be send no matter if there is any local or network subscription) @@ -80,7 +80,7 @@ Every layer can set up in 3 different activation modes. Every mode can be config Independent from this publisher setting you can switch on/off the receiving (subscription) logic for every layer. That means you can prevent incoming payload on specific layers. -This can be done in the ecal.ini file [network] section. +This can be done in the ecal.yaml file [network] section. - shm_rec_enabled = true / false : enable / disable inter process subscriptions - udp_mc_rec_enabled = true / false : enable / disable inter host subscriptions diff --git a/doc/rst/applications/sys/sys_gui.rst b/doc/rst/applications/sys/sys_gui.rst index d82824995b..dbff603b30 100644 --- a/doc/rst/applications/sys/sys_gui.rst +++ b/doc/rst/applications/sys/sys_gui.rst @@ -86,7 +86,7 @@ Task configuration Whether the window of the started task shall be normal, maximized or minimized. Only supported on Microsoft Windows. - When using a terminal emulator on linux (see :ref:`ecal.ini`), the option *minimized* will prevent opening a terminal emulator and the process will print its output in the ecal_sys_client terminal as usual. + When using a terminal emulator on linux (see :ref:`ecal.yaml`), the option *minimized* will prevent opening a terminal emulator and the process will print its output in the ecal_sys_client terminal as usual. - **Launch order**: Defines the order of tasks that must be started serialized. diff --git a/doc/rst/configuration/cloud.rst b/doc/rst/configuration/cloud.rst index 0cf0b6ef68..de431e11b0 100644 --- a/doc/rst/configuration/cloud.rst +++ b/doc/rst/configuration/cloud.rst @@ -6,16 +6,31 @@ Cloud Configuration =================== -To switch eCAL to cloud mode, edit your :file:`ecal.ini` and change the following settings: +To switch eCAL to cloud mode, edit your :file:`ecal.yaml` and change the following settings: * |fa-windows| Windows: |ecalini-path-windows| * |fa-ubuntu| Ubuntu: |ecalini-path-ubuntu| -.. code-block:: ini - - [network] - network_enabled = true - multicast_ttl = 2 +.. code-block:: yaml + + # Registration layer configuration + registration: + [..] + # true = all eCAL components communicate over network boundaries + # false = local host only communication (Default: false) + network_enabled: true + + [..] + + # Transport layer configuration + transport_layer: + udp: + [..] + + network: + [..] + # TTL (hop limit) is used to determine the amount of routers being traversed towards the destination + ttl: 3 .. important:: Don't forget to set your multicast routes and make sure your hostname resolution works on all machines! diff --git a/doc/rst/configuration/local.rst b/doc/rst/configuration/local.rst index 83cc602fd3..4d2b1632ef 100644 --- a/doc/rst/configuration/local.rst +++ b/doc/rst/configuration/local.rst @@ -6,21 +6,36 @@ Local configuration =================== -Using eCAL in local mode can be benefitial +Using eCAL in local mode can be beneficial * to avoid network congestion if the data is not needed on other machines * to make eCAL work in VPN and Firewall scenarios where non-local traffic is blocked -To switch eCAL to local mode, edit your :file:`ecal.ini` and change the following settings: +To switch eCAL to local mode, edit your :file:`ecal.yaml` and change the following settings: * |fa-windows| Windows: |ecalini-path-windows| * |fa-ubuntu| Ubuntu: |ecalini-path-ubuntu| -.. code-block:: ini +.. code-block:: yaml - [network] - network_enabled = false - multicast_ttl = 0 + # Registration layer configuration + registration: + [..] + # true = all eCAL components communicate over network boundaries + # false = local host only communication (Default: false) + network_enabled: false + + [..] + + # Transport layer configuration + transport_layer: + udp: + [..] + + network: + [..] + # TTL (hop limit) is used to determine the amount of routers being traversed towards the destination + ttl: 0 .. seealso:: To learn about the differences between local mode and cloud mode, check out :ref:`this table `. \ No newline at end of file diff --git a/doc/rst/configuration/npcap.rst b/doc/rst/configuration/npcap.rst index f2d493ceb4..377f880682 100644 --- a/doc/rst/configuration/npcap.rst +++ b/doc/rst/configuration/npcap.rst @@ -39,9 +39,14 @@ How to use Npcap #. Edit |ecalini-path-windows|: - .. code-block:: ini + .. code-block:: yaml - npcap_enabled = true + # Transport layer configuration + transport_layer: + udp: + [..] + # Windows specific setting to enable receiving UDP traffic with the Npcap based receiver + npcap_enabled: true #. Check eCAL Mon diff --git a/doc/rst/configuration/options.rst b/doc/rst/configuration/options.rst index bb93203ef4..35689decdd 100644 --- a/doc/rst/configuration/options.rst +++ b/doc/rst/configuration/options.rst @@ -3,7 +3,7 @@ .. _configuration_options: ======== -ecal.ini +ecal.yaml ======== eCAL has many options that can be configured via an .ini file which is located at: @@ -15,278 +15,498 @@ Loading strategy (Priority) =========================== The eCAL configuration file is loaded based on the following priorities, whereever it is found first. -If you want a specific eCAL Node to run with another ``ecal.ini`` than the others, you can set the ``ECAL_DATA`` variable before starting the process, e.g. from a batch or shell skript. -In addition, some eCAL applications support providing a path from the command line option ``--ecal-ini-file``. +If you want a specific eCAL Node to run with another ``ecal.yaml`` than the others, you can set the ``ECAL_DATA`` variable before starting the process, e.g. from a batch or shell skript. +In addition, some eCAL applications support providing a path from the command line option ``--ecal-config-file``. .. important:: This loading strategy is valid for eCAL 5.10 and up. - |fa-windows| Windows: - 1. ``%ECAL_DATA%/ecal.ini`` - 2. ``%ProgramData%/ecal/ecal.ini`` + 1. ``%ECAL_DATA%/ecal.yaml`` + 2. ``%ProgramData%/ecal/ecal.yaml`` - |fa-ubuntu| Ubuntu: - 1. ``$ECAL_DATA/ecal.ini`` - 2. ``/etc/ecal/ecal.ini`` (from ``CMAKE_INSTALL_SYSCONFDIR``) - 3. ``/etc/ecal/ecal.ini`` (fallback) + 1. ``$ECAL_DATA/ecal.yaml`` + 2. ``/etc/ecal/ecal.yaml`` (from ``CMAKE_INSTALL_SYSCONFDIR``) + 3. ``/etc/ecal/ecal.yaml`` (fallback) .. note:: - This second path is set from CMake to ``CMAKE_INSTALL_SYSCONFDIR/ecal/ecal.ini``. + This second path is set from CMake to ``CMAKE_INSTALL_SYSCONFDIR/ecal/ecal.yaml``. Official builds are configured to use ``/etc``. - If you are compiling eCAL yourself and don't provide the SYSCONFDIR, CMake will usually use ``/usr/local/etc/ecal/ecal.ini``. + If you are compiling eCAL yourself and don't provide the SYSCONFDIR, CMake will usually use ``/usr/local/etc/ecal/ecal.yaml``. -ecal.ini options -================ +ecal.yaml options +================= -[network] ---------- +Registration settings are listed in the section ``registration`` +---------------------------------------------------------------- -The network setting drive how and which ... +.. option:: registration -.. option:: network_enabled + .. option:: registration_refresh - ``true`` / ``false``, default: ``true`` - - ``true`` = all eCAL components communicate over network boundaries - - ``false`` = local host only communication - -.. option:: multicast_config_version + ``< registration_timeout/2``, default ``1000`` - ``v1`` / ``v2``, default: ``v1`` + topic registration refresh cycle (has to be smaller than registration timeout!) - UDP configuration version (Since eCAL 5.12.) - - ``v1`` = default behavior + .. option:: registration_timeout + + ``1000 + (x * 1000)``, default ``60000`` - ``v2`` = new behavior, comes with a bit more intuitive handling regarding masking of the groups + timeout for topic registration in ms -.. option:: multicast_group + .. option:: loopback - IPV4 Adress, default ``239.0.0.1`` + ``true`` / ``false``, default ``true`` + + enable to receive registration information on the same local machine - UDP multicast group base. All registration and logging information is sent on this address. + .. option:: host_group_name -.. option:: multicast_mask + ````, default ``""`` - ``v1`` **behavior:** - - ``0.0.0.1``-``0.0.0.255`` + host group name that enables interprocess mechanisms across (virtual) host borders (e.g. Docker); + by default equivalent to local host name - Mask maximum number of dynamic multicast group + .. option:: network_enabled - ``v2`` **behavior:** - - ``255.0.0.0``-``255.255.255.255`` - - Mask for the multicast group. Topic traffic may be set on any of the unmasked addresses. - - With ``multicast_group``: ``239.0.0.1`` and ``multicast_mask``: ``255.255.255.0``, topic traffic will be sent on addresses ``239.0.0.0``-``239.0.0.255``. - -.. option:: multicast_port - - ``14000 + x`` + ``true`` / ``false``, default: ``true`` + + ``true`` = all eCAL components communicate over network boundaries + + ``false`` = local host only communication - UDP multicast port number (eCAL will use at least the 2 following port numbers too, so please modify in steps of 10 (e.g. 1010, 1020 ...) + .. option:: layer -.. option:: multicast_ttl - - ``0 + x`` - - UDP ttl value, also known as hop limit, is used in determining the intermediate routers being traversed towards the destination + .. option:: shm + + .. option:: enabled -.. option:: multicast_sndbuf - - ``1024 * x`` - - UDP send buffer in bytes + ``true`` / ``false``, default: ``false`` -.. option:: multicast_rcvbuf - - ``1024 * x`` + enable shared memory layer - UDP receive buffer in bytes + .. option:: domain -.. option:: bandwidth_max_udp - - ``1048576`` - - UDP bandwidth limit for eCAL udp layer (-1 == unlimited) + ``ecal_mon`` -.. option:: shm_rec_enabled + Domain name for shared momory based registration - ``true`` + .. option:: queue_size - Enable to receive on eCAL shared memory layer + ``1024`` -.. option:: udp_mc_rec_enabled - - ``true`` + Queue size of registration events - Enable to receive on eCAL udp multicast layer + .. option:: udp + + .. option:: enabled + + ``true`` / ``false``, default: ``true`` + + enable UDP multicast layer + + .. option:: port + + ``14000 + x`` + + UDP port for registration information if UDP is enabled + + +Monitoring settings are listed in the section ``monitoring`` +---------------------------------------------------------- + +.. option:: monitoring + + .. option:: timeout + + ``1000 + (x * 1000)`` default ``1000`` + Timeout for topic monitoring in ms + If no additional registration information for the topic has be received in that time period, topics will no longer be shown in eCAL Monitor. + + + .. option:: filter_excl + + topics blacklist as regular expression (will not be monitored) + Per default includes all eCAL internal topics. + + default = ``^__.*$`` + + .. option:: filter_incl + + topics whitelist as regular expression (will be monitored only) + + default = `` `` + + +Transportlayer settings are listed in the section ``transport_layer`` +--------------------------------------------------------------------- + +.. option:: transport_layer + + .. option:: udp + + .. option:: config_version + + ``v1`` / ``v2``, default: ``v2`` + + UDP configuration version (Since eCAL 5.12.) + + ``v1`` = default behavior + + ``v2`` = new behavior, comes with a bit more intuitive handling regarding masking of the groups + + .. option:: mode + + ``local`` / ``network``, default: ``local`` + + ``local`` = use local network settings - not configurable by the user + + ``network`` = use network settings - configurable by the user + + .. option:: port + + ``14000 + x`` + + UDP multicast port number + + .. option:: mask + + ``v1`` **behavior:** + + ``0.0.0.1``-``0.0.0.255`` + + Mask maximum number of dynamic multicast group + + ``v2`` **behavior:** + + ``255.0.0.0``-``255.255.255.255`` + + Mask for the multicast group. Topic traffic may be set on any of the unmasked addresses. + + With ``group``: ``239.0.0.1`` and ``mask``: ``255.255.255.0``, topic traffic will be sent on addresses ``239.0.0.0``-``239.0.0.255``. + + .. option:: send_buffer + + ``1024 * x`` default ``5242880`` + + UDP send buffer in bytes + + .. option:: receive_buffer + + ``1024 * x`` default ``5242880`` + + UDP receive buffer in bytes + + .. option:: join_all_interfaces + + ``true`` / ``false`` default ``false`` + + Linux specific setting to join all network interfaces independend of their link state. + Enabling ensures that eCAL processes receive data when they are started before the + network devices are up and running. + + .. option:: npcap_enabled + + ``true`` / ``false`` default ``false`` + + Enable to receive UDP traffic with the Npcap based receiver + + .. option:: local + + In local mode the UDP multicast group is set to the local host address ("127.0.0.1") and the multicast TTL is set to 0 by default. + This is not configurable. + + .. option:: network + + .. option:: group + + ``xxx.xxx.xxx.xxx`` IP address as text, default ``239.0.0.1`` + + Multicast group base: all registration and logging is sent on this address + + .. option:: ttl + + ``0 + x`` default ``3`` + + TTL (hop limit) is used to determine the amount of routers being traversed towards the destination + + .. option:: tcp + + .. option:: number_executor_reader + + ``1 + x`` default ``1`` + + Number of reader threads that shall execute workload + + .. option:: number_executor_writer + + ``1 + x`` default ``1`` + + Number of writer threads that shall execute workload + + .. option:: max_reconnections + + ``1 + x`` default ``1`` + + Reconnection attemps the session will try to reconnect in case of an issue + + .. option:: shm + + .. option:: memfile_min_size_bytes + + ``x * 4096 kB`` default ``4096`` + + Default memory file size for new publisher + + + .. option:: memfile_reserve_percent + + ``20 .. x`` default ``50`` + + Dynamic file size reserve before recreating memory file if topic size changes + + +Publisher settings are listed in the section ``publisher`` +---------------------------------------------------------- + +.. option:: publisher + + .. option:: layer + + .. option:: shm -.. option:: npcap_enabled - - ``false`` - - Enable to receive UDP traffic with the Npcap based receiver + .. option:: enable + ``true`` / ``false`` default ``true`` -[common] --------- + Enable shared memory layer -.. option:: registration_timeout - - ``1000 + (x * 1000)``, default ``60000`` + .. option:: zero_copy_mode - timeout for topic registration in ms + ``true`` / ``false`` default ``false`` -.. option:: registration_refresh + Enable zero copy mode for shared memory transport mode - ``< registration_timeout/2``, default ``1000`` + .. option:: acknowledge_timeout_ms - topic registration refresh cylce (has to be smaller then registration timeout !) + ``0 + x`` default ``0`` + Force connected subscribers to send acknowledge event after processing the message. + The publisher send call is blocked on this event with this timeout (0 == no handshake). -[time] ------- + .. option:: memfile_buffer_count -.. option:: timesync_module_rt - - default: ``"ecaltime-localtime"`` - - module (dll / so) name time sync interface. The name will be extended with debug suffix (d) and platform extension (.dll|.so) + ``1 + x`` default ``1`` - Available modules are: - - - ecaltime-localtime local system time without synchronization - - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux (device configuration in ecaltime.ini) - - ecaltime-simtime Simulation time as published by the eCAL Player. + Maximum number of used buffers (needs to be greater than 0, default = 1) -[process] ---------- + .. option:: udp + + .. option:: enable + + ``true`` / ``false`` default ``true`` + + Enable UDP multicast layer + + .. option:: tcp + + .. option:: enable + + ``true`` / ``false`` default ``false`` + + Enable TCP layer + + .. option:: share_topic_type + + ``true`` / ``false`` default ``true`` + + Share topic type via registration layer + + + + .. option:: share_topic_description + + ``true`` / ``false`` default ``true`` + + Share topic description via registration layer. + If set to false, reflection is completely disabled. It is not possible then to monitor the content of messages in the eCAL Monitor. + + .. option:: priority_local + + ``["shm", "udp", "tcp"]`` default ``["shm", "udp", "tcp"]`` + + A list of transport layers as text that specifies the prioritized layer for local communication. + + .. option:: priority_remote + + ``["udp", "tcp"]`` default ``["udp", "tcp"]`` + + A list of transport layers as text that specifies the prioritized layer for remote communication. + + +Subscriber settings are listed in the section ``subscriber`` +------------------------------------------------------------ + +.. option:: subscriber + + .. option:: layer + + .. option:: shm + + .. option:: enable + + ``true`` / ``false`` default ``true`` + + Enable shared memory layer + + .. option:: udp + + .. option:: enable + + ``true`` / ``false`` default ``true`` + + Enable UDP multicast layer + + .. option:: tcp + + .. option:: enable + + ``true`` / ``false`` default ``false`` + + Enable TCP layer -.. _ecalini_option_terminal_emulator: + .. option:: drop_out_of_order_messages -.. option:: terminal_emulator - - default: ``""`` + ``true`` / ``false`` default ``false`` + + Enable dropping of payload messages that arrive out of order - command for starting applications with an external terminal emulator. - If empty, the command will be ignored. - Ignored on Windows. - - e.g: - - ``/usr/bin/x-terminal-emulator -e`` - - ``/usr/bin/gnome-terminal -x`` - - ``/usr/bin/xterm -e`` +Time settings are listed in the section ``time`` +------------------------------------------------ -[publisher] ------------ - -.. option:: use_shm - - use shared memory transport layer - - - 0 = off - - 1 = on - - 2 = auto - - default = 2 - -.. option:: use_udp_mc - - use udp multicast transport layer - - - 0 = off - - 1 = on - - 2 = auto - - default = 2 - +.. option:: time + .. option:: rt + + default: ``"ecaltime-localtime"`` + + Module (dll / so) name time sync interface. The name will be extended with debug suffix (d) and platform extension (.dll|.so) -.. option:: memfile_minsize = x * 4096 kB - - default memory file size for new publisher (``x * 4096 kB``) - - default = 4096 + Available modules are: + + - ecaltime-localtime local system time without synchronization + - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux (device configuration in ecaltime.ini) + - ecaltime-simtime Simulation time as published by the eCAL Player. -.. option:: memfile_reserve + .. option:: replay - dynamic file size reserve before recreating memory file if topic size changes (``20 .. x``) + default ``""`` - default = 50 + Module name of time plugin for replaying -.. option:: memfile_ack_timeout - Publisher timeout for ack event from subscriber that memory file content is processed - - default = 0 +Service settings are listed in the section ``service`` +------------------------------------------------------ -.. option:: share_ttype +.. option:: service - share topic type via registration layer ( ``0, 1``) + .. option:: protocol_v0 + + ``true`` / ``false`` default ``true`` - default = 1 + Support service protocol v0, eCAL 5.11 and older -.. option:: share_tdesc + .. option:: protocol_v1 + + ``true`` / ``false`` default ``false`` - share topic description via registration layer ( ``0, 1``) - If set to 0, reflection is completely disabled. It is not possible then to monitor the content of messages in the eCAL Monitor. - - default = 1 + Support service protocol v1, eCAL 5.12 and newer -[monitoring] ------------- -Monitor settings are listed in the section ``monitoring`` +Application settings are listed in the section ``application`` +-------------------------------------------------------------- -.. option:: timeout +.. option::application + + .. option:: sys + + .. option:: filter_excl + + Text as regex, default ``"^eCALSysClient$|^eCALSysGUI$|^eCALSys$*"`` + + Apps blacklist to be excluded when importing tasks from cloud. + + .. option:: terminal + + .. option:: emulator + + External terminal emulator as text, default ``""`` + + Linux only command for starting applications with an external terminal emulator. + e.g. + /usr/bin/x-terminal-emulator -e + /usr/bin/gnome-terminal -x + /usr/bin/xterm -e + + If empty, the command will be ignored. + + +Logging settings are listed in the section ``logging`` +------------------------------------------------------ + +.. option:: logging + + .. option:: sinks + + Log levels are: "info", "warning", "error", "fatal", "debug1", "debug2", "debug3", "debug4" + + .. option:: console + + .. option:: enable - timeout for topic monitoring in ms (``1000 + (x * 1000)``) - If no additional registration information for the topic has be received in that time period, topics will no longer be shown in eCAL Monitor. - - default = 5000 - -.. option:: filter_excl - - topics blacklist as regular expression (will not be monitored) - Per default includes all eCAL internal topics. - - default = ``^__.*$`` - -.. option:: filter_incl - - topics whitelist as regular expression (will be monitored only) - - default = `` `` - -.. option:: filter_log_con - - log messages to console (all, info, warning, error, fatal, debug1, debug2, debug3, debug4) - - default = ``error, fatal`` - -.. option:: filter_log_file - - log messages to log file - - default = ``error, fatal`` - -.. option:: filter_log_udp - - log messages to udp bus - - default = ``info, warning, error, fatal`` - -[sys] ------ - -.. option:: filter_excl - - Apps blacklist to be excluded when importing tasks from cloud. + ``true`` / ``false`` default ``false`` + + Enable console logging + + .. option:: level + + List of log levels as text, default ``["info", "warning", "error", "fatal"]`` + + .. option:: file + + .. option:: enable + + ``true`` / ``false`` default ``false`` + + Enable file logging + + .. option:: level + + List of log levels as text, default ``[]`` + + .. option:: path + + Path as text, default ``ecal.log`` + + .. option:: udp + + .. option:: enable + + ``true`` / ``false`` default ``false`` + + Enable udp logging + + .. option:: level + + List of log levels as text, default ``["info", "warning", "error", "fatal"]`` + + .. option:: port + + ``14000 + x`` default ``14001`` + + UDP port for logging information diff --git a/doc/rst/configuration/runtime_configuration.rst b/doc/rst/configuration/runtime_configuration.rst index 5c62120e76..f8d8ed6b95 100644 --- a/doc/rst/configuration/runtime_configuration.rst +++ b/doc/rst/configuration/runtime_configuration.rst @@ -12,7 +12,7 @@ The corresponding structure reflects the configuration file (:ref:`configuration Custom types ============ -In order to rule out configuration errors, custom datatypes for IP addresses (IpAddressV4) and sizes (constrained integer) are introduced. +In order to rule out configuration errors, custom datatypes for IP addresses (IpAddressV4) and sizes (ConstrainedInteger) are introduced. **IpAddressV4:** For assigning an IP address simply assign a string with the desired address. Decimal and hexadecimal format is supported. @@ -22,7 +22,7 @@ The IP address can be used like a normal string object. For example: .. code-block:: c++ - eCAL::Types::IpAddressV4 ip_address = std::string("192.168.7.1"); // in hex: "C0.A8.7.1" + eCAL::Types::IpAddressV4 ip_address = "192.168.7.1"; // in hex: "C0.A8.7.1" std::cout << ip_address << "\n"; **ConstrainedInteger**: ConstrainedInteger are specified with a minimum (default: 0), step (default: 1) and maximum (default: maximum of int) value. @@ -41,12 +41,12 @@ Global configuration initialization =================================== The configuration will be first initialized with the default values specified by eCAL. -If you want to use the systems eCAL .ini file, call the ``InitConfigWithDefaultIni()`` function of the config object. +If you want to use the systems eCAL .ini file, call the ``InitConfigWithDefaultYaml()`` function of the config object. In case the .ini to use is specified via command line parameter, this one is chosen instead. The object will throw an error, in case the specified .ini file cannot be found. -It is also possible to specify the .ini by calling the function ``InitConfig(std::string _ini_path)`` of the config object. +It is also possible to specify the .ini by calling the function ``checkForValidConfigFilePath(const std::string yaml_path_)`` of the config object. * |fa-file-alt| :file:`hello_config/main.cpp`: diff --git a/doc/rst/configuration/src/hello_config/main.cpp b/doc/rst/configuration/src/hello_config/main.cpp index 68a4967ad1..e35cdc70d7 100644 --- a/doc/rst/configuration/src/hello_config/main.cpp +++ b/doc/rst/configuration/src/hello_config/main.cpp @@ -8,24 +8,24 @@ int main(int argc, char** argv) // Create a configuration object with the command line arguments eCAL::Configuration custom_config(argc, argv); - // Use the .ini file of the system if available - custom_config.InitConfigWithDefaultIni(); + // Use the .yaml file of the system if available + custom_config.InitConfigWithDefaultYaml(); // Set the values in a try/catch block, as wrong configuration leads to exceptions try { - // In case you decided to specify an own .ini file to use + // In case you decided to specify an own .yaml file to use // Configuration based on previous ini file will be overwritten - custom_config.InitConfig("C:\\eCAL_local.ini"); + custom_config.InitConfigFromFile("C:\\eCAL_local.yaml"); // Set the communication layer to network - custom_config.transport_layer.network_enabled = true; + custom_config.registration.network_enabled = true; // Set a custom udp multicast group, correct IP address necessary - custom_config.transport_layer.mc_options.group = std::string("239.0.1.1"); + custom_config.transport_layer.udp.network.group = std::string("239.0.1.1"); // Increase the send buffer, size increase in 1024 bytes steps - custom_config.transport_layer.mc_options.sndbuf = (5242880 + 10 * 1024); + custom_config.transport_layer.udp.send_buffer = (5242880 + 10 * 1024); } catch (std::invalid_argument& e) { diff --git a/doc/rst/configuration/src/publisher_config/main.cpp b/doc/rst/configuration/src/publisher_config/main.cpp index c3cea80141..a2bf982cba 100644 --- a/doc/rst/configuration/src/publisher_config/main.cpp +++ b/doc/rst/configuration/src/publisher_config/main.cpp @@ -14,15 +14,15 @@ int main(int argc, char** argv) eCAL::Publisher::Configuration pub_config; // disable all layers except for SHM - pub_config.shm.enable = true; - pub_config.udp.enable = false; - pub_config.tcp.enable = false; + pub_config.layer.shm.enable = true; + pub_config.layer.udp.enable = false; + pub_config.layer.tcp.enable = false; // create publisher 1 eCAL::string::CPublisher pub_1("topic_1", pub_config); // enable for the second publisher also tcp - pub_config.tcp.enable = true; + pub_config.layer.tcp.enable = true; // create publisher 2 eCAL::string::CPublisher pub_2("topic_2", pub_config); diff --git a/doc/rst/development/building_ecal_from_source.rst b/doc/rst/development/building_ecal_from_source.rst index 89db9fc312..acaeebd58a 100644 --- a/doc/rst/development/building_ecal_from_source.rst +++ b/doc/rst/development/building_ecal_from_source.rst @@ -100,7 +100,7 @@ We support building on currently supported Ubuntu LTS releases. .. seealso:: - The build described here is a very simple (yet complete and fully functional) build that differs from our "official" binaries, e.g. in regards of the library install directory and the :file:`ecal.ini` location-. + The build described here is a very simple (yet complete and fully functional) build that differs from our "official" binaries, e.g. in regards of the library install directory and the :file:`ecal.yaml` location-. If your goal is to replicate the official build, you should apply the CMake Options exactly as we do. You can grab those from our GitHub Action build scripts: diff --git a/doc/rst/getting_started/cloud.rst b/doc/rst/getting_started/cloud.rst index 84e922c178..7ab97d84eb 100644 --- a/doc/rst/getting_started/cloud.rst +++ b/doc/rst/getting_started/cloud.rst @@ -31,7 +31,7 @@ eCAL can run in two modes, that differ from each other: **local mode** and **clo * Uses shared memory to send data to processes on the same host -Enable network-mode in :file:`ecal.ini` +Enable network-mode in :file:`ecal.yaml` ======================================= .. note:: @@ -42,18 +42,32 @@ Enable network-mode in :file:`ecal.ini` So since eCAL 5.10 you will have to enable network-mode first. By default, eCAL is configured in local mode. -To switch eCAL to cloud mode, edit your :file:`ecal.ini` and change the following settings: +To switch eCAL to cloud mode, edit your :file:`ecal.yaml` and change the following settings: * |fa-windows| Windows: |ecalini-path-windows| * |fa-ubuntu| Ubuntu: |ecalini-path-ubuntu| -.. code-block:: ini +.. code-block:: yaml - [network] - network_enabled = true - multicast_ttl = 2 + # Registration layer configuration + registration: + [..] + # true = all eCAL components communicate over network boundaries + # false = local host only communication (Default: false) + network_enabled: true + [..] -The ``multicast_ttl`` setting configures the *time to live* of the UDP datagrams, i.e. the number of hops a datagram can take before it is discarded. + # Transport layer configuration + transport_layer: + udp: + [..] + + network: + [..] + # TTL (hop limit) is used to determine the amount of routers being traversed towards the destination + ttl: 2 + +The ``transport_layer->udp->network->ttl`` setting configures the *time to live* of the UDP datagrams, i.e. the number of hops a datagram can take before it is discarded. Usually, ``2`` is sufficient, but if you have a network with many routers, you may have to increase that number. .. seealso:: diff --git a/doc/rst/getting_started/hello_world_python_proto.rst b/doc/rst/getting_started/hello_world_python_proto.rst index 0a48af805d..e531c20611 100644 --- a/doc/rst/getting_started/hello_world_python_proto.rst +++ b/doc/rst/getting_started/hello_world_python_proto.rst @@ -111,7 +111,7 @@ For that, create a file :file:`protobuf_snd.py` next to your :file:`proto_messag **Line 14** initializes eCAL. The name of the node is "Python Protobuf Publisher". - By also providing ``sys.argv`` you get the ability to pass command line arguments to eCAL, e.g. for loading an :file:`ecal.ini` configuration file from a non-standard path. + By also providing ``sys.argv`` you get the ability to pass command line arguments to eCAL, e.g. for loading an :file:`ecal.yaml` configuration file from a non-standard path. In practice this is not often used and you could also pass ``[]`` as arguments. **Line 20** creates an eCAL Publisher for the topic "hello_world_python_protobuf_topic". diff --git a/doc/rst/getting_started/recorder.rst b/doc/rst/getting_started/recorder.rst index ca0838824f..287423e5da 100644 --- a/doc/rst/getting_started/recorder.rst +++ b/doc/rst/getting_started/recorder.rst @@ -133,7 +133,7 @@ Before continuing to the next section and replaying the measurement, let's quick * |fa-file-alt| :file:`doc/description.txt`: Your description and your comment is saved in this file * |fa-folder-open| :file:`florian-windows`: Each recorder creates a directory with its hostname for its files. This is important in a distributed measurement scenario, where multiple recorders exist. You will see a directory with your own hostname here. - * |fa-file-alt| :file:`ecal.ini`: The configuration file at the time when you started the recorder. Useful for finding configuration issues later. + * |fa-file-alt| :file:`ecal.yaml`: The configuration file at the time when you started the recorder. Useful for finding configuration issues later. * |fa-file| :file:`florian-windows.hdf5`: The actual recorded data. eCAL records data in the standardized `HDF5 format `_ * |fa-file-alt| :file:`system-information.txt`: The same system information that we have seen in the eCAL Monitor. Useful for finding issues later. diff --git a/doc/rst/include.txt b/doc/rst/include.txt index 0afa32c585..29542e0301 100644 --- a/doc/rst/include.txt +++ b/doc/rst/include.txt @@ -52,11 +52,11 @@ -.. |ecalini-path-windows| replace:: :file:`C:\\ProgramData\\eCAL\\ecal.ini` -.. |ecalini-path-ubuntu| replace:: :file:`/etc/ecal/ecal.ini` +.. |ecalini-path-windows| replace:: :file:`C:\\ProgramData\\eCAL\\ecal.yaml` +.. |ecalini-path-ubuntu| replace:: :file:`/etc/ecal/ecal.yaml` -.. |ecalini-path-windows-old| replace:: :file:`C:\\eCAL\\cfg\\ecal.ini` -.. |ecalini-path-ubuntu-old| replace:: :file:`/usr/etc/ecal/ecal.ini` +.. |ecalini-path-windows-old| replace:: :file:`C:\\eCAL\\cfg\\ecal.yaml` +.. |ecalini-path-ubuntu-old| replace:: :file:`/usr/etc/ecal/ecal.yaml` .. |person_snd-path-windows| replace:: :file:`C:\\eCAL\\samples\\bin\\ecal_sample_person_snd.exe` diff --git a/doc/rst/old code.txt b/doc/rst/old code.txt index 17b58a322f..2f8d0f9d71 100644 --- a/doc/rst/old code.txt +++ b/doc/rst/old code.txt @@ -4,7 +4,7 @@ ├─ |fa-folder-open| :file:`doc` │ └─ |fa-file-alt| :file:`description.txt`: Your description and your comment is saved in this file ├─ |fa-folder-open| :file:`florian-windows`: Each recorder creates a directory with its hostname for its files. This is important in a distributed measurement scenario, where multiple recorders exist. You will see a directory with your own hostname here. - │ ├─ |fa-file-alt| :file:`ecal.ini`: The configuration file at the time when you started the recorder. Usefull for finding configuration issues later. + │ ├─ |fa-file-alt| :file:`ecal.yaml`: The configuration file at the time when you started the recorder. Usefull for finding configuration issues later. │ ├─ |fa-file| :file:`florian-windows.hdf5`: The actual recorded data. eCAL records data in the standardized `HDF5 format `_ │ └─ |fa-file-alt| :file:`system-information.txt`: The same system information that we have seen in the eCAL Monitor. Usefull for finding issues later. └─ |fa-file| :file:`2020-07-28_09-39-51.661_measurement.ecalmeas`: "Index-File" that can be opened with the eCAL Player. Does not contain any data by itself. \ No newline at end of file diff --git a/doc/rst/versions/5.10/whats_new.rst b/doc/rst/versions/5.10/whats_new.rst index 46c7734fdc..7c26953721 100644 --- a/doc/rst/versions/5.10/whats_new.rst +++ b/doc/rst/versions/5.10/whats_new.rst @@ -46,7 +46,7 @@ New features Other notable changes ===================== -- The :file:`ecal.ini` now installs a local configuration by default +- The :file:`ecal.yaml` now installs a local configuration by default - Improved eCAL Service API and internals diff --git a/doc/rst/versions/5.11/whats_new.rst b/doc/rst/versions/5.11/whats_new.rst index 39961bfeb3..23c9aa03af 100644 --- a/doc/rst/versions/5.11/whats_new.rst +++ b/doc/rst/versions/5.11/whats_new.rst @@ -27,7 +27,7 @@ New features - eCAL Rec can now **record each topic to its own file** -- Added **Shared-Memory-Monitoring-Layer** (experimental, enable via ``ecal.ini``!). This new monitoring layer can locally match publishers and subscribers and distribute monitoring information without the need for UDP Multicast. +- Added **Shared-Memory-Monitoring-Layer** (experimental, enable via ``ecal.yaml``!). This new monitoring layer can locally match publishers and subscribers and distribute monitoring information without the need for UDP Multicast. - GUI Apps now start in **dark-mode** by default diff --git a/doc/rst/versions/5.12/compatibility_table.txt b/doc/rst/versions/5.12/compatibility_table.txt index c76e57f91c..1c26707868 100644 --- a/doc/rst/versions/5.12/compatibility_table.txt +++ b/doc/rst/versions/5.12/compatibility_table.txt @@ -6,7 +6,7 @@ - * **eCAL UDP**: 100% compatible in default settings A proper Topic-Name -> UDP-Multicast-Group computation has been added. - By default, the old version (``multicast_config_version = v1``) is enabled in the :file:`ecal.ini`, which makes the UDP Layer 100% compatible. + By default, the old version (``multicast_config_version = v1``) is enabled in the :file:`ecal.yaml`, which makes the UDP Layer 100% compatible. If the new version (``v2``) is enabled, UDP communication between eCAL 5.12 and older versions of eCAL will fail. * **eCAL TCP**: 100% compatible diff --git a/doc/rst/versions/5.8/compatibility_table.txt b/doc/rst/versions/5.8/compatibility_table.txt index 36a50a205e..a17a8639af 100644 --- a/doc/rst/versions/5.8/compatibility_table.txt +++ b/doc/rst/versions/5.8/compatibility_table.txt @@ -19,6 +19,6 @@ * - Additional notes - * On Ubuntu, the official builds changes the lib dirs to the proper multiarch lib dirs. - * On Ubuntu the location of :file:`ecal.ini` changed to ``/etc/ecal/ecal.ini`` - * On Windows, the location of :file:`ecal.ini` changed to ``C:\ProgramData\eCAL\ecal.ini`` + * On Ubuntu the location of :file:`ecal.yaml` changed to ``/etc/ecal/ecal.yaml`` + * On Windows, the location of :file:`ecal.yaml` changed to ``C:\ProgramData\eCAL\ecal.yaml`` diff --git a/ecal/CMakeLists.txt b/ecal/CMakeLists.txt index 8463fa41a9..d24ef206a9 100644 --- a/ecal/CMakeLists.txt +++ b/ecal/CMakeLists.txt @@ -34,6 +34,9 @@ endif() # core config # -------------------------------------------------------- add_subdirectory(core/cfg) +if(NOT CMAKE_CROSSCOMPILING) + add_subdirectory(core/cfg/gen) +endif() # -------------------------------------------------------- # services diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index 5a93f34118..fe7e1c6e2c 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -22,8 +22,10 @@ find_package(asio REQUIRED) find_package(Threads REQUIRED) find_package(ecaludp REQUIRED) -if (ECAL_CORE_CONFIG_INIFILE) - find_package(simpleini REQUIRED) +if (ECAL_CORE_CONFIGURATION) + find_package(yaml-cpp REQUIRED) + include(${ECAL_PROJECT_ROOT}/thirdparty/yaml-cpp/compatibility-yaml-cpp.cmake) + yaml_cpp_create_compatibility_targets() endif() if (ECAL_CORE_COMMAND_LINE) @@ -63,16 +65,19 @@ endif() # config ###################################### set(ecal_config_src + src/config/default_configuration.cpp src/config/ecal_cmd_parser.cpp src/config/ecal_config.cpp src/config/ecal_config_initializer.cpp - src/config/ecal_config_reader.cpp - src/config/ecal_config_reader.h - src/config/ecal_config_reader_hlp.h + src/config/transport_layer.cpp src/types/ecal_custom_data_types.cpp - src/types/ecal_registration_options.cpp ) - +if (ECAL_CORE_CONFIGURATION) + list(APPEND ecal_config_src + src/config/configuration_to_yaml.cpp + src/config/configuration_reader.cpp + ) +endif() ###################################### # io/mtx ###################################### @@ -199,7 +204,6 @@ endif() if(ECAL_CORE_PUBLISHER) set(ecal_pub_src src/pubsub/ecal_publisher.cpp - src/pubsub/ecal_publisher_config.cpp src/pubsub/ecal_pubgate.cpp src/pubsub/ecal_pubgate.h ) @@ -208,7 +212,6 @@ endif() if(ECAL_CORE_SUBSCRIBER) set(ecal_sub_src src/pubsub/ecal_subscriber.cpp - src/pubsub/ecal_subscriber_config.cpp src/pubsub/ecal_subgate.cpp src/pubsub/ecal_subgate.h ) @@ -217,6 +220,10 @@ endif() ###################################### # readwrite ###################################### +set(ecal_readwrite_src + src/readwrite/ecal_transport_layer.h +) + if(ECAL_CORE_PUBLISHER) set(ecal_writer_src src/readwrite/ecal_writer.cpp @@ -278,15 +285,33 @@ endif() ###################################### if (ECAL_CORE_REGISTRATION) set(ecal_registration_src + src/registration/ecal_process_registration.cpp + src/registration/ecal_process_registration.h + src/registration/ecal_registration.cpp src/registration/ecal_registration_provider.cpp src/registration/ecal_registration_provider.h src/registration/ecal_registration_receiver.cpp src/registration/ecal_registration_receiver.h + src/registration/ecal_registration_sample_applier.cpp + src/registration/ecal_registration_sample_applier.h + src/registration/ecal_registration_sample_applier_gates.cpp + src/registration/ecal_registration_sample_applier_gates.h + src/registration/ecal_registration_sample_applier_user.cpp + src/registration/ecal_registration_sample_applier_user.h + src/registration/ecal_registration_timeout_provider.cpp + src/registration/ecal_registration_timeout_provider.h + src/registration/ecal_registration_sender.h + src/registration/udp/ecal_registration_receiver_udp.cpp + src/registration/udp/ecal_registration_receiver_udp.h + src/registration/udp/ecal_registration_sender_udp.cpp + src/registration/udp/ecal_registration_sender_udp.h ) if(ECAL_CORE_REGISTRATION_SHM) list(APPEND ecal_registration_src - src/registration/ecal_registration_receiver_shm.cpp - src/registration/ecal_registration_receiver_shm.h + src/registration/shm/ecal_registration_receiver_shm.cpp + src/registration/shm/ecal_registration_receiver_shm.h + src/registration/shm/ecal_registration_sender_shm.cpp + src/registration/shm/ecal_registration_sender_shm.h src/registration/shm/ecal_memfile_broadcast.cpp src/registration/shm/ecal_memfile_broadcast.h src/registration/shm/ecal_memfile_broadcast_reader.cpp @@ -397,6 +422,7 @@ endif() # common ###################################### set(ecal_cmn_src + src/builder/monitoring_attribute_builder.cpp src/ecal.cpp src/ecal_def.h src/ecal_def_ini.h @@ -419,6 +445,17 @@ if (WIN32) ) endif() +###################################### +# builder +###################################### +set (ecal_builder_src + src/builder/registration_attribute_builder.cpp + src/registration/builder/udp_shm_attribute_builder.cpp + src/registration/builder/sample_applier_attribute_builder.cpp + src/registration/udp/builder/udp_attribute_builder.cpp +) + + ###################################### # c interface ###################################### @@ -431,6 +468,7 @@ set(ecal_c_src src/cimpl/ecal_monitoring_cimpl.cpp src/cimpl/ecal_process_cimpl.cpp src/cimpl/ecal_publisher_cimpl.cpp + src/cimpl/ecal_registration_cimpl.cpp src/cimpl/ecal_server_cimpl.cpp src/cimpl/ecal_subscriber_cimpl.cpp src/cimpl/ecal_time_cimpl.cpp @@ -454,6 +492,7 @@ endif() set(ecal_header_cmn include/ecal/types/logging.h include/ecal/types/monitoring.h + include/ecal/config/configuration.h include/ecal/config/publisher.h include/ecal/config/subscriber.h include/ecal/ecal.h @@ -469,6 +508,7 @@ set(ecal_header_cmn include/ecal/ecal_monitoring.h include/ecal/ecal_process.h include/ecal/ecal_process_severity.h + include/ecal/ecal_registration.h include/ecal/ecal_publisher.h include/ecal/ecal_server.h include/ecal/ecal_service_info.h @@ -478,7 +518,6 @@ set(ecal_header_cmn include/ecal/ecal_tlayer.h include/ecal/ecal_types.h include/ecal/ecal_util.h - include/ecal/config/configuration.h ) set(ecal_header_cimpl @@ -490,6 +529,7 @@ set(ecal_header_cimpl include/ecal/cimpl/ecal_monitoring_cimpl.h include/ecal/cimpl/ecal_process_cimpl.h include/ecal/cimpl/ecal_publisher_cimpl.h + include/ecal/cimpl/ecal_registration_cimpl.h include/ecal/cimpl/ecal_server_cimpl.h include/ecal/cimpl/ecal_service_info_cimpl.h include/ecal/cimpl/ecal_subscriber_cimpl.h @@ -528,6 +568,7 @@ set(ecal_sources ${ecal_monitoring_src} ${ecal_pub_src} ${ecal_sub_src} + ${ecal_readwrite_src} ${ecal_writer_src} ${ecal_reader_src} ${ecal_registration_src} @@ -536,6 +577,7 @@ set(ecal_sources ${ecal_time_src} ${ecal_util_src} ${ecal_cmn_src} + ${ecal_builder_src} ${ecal_header_cmn} ${ecal_header_msg} ) @@ -545,6 +587,10 @@ ecal_add_ecal_shared_library(${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR}/include/ecal/ecal_defs.h ) +if (ECAL_CORE_CONFIGURATION) + target_compile_definitions(${PROJECT_NAME} PRIVATE ECAL_CORE_CONFIGURATION) +endif() + if(UNIX) set_source_files_properties(src/util/convert_utf.cpp PROPERTIES COMPILE_FLAGS -Wno-implicit-fallthrough) endif() @@ -587,7 +633,7 @@ target_compile_definitions(${PROJECT_NAME} ) set(ECAL_CORE_FEATURES - ECAL_CORE_CONFIG_INIFILE + ECAL_CORE_CONFIGURATION ECAL_CORE_COMMAND_LINE ECAL_CORE_REGISTRATION ECAL_CORE_MONITORING @@ -609,10 +655,10 @@ foreach(CORE_FEATURE ${ECAL_CORE_FEATURES}) endif() endforeach() -if(ECAL_CORE_CONFIG_INIFILE) +if(ECAL_CORE_CONFIGURATION) target_link_libraries(${PROJECT_NAME} PRIVATE - simpleini::simpleini + yaml-cpp::yaml-cpp ) endif() diff --git a/ecal/core/cfg/CMakeLists.txt b/ecal/core/cfg/CMakeLists.txt index e577b77888..bf3456e4c1 100644 --- a/ecal/core/cfg/CMakeLists.txt +++ b/ecal/core/cfg/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,12 +16,14 @@ # # ========================= eCAL LICENSE ================================= +set(ECALTIME_INI "ecaltime.ini") + # Merge all ecaltime config files # -> The ecaltime.ini will only contain valid settings for the time modules that were actually compiled. -file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/ecaltime.ini "") +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${ECALTIME_INI} "") foreach(ECALTIME_CONFIG ${ECALTIME_CONFIG_FILES}) file(READ ${ECALTIME_CONFIG} CONTENTS) - file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/ecaltime.ini "${CONTENTS}") + file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/${ECALTIME_INI} "${CONTENTS}") endforeach() # Select the correct ecal config directory on Linux and Windows @@ -33,24 +35,13 @@ else() message(STATUS "Unsupported OS, not installing configs") endif() -configure_file( - ecal.ini - ecal.ini -) # Install the configs if(ECAL_SHARED_CONFIG AND NOT CMAKE_CROSSCOMPILING) - install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/ecal.ini - DESTINATION - ${ECAL_SHARED_CONFIG} - COMPONENT configuration - ) if(BUILD_TIME) install( FILES - ${CMAKE_CURRENT_BINARY_DIR}/ecaltime.ini + ${CMAKE_CURRENT_BINARY_DIR}/${ECALTIME_INI} DESTINATION ${ECAL_SHARED_CONFIG} COMPONENT configuration diff --git a/ecal/core/cfg/ecal.ini b/ecal/core/cfg/ecal.ini deleted file mode 100644 index 1fb5a73896..0000000000 --- a/ecal/core/cfg/ecal.ini +++ /dev/null @@ -1,190 +0,0 @@ -; -------------------------------------------------- -; NETWORK SETTINGS -; -------------------------------------------------- -; network_enabled = true / false true = all eCAL components communicate over network boundaries -; false = local host only communication -; -; multicast_config_version = v1 / v2 UDP configuration version (Since eCAL 5.12.) -; v1: default behavior -; v2: new behavior, comes with a bit more intuitive handling regarding masking of the groups -; multicast_group = 239.0.0.1 UDP multicast group base -; All registration and logging is sent on this address -; multicast_mask = 0.0.0.1-0.0.0.255 v1: Mask maximum number of dynamic multicast group -; 255.0.0.0-255.255.255.255 v2: masks are now considered like routes masking -; -; multicast_port = 14000 + x UDP multicast port number (eCAL will use at least the 2 following port -; numbers too, so please modify in steps of 10 (e.g. 1010, 1020 ...) -; -; multicast_ttl = 0 + x UDP ttl value, also known as hop limit, is used in determining -; the intermediate routers being traversed towards the destination -; -; multicast_sndbuf = 1024 * x UDP send buffer in bytes -; -; multicast_rcvbuf = 1024 * x UDP receive buffer in bytes -; -; multicast_join_all_if = false Linux specific setting to enable joining multicast groups on all network interfacs -; independent of their link state. Enabling this makes sure that eCAL processes -; receive data if they are started before network devices are up and running. -; -; shm_rec_enabled = true Enable to receive on eCAL shared memory layer -; tcp_rec_enabled = true Enable to receive on eCAL tcp layer -; udp_mc_rec_enabled = true Enable to receive on eCAL udp multicast layer -; -; npcap_enabled = false Enable to receive UDP traffic with the Npcap based receiver -; -; tcp_pubsub_num_executor_reader = 4 Tcp_pubsub reader amount of threads that shall execute workload -; tcp_pubsub_num_executor_writer = 4 Tcp_pubsub writer amount of threads that shall execute workload -; tcp_pubsub_max_reconnections = 5 Tcp_pubsub reconnection attemps the session will try to reconnect in -; case of an issue (a negative value means infinite reconnection attemps) -; -; host_group_name = Common host group name that enables interprocess mechanisms across -; (virtual) host borders (e.g, Docker); by default equivalent to local host name -; -------------------------------------------------- - -[network] -network_enabled = false -multicast_config_version = v1 -multicast_group = 239.0.0.1 -multicast_mask = 0.0.0.15 -multicast_port = 14000 -multicast_ttl = 2 -multicast_sndbuf = 5242880 -multicast_rcvbuf = 5242880 - -multicast_join_all_if = false - -shm_rec_enabled = true -tcp_rec_enabled = true -udp_mc_rec_enabled = true - -npcap_enabled = false - -tcp_pubsub_num_executor_reader = 4 -tcp_pubsub_num_executor_writer = 4 -tcp_pubsub_max_reconnections = 5 - -host_group_name = - -; -------------------------------------------------- -; COMMON SETTINGS -; -------------------------------------------------- -; registration_timeout = 60000 Timeout for topic registration in ms (internal) -; registration_refresh = 1000 Topic registration refresh cylce (has to be smaller then registration timeout !) - -; -------------------------------------------------- -[common] -registration_timeout = 60000 -registration_refresh = 1000 - -; -------------------------------------------------- -; TIME SETTINGS -; -------------------------------------------------- -; timesync_module_rt = ecaltime-localtime Time synchronisation interface name (dynamic library) -; The name will be extended with platform suffix (32|64), debug suffix (d) and platform extension (.dll|.so) -; -; Available modules are: -; - ecaltime-localtime local system time without synchronization -; - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux -; (device configuration in ecaltime.ini) -; -------------------------------------------------- -[time] -timesync_module_rt = ecaltime-localtime - -; --------------------------------------------- -; PROCESS SETTINGS -; --------------------------------------------- -; -; terminal_emulator = /usr/bin/x-terminal-emulator -e command for starting applications with an external terminal emulator. If empty, the command will be ignored. Ignored on Windows. -; e.g. /usr/bin/x-terminal-emulator -e -; /usr/bin/gnome-terminal -x -; /usr/bin/xterm -e -; -; --------------------------------------------- -[process] -terminal_emulator = - -; -------------------------------------------------- -; PUBLISHER SETTINGS -; -------------------------------------------------- -; use_shm = 0, 1, 2 Use shared memory transport layer (0 = off, 1 = on, 2 = auto, default = 2) -; use_tcp = 0, 1, 2 Use tcp transport layer (0 = off, 1 = on, 2 = auto, default = 0) -; use_udp_mc = 0, 1, 2 Use udp multicast transport layer (0 = off, 1 = on, 2 = auto, default = 2) -; -; memfile_minsize = x * 4096 kB Default memory file size for new publisher -; -; memfile_reserve = 50 .. x % Dynamic file size reserve before recreating memory file if topic size changes -; -; memfile_ack_timeout = 0 .. x ms Publisher timeout for ack event from subscriber that memory file content is processed -; -; memfile_buffer_count = 1 .. x Number of parallel used memory file buffers for 1:n publish/subscribe ipc connections (default = 1) -; memfile_zero_copy = 0, 1 Allow matching subscriber to access memory file without copying its content in advance (blocking mode) -; -; share_ttype = 0, 1 Share topic type via registration layer -; share_tdesc = 0, 1 Share topic description via registration layer (switch off to disable reflection) -; -------------------------------------------------- -[publisher] -use_shm = 2 -use_tcp = 0 -use_udp_mc = 2 - -memfile_minsize = 4096 -memfile_reserve = 50 -memfile_ack_timeout = 0 -memfile_buffer_count = 1 -memfile_zero_copy = 0 - -share_ttype = 1 -share_tdesc = 1 - -; -------------------------------------------------- -; SERVICE SETTINGS -; -------------------------------------------------- -; protocol_v0 = 0, 1 Support service protocol v0, eCAL 5.11 and older (0 = off, 1 = on) -; protocol_v1 = 0, 1 Support service protocol v1, eCAL 5.12 and newer (0 = off, 1 = on) -; -------------------------------------------------- -[service] -protocol_v0 = 1 -protocol_v1 = 1 - -; -------------------------------------------------- -; MONITORING SETTINGS -; -------------------------------------------------- -; timeout = 1000 + (x * 1000) Timeout for topic monitoring in ms -; filter_excl = __.* Topics blacklist as regular expression (will not be monitored) -; filter_incl = Topics whitelist as regular expression (will be monitored only) -; filter_log_con = info, warning, error, fatal Log messages logged to console (all, info, warning, error, fatal, debug1, debug2, debug3, debug4) -; filter_log_file = Log messages to logged into file system -; filter_log_udp = info, warning, error, fatal Log messages logged via udp network -; -------------------------------------------------- -[monitoring] -timeout = 5000 -filter_excl = __.* -filter_incl = -filter_log_con = info, warning, error, fatal -filter_log_file = -filter_log_udp = info, warning, error, fatal - -; -------------------------------------------------- -; SYS SETTINGS -; -------------------------------------------------- -; filter_excl = App1,App2 Apps blacklist to be excluded when importing tasks from cloud -; -------------------------------------------------- -[sys] -filter_excl = ^eCALSysClient$|^eCALSysGUI$|^eCALSys$ - -; -------------------------------------------------- -; EXPERIMENTAL SETTINGS -; -------------------------------------------------- -; shm_monitoring_enabled = false Enable distribution of monitoring/registration information via shared memory -; shm_monitoring_domain = ecal_monitoring Domain name for shared memory based monitoring/registration -; shm_monitoring_queue_size = 1024 Queue size of monitoring/registration events -; network_monitoring_enabled = true Enable distribution of monitoring/registration information via network -; -; drop_out_of_order_messages = false Enable dropping of payload messages that arrive out of order -; -------------------------------------------------- -[experimental] -shm_monitoring_enabled = false -shm_monitoring_domain = ecal_mon -shm_monitoring_queue_size = 1024 -network_monitoring_enabled = true -drop_out_of_order_messages = false diff --git a/ecal/core/cfg/gen/CMakeLists.txt b/ecal/core/cfg/gen/CMakeLists.txt new file mode 100644 index 0000000000..76d0ca35ae --- /dev/null +++ b/ecal/core/cfg/gen/CMakeLists.txt @@ -0,0 +1,77 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2024 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +project(generate_config) + +# Set the name of the YAML file to be generated +set(ECAL_YAML "ecal.yaml") + +add_executable(${PROJECT_NAME} + ${ECAL_CORE_PROJECT_ROOT}/core/cfg/gen/generate_configuration_yaml.cpp + ${ECAL_CORE_PROJECT_ROOT}/core/src/config/default_configuration.cpp +) + +# Set the output path for the generated YAML file +set(YAML_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/${ECAL_YAML}) + +# Add a custom command to generate the YAML file +add_custom_command( + OUTPUT ${YAML_OUTPUT_PATH} # Specify the output file + COMMAND $ # Command to run the executable + DEPENDS ${PROJECT_NAME} # Ensure the executable is built before running the command + COMMENT "Generating ${YAML_OUTPUT_PATH}" # Comment to display during the build process + VERBATIM # Ensure command arguments are passed as-is +) + +# Add a custom target that depends on the generated YAML file +add_custom_target(run_${PROJECT_NAME} ALL + DEPENDS ${YAML_OUTPUT_PATH} # Ensure the custom command is executed +) + +# Ensure the executable is built before running the custom target +add_dependencies(run_${PROJECT_NAME} ${PROJECT_NAME}) + +# Specify include directories for the executable +target_include_directories(${PROJECT_NAME} PRIVATE + ${ECAL_CORE_PROJECT_ROOT}/core/src + ${ECAL_CORE_PROJECT_ROOT}/core/include +) + +target_link_libraries(${PROJECT_NAME} PRIVATE + eCAL::core +) + +# Select the correct ecal config directory on Linux and Windows +if(UNIX) + set(ECAL_SHARED_CONFIG ${eCAL_install_config_dir}) +elseif(WIN32) + set(ECAL_SHARED_CONFIG ${eCAL_install_config_dir}) +else() + message(STATUS "Unsupported OS, not installing configs") +endif() + +# Install the generated YAML file if not cross-compiling +if(ECAL_SHARED_CONFIG) + install( + FILES + ${YAML_OUTPUT_PATH} # File to install + DESTINATION + ${ECAL_SHARED_CONFIG} # Destination directory + COMPONENT configuration # Installation component + ) +endif() \ No newline at end of file diff --git a/ecal/core/cfg/gen/generate_configuration_yaml.cpp b/ecal/core/cfg/gen/generate_configuration_yaml.cpp new file mode 100644 index 0000000000..c8a83e97b6 --- /dev/null +++ b/ecal/core/cfg/gen/generate_configuration_yaml.cpp @@ -0,0 +1,11 @@ +#include "config/default_configuration.h" + +int main() { + if (!eCAL::Config::dumpConfigToFile()) + { + std::cerr << "Failed to write default configuration to file." << std::endl; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/ecal/core/include/ecal/cimpl/ecal_registration_cimpl.h b/ecal/core/include/ecal/cimpl/ecal_registration_cimpl.h new file mode 100644 index 0000000000..7b11172c11 --- /dev/null +++ b/ecal/core/include/ecal/cimpl/ecal_registration_cimpl.h @@ -0,0 +1,176 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @file ecal_registration_cimpl.h + * @brief eCAL registration c interface +**/ + +#ifndef ecal_registration_cimpl_h_included +#define ecal_registration_cimpl_h_included + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /*__cplusplus*/ + /** + * @brief Gets type name of the specified topic. + * + * @param topic_name_ Topic name. + * @param [out] topic_type_ Pointer to store the type name information. + * @param topic_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Type name buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetTopicTypeName(const char* topic_name_, void* topic_type_, int topic_type_len_); + + /** + * @brief Gets encoding of the specified topic. + * + * @param topic_name_ Topic name. + * @param [out] topic_encoding_ Pointer to store the encoding information. + * @param topic_encoding__len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Type name buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetTopicEncoding(const char* topic_name_, void* topic_encoding_, int topic_encoding_len_); + + /** + * @brief Gets type description of the specified topic. + * + * @param topic_name_ Topic name. + * @param [out] topic_desc_ Pointer to store the type description0 information. + * @param topic_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Type name buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetTopicDescription(const char* topic_name_, void* topic_desc_, int topic_desc_len_); + + /** + * @brief Gets service method request type name. + * + * @param service_name_ Service name. + * @param method_name_ Method name. + * @param [out] req_type_ Pointer to store the request type. + * @param req_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Type name buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetServiceRequestTypeName(const char* service_name_, const char* method_name_, void* req_type_, int req_type_len_); + + /** + * @brief Gets service method response type name. + * + * @param service_name_ Service name. + * @param method_name_ Method name. + * @param [out] resp_type_ Pointer to store the response type. + * @param resp_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * + * @return Type name buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetServiceResponseTypeName(const char* service_name_, const char* method_name_, void* resp_type_, int resp_type_len_); + + /** + * @brief Gets service method request description. + * + * @param service_name_ Service name. + * @param method_name_ Method name. + * @param [out] req_desc_ Pointer to store the request description. + * @param req_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Request description buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetServiceRequestDescription(const char* service_name_, const char* method_name_, void* req_desc_, int req_desc_len_); + + /** + * @brief Gets service method response description. + * + * @param service_name_ Service name. + * @param method_name_ Method name. + * @param [out] resp_desc_ Pointer to store the response description. + * @param resp_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Response description buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetServiceResponseDescription(const char* service_name_, const char* method_name_, void* resp_desc_, int resp_desc_len_); + + /** + * @brief Gets client method request type name. + * + * @param client_name_ Client name. + * @param method_name_ Method name. + * @param [out] req_type_ Pointer to store the request type. + * @param req_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Type name buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetClientRequestTypeName(const char* client_name_, const char* method_name_, void* req_type_, int req_type_len_); + + /** + * @brief Gets client method response type name. + * + * @param client_name_ Client name. + * @param method_name_ Method name. + * @param [out] resp_type_ Pointer to store the response type. + * @param resp_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * + * @return Type name buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetClientResponseTypeName(const char* client_name_, const char* method_name_, void* resp_type_, int resp_type_len_); + + /** + * @brief Gets client method request description. + * + * @param client_name_ Client name. + * @param method_name_ Method name. + * @param [out] req_desc_ Pointer to store the request description. + * @param req_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Request description buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetClientRequestDescription(const char* client_name_, const char* method_name_, void* req_desc_, int req_desc_len_); + + /** + * @brief Gets client method response description. + * + * @param client_name_ Client name. + * @param method_name_ Method name. + * @param [out] resp_desc_ Pointer to store the response description. + * @param resp_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if + * eCAL should allocate the buffer for you (see eCAL_FreeMem). + * + * @return Response description buffer length or zero if failed. + **/ + ECALC_API int eCAL_Registration_GetClientResponseDescription(const char* client_name_, const char* method_name_, void* resp_desc_, int resp_desc_len_); +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*ecal_registration_cimpl_h_included*/ diff --git a/ecal/core/include/ecal/cimpl/ecal_util_cimpl.h b/ecal/core/include/ecal/cimpl/ecal_util_cimpl.h index d5d0cb2f09..02cbd273cb 100644 --- a/ecal/core/include/ecal/cimpl/ecal_util_cimpl.h +++ b/ecal/core/include/ecal/cimpl/ecal_util_cimpl.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ #define ecal_util_cimpl_h_included #include -#include #ifdef __cplusplus extern "C" @@ -64,144 +63,6 @@ extern "C" * @param state_ Switch on message loop back.. **/ ECALC_API void eCAL_Util_EnableLoopback(int state_); - - /** - * @brief Gets type name of the specified topic. - * - * @param topic_name_ Topic name. - * @param [out] topic_type_ Pointer to store the type name information. - * @param topic_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Type name buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetTopicTypeName(const char* topic_name_, void* topic_type_, int topic_type_len_); - - /** - * @brief Gets encoding of the specified topic. - * - * @param topic_name_ Topic name. - * @param [out] topic_encoding_ Pointer to store the encoding information. - * @param topic_encoding__len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Type name buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetTopicEncoding(const char* topic_name_, void* topic_encoding_, int topic_encoding_len_); - - /** - * @brief Gets type description of the specified topic. - * - * @param topic_name_ Topic name. - * @param [out] topic_desc_ Pointer to store the type description0 information. - * @param topic_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Type name buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetTopicDescription(const char* topic_name_, void* topic_desc_, int topic_desc_len_); - - /** - * @brief Gets service method request type name. - * - * @param service_name_ Service name. - * @param method_name_ Method name. - * @param [out] req_type_ Pointer to store the request type. - * @param req_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Type name buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetServiceRequestTypeName(const char* service_name_, const char* method_name_, void* req_type_, int req_type_len_); - - /** - * @brief Gets service method response type name. - * - * @param service_name_ Service name. - * @param method_name_ Method name. - * @param [out] resp_type_ Pointer to store the response type. - * @param resp_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * - * @return Type name buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetServiceResponseTypeName(const char* service_name_, const char* method_name_, void* resp_type_, int resp_type_len_); - - /** - * @brief Gets service method request description. - * - * @param service_name_ Service name. - * @param method_name_ Method name. - * @param [out] req_desc_ Pointer to store the request description. - * @param req_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Request description buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetServiceRequestDescription(const char* service_name_, const char* method_name_, void* req_desc_, int req_desc_len_); - - /** - * @brief Gets service method response description. - * - * @param service_name_ Service name. - * @param method_name_ Method name. - * @param [out] resp_desc_ Pointer to store the response description. - * @param resp_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Response description buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetServiceResponseDescription(const char* service_name_, const char* method_name_, void* resp_desc_, int resp_desc_len_); - - /** - * @brief Gets client method request type name. - * - * @param client_name_ Client name. - * @param method_name_ Method name. - * @param [out] req_type_ Pointer to store the request type. - * @param req_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Type name buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetClientRequestTypeName(const char* client_name_, const char* method_name_, void* req_type_, int req_type_len_); - - /** - * @brief Gets client method response type name. - * - * @param client_name_ Client name. - * @param method_name_ Method name. - * @param [out] resp_type_ Pointer to store the response type. - * @param resp_type_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * - * @return Type name buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetClientResponseTypeName(const char* client_name_, const char* method_name_, void* resp_type_, int resp_type_len_); - - /** - * @brief Gets client method request description. - * - * @param client_name_ Client name. - * @param method_name_ Method name. - * @param [out] req_desc_ Pointer to store the request description. - * @param req_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Request description buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetClientRequestDescription(const char* client_name_, const char* method_name_, void* req_desc_, int req_desc_len_); - - /** - * @brief Gets client method response description. - * - * @param client_name_ Client name. - * @param method_name_ Method name. - * @param [out] resp_desc_ Pointer to store the response description. - * @param resp_desc_len_ Length of allocated buffer or ECAL_ALLOCATE_4ME if - * eCAL should allocate the buffer for you (see eCAL_FreeMem). - * - * @return Response description buffer length or zero if failed. - **/ - ECALC_API int eCAL_Util_GetClientResponseDescription(const char* client_name_, const char* method_name_, void* resp_desc_, int resp_desc_len_); #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/ecal/core/include/ecal/config/application.h b/ecal/core/include/ecal/config/application.h index e08367a24a..8628ec22e6 100644 --- a/ecal/core/include/ecal/config/application.h +++ b/ecal/core/include/ecal/config/application.h @@ -36,7 +36,7 @@ namespace eCAL { struct Configuration { - std::string filter_excl; //!< + std::string filter_excl { "^eCALSysClient$|^eCALSysGUI$|^eCALSys$" }; //!< Apps blacklist to be excluded when importing tasks from cloud }; } @@ -44,14 +44,14 @@ namespace eCAL { struct Configuration { - std::string terminal_emulator; //!< + std::string terminal_emulator { "" }; //!< Linux only command for starting applications with an external terminal emulator }; } struct Configuration { - Sys::Configuration sys; //!< - Startup::Configuration startup; //!< + Sys::Configuration sys; + Startup::Configuration startup; }; } } \ No newline at end of file diff --git a/ecal/core/include/ecal/config/configuration.h b/ecal/core/include/ecal/config/configuration.h index 101c890dea..47c4b3fc53 100644 --- a/ecal/core/include/ecal/config/configuration.h +++ b/ecal/core/include/ecal/config/configuration.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -47,34 +46,29 @@ namespace eCAL { - struct Configuration - { - TransportLayer::Configuration transport_layer{}; - Registration::Configuration registration{}; - Monitoring::Configuration monitoring{}; - Subscriber::Configuration subscriber{}; - Publisher::Configuration publisher{}; - Time::Configuration timesync{}; - Service::Configuration service{}; - Application::Configuration application{}; - Logging::Configuration logging{}; - Cli::Configuration command_line_arguments{}; - - ECAL_API Configuration(); - ECAL_API Configuration(int argc_ , char **argv_); - ECAL_API Configuration(std::vector& args_); + struct Configuration + { + TransportLayer::Configuration transport_layer; + Registration::Configuration registration; + Monitoring::Configuration monitoring; + Subscriber::Configuration subscriber; + Publisher::Configuration publisher; + Time::Configuration timesync; + Service::Configuration service; + Application::Configuration application; + Logging::Configuration logging; + Cli::Configuration command_line_arguments; - ECAL_API void InitConfigWithDefaultIni(); - ECAL_API void InitConfig(std::string ini_path_ = std::string("")); + ECAL_API Configuration(); + ECAL_API Configuration(int argc_, char** argv_); + ECAL_API Configuration(const std::vector& args_); - ECAL_API std::string GetIniFilePath(); + ECAL_API void InitFromConfig(); + ECAL_API void InitFromFile(const std::string& yaml_path_); - friend class CmdParser; + ECAL_API std::string GetYamlFilePath(); - protected: - std::string ecal_ini_file_path{}; - - private: - ECAL_API void Init(std::vector& args_); - }; -} \ No newline at end of file + protected: + std::string ecal_yaml_file_path; + }; +} diff --git a/ecal/core/include/ecal/config/logging.h b/ecal/core/include/ecal/config/logging.h index bb1ae9ef45..d5a82f322e 100644 --- a/ecal/core/include/ecal/config/logging.h +++ b/ecal/core/include/ecal/config/logging.h @@ -26,16 +26,63 @@ #include +#include +#include + +namespace +{ + // After switchting to c++17, this can be replaced by an inline constexpr + static const eCAL_Logging_Filter log_level_default = log_level_info | log_level_warning | log_level_error | log_level_fatal; +} + namespace eCAL { namespace Logging { + namespace Sinks + { + namespace Console + { + struct Configuration + { + bool enable { true }; //!< Enable console logging (Default: true) + eCAL_Logging_Filter filter_log_con { log_level_error | log_level_fatal }; /*!< Log messages logged to console (all, info, warning, error, fatal, debug1, debug2, debug3, debug4) + (Default: info, warning, error, fatal)*/ + }; + } + + namespace File + { + struct Configuration + { + bool enable { false }; //!< Enable file logging (Default: false) + std::string path { "" }; //!< Path to log file (Default: "") + eCAL_Logging_Filter filter_log_file { log_level_none }; /*!< Log messages logged into file system (all, info, warning, error, fatal, debug1, debug2, debug3, debug4) + (Default: info, warning, error, fatal)*/ + }; + } + + namespace UDP + { + struct Configuration + { + bool enable { true }; //!< Enable UDP logging (Default: false) + unsigned int port { 14001 }; //!< UDP port number (Default: 14001) + eCAL_Logging_Filter filter_log_udp { log_level_default }; //!< Log messages logged via udp network (Default: info, warning, error, fatal) + }; + } + + struct Configuration + { + Console::Configuration console; + File::Configuration file; + UDP::Configuration udp; + }; + } + struct Configuration { - eCAL_Logging_Filter filter_log_con{}; /*!< Log messages logged to console (all, info, warning, error, fatal, debug1, debug2, debug3, debug4) - (Default: info, warning, error, fatal)*/ - eCAL_Logging_Filter filter_log_file{}; //!< Log messages to logged into file system (Default: "") - eCAL_Logging_Filter filter_log_udp{}; //!< Log messages logged via udp network (Default: info, warning, error, fatal) + Sinks::Configuration sinks; }; } } \ No newline at end of file diff --git a/ecal/core/include/ecal/config/monitoring.h b/ecal/core/include/ecal/config/monitoring.h index 2c116202ff..3a085ce72c 100644 --- a/ecal/core/include/ecal/config/monitoring.h +++ b/ecal/core/include/ecal/config/monitoring.h @@ -30,44 +30,10 @@ namespace eCAL { namespace Monitoring { - namespace Types - { - enum Mode - { - none = 0, - udp_monitoring = 1 << 0, - shm_monitoring = 1 << 1 - }; - - using Mode_Filter = char; - } - - namespace UDP - { - struct Configuration - { - }; - } - - namespace SHM - { - struct Configuration - { - std::string shm_monitoring_domain{}; //!< Domain name for shared memory based monitoring/registration (Default: ecal_mon) - size_t shm_monitoring_queue_size{}; //!< Queue size of monitoring/registration events (Default: 1024) - }; - } - struct Configuration { - Types::Mode_Filter monitoring_mode{}; //!< Specify which monitoring is enabled (Default: none) - eCAL::Types::ConstrainedInteger<1000, 1000> monitoring_timeout{}; //!< Timeout for topic monitoring in ms (Default: 5000) - bool network_monitoring{}; //!< Enable distribution of monitoring/registration information via network (Default: true) - UDP::Configuration udp_options{}; - SHM::Configuration shm_options{}; - - std::string filter_excl{}; //!< Topics blacklist as regular expression (will not be monitored) (Default: "__.*") - std::string filter_incl{}; //!< Topics whitelist as regular expression (will be monitored only) (Default: "") + std::string filter_excl { "^__.*$" }; //!< Topics blacklist as regular expression (will not be monitored) (Default: "^__.*$") + std::string filter_incl { "" }; //!< Topics whitelist as regular expression (will be monitored only) (Default: "") }; } } \ No newline at end of file diff --git a/ecal/core/include/ecal/config/publisher.h b/ecal/core/include/ecal/config/publisher.h index 1af155c9b3..802c932925 100644 --- a/ecal/core/include/ecal/config/publisher.h +++ b/ecal/core/include/ecal/config/publisher.h @@ -88,57 +88,66 @@ #pragma once -#include +#include #include +#include #include +#include namespace eCAL { namespace Publisher { - namespace SHM + namespace Layer { - struct Configuration + namespace SHM { - bool enable; //!< enable layer - bool zero_copy_mode; //!< enable zero copy shared memory transport mode - unsigned int acknowledge_timeout_ms; /*!< force connected subscribers to send acknowledge event after processing the message - the publisher send call is blocked on this event with this timeout (0 == no handshake) */ - Types::ConstrainedInteger<4096, 4096> memfile_min_size_bytes; //!< default memory file size for new publisher - Types::ConstrainedInteger<50, 1, 100> memfile_reserve_percent; //!< dynamic file size reserve before recreating memory file if topic size changes - Types::ConstrainedInteger<1, 1> memfile_buffer_count; //!< maximum number of used buffers (needs to be greater than 1, default = 1) - }; - } + struct Configuration + { + bool enable { true }; //!< enable layer - namespace UDP - { - struct Configuration + bool zero_copy_mode { false }; //!< Enable zero copy shared memory transport mode + unsigned int acknowledge_timeout_ms { 0U }; /*!< Force connected subscribers to send acknowledge event after processing the message. + The publisher send call is blocked on this event with this timeout (0 == no handshake).*/ + unsigned int memfile_buffer_count { 1U }; /*!< Maximum number of used buffers (needs to be greater than 1, default = 1) */ + }; + } + + namespace UDP { - bool enable; //!< enable layer - bool loopback; //!< enable to receive udp messages on the same local machine - Types::ConstrainedInteger<5242880, 1024> sndbuf_size_bytes; //!< udp send buffer size in bytes (default 5MB) - }; - } + struct Configuration + { + bool enable { true }; //!< enable layer + }; + } + + namespace TCP + { + struct Configuration + { + bool enable { true }; //!< enable layer + }; + } - namespace TCP - { struct Configuration { - bool enable; //!< enable layer + SHM::Configuration shm; + UDP::Configuration udp; + TCP::Configuration tcp; }; } struct Configuration { - ECAL_API Configuration(); + Layer::Configuration layer; //!< Layer configuration - SHM::Configuration shm; - UDP::Configuration udp; - TCP::Configuration tcp; + using LayerPriorityVector = std::vector; + LayerPriorityVector layer_priority_local { TLayer::tlayer_shm, TLayer::tlayer_udp_mc, TLayer::tlayer_tcp }; + LayerPriorityVector layer_priority_remote { TLayer::tlayer_udp_mc, TLayer::tlayer_tcp }; - bool share_topic_type; //!< share topic type via registration - bool share_topic_description; //!< share topic description via registration + bool share_topic_type { true }; //!< share topic type via registration + bool share_topic_description { true }; //!< share topic description via registration }; } } diff --git a/ecal/core/include/ecal/config/registration.h b/ecal/core/include/ecal/config/registration.h index 46c88f5b0d..79f9798a06 100644 --- a/ecal/core/include/ecal/config/registration.h +++ b/ecal/core/include/ecal/config/registration.h @@ -33,32 +33,45 @@ namespace eCAL { namespace Registration { - /** - * @brief Struct for storing RegistrationOptions. - * If not specified, registration timeout and refresh times from eCAL predefines will be used. - * When specifying: reg_timeout >= reg_refresh. If not, an invalid_argument exception will be thrown. - * By default, share_ttype and share_tdesc is true based on eCAL predefines. - * - * @param reg_timeout_ Timeout for topic registration in ms - * @param reg_refresh_ Topic registration refresh cylce in ms - * - * @throws std::invalid_argument exception. - **/ - struct Configuration + namespace Layer { - public: - ECAL_API Configuration(); - ECAL_API Configuration(unsigned int reg_timeout_, unsigned int reg_refresh_); + namespace SHM + { + struct Configuration + { + bool enable { false }; /*!< Enable shared memory based registration (Default: false) */ + std::string domain { "ecal_mon" }; //!< Domain name for shared memory based registration (Default: ecal_mon) + size_t queue_size { 1024 }; //!< Queue size of registration events (Default: 1024) + }; + } + + namespace UDP + { + struct Configuration + { + bool enable { true }; /*!< Enable UDP based registration (Default: true) */ + unsigned int port { 14000 }; /*!< UDP multicast port number (Default: 14000) */ + }; + } - ECAL_API unsigned int getTimeoutMS() const; //!< Timeout for topic registration in ms (internal) (Default: 60000) - ECAL_API unsigned int getRefreshMS() const; //!< Topic registration refresh cylce (has to be smaller then registration timeout!) (Default: 1000) + struct Configuration + { + SHM::Configuration shm; /*!< Shared memory based registration configuration */ + UDP::Configuration udp; /*!< UDP based registration configuration */ + }; + } - bool share_ttype; //!< Share topic type via registration layer (Default: true) - bool share_tdesc; //!< Share topic description via registration layer (switch off to disable reflection) (Default: true) + struct Configuration + { + unsigned int registration_timeout { 10000U }; //!< Timeout for topic registration in ms (internal) (Default: 10000) + unsigned int registration_refresh { 1000U }; //!< Topic registration refresh cylce (has to be smaller then registration timeout!) (Default: 1000) - private: - unsigned int m_registration_timeout; - unsigned int m_registration_refresh; + bool network_enabled { false }; /*!< true = all eCAL components communicate over network boundaries + false = local host only communication (Default: false) */ + bool loopback { true }; //!< enable to receive udp messages on the same local machine (Default: true) + std::string host_group_name { "" }; /*!< Common host group name that enables interprocess mechanisms across + (virtual) host borders (e.g, Docker); by default equivalent to local host name (Default: "") */ + Layer::Configuration layer; }; } } \ No newline at end of file diff --git a/ecal/core/include/ecal/config/service.h b/ecal/core/include/ecal/config/service.h index 74f4b54291..3082a6c521 100644 --- a/ecal/core/include/ecal/config/service.h +++ b/ecal/core/include/ecal/config/service.h @@ -32,8 +32,8 @@ namespace eCAL { struct Configuration { - bool protocol_v0{}; //!< Support service protocol v0, eCAL 5.11 and older (Default: true) - bool protocol_v1{}; //!< Support service protocol v1, eCAL 5.12 and newer (Default: true) + bool protocol_v0 { false }; //!< Support service protocol v0, eCAL 5.11 and older (Default: false) + bool protocol_v1 { true }; //!< Support service protocol v1, eCAL 5.12 and newer (Default: true) }; } } \ No newline at end of file diff --git a/ecal/core/include/ecal/config/subscriber.h b/ecal/core/include/ecal/config/subscriber.h index d306250d43..121f5f659e 100644 --- a/ecal/core/include/ecal/config/subscriber.h +++ b/ecal/core/include/ecal/config/subscriber.h @@ -24,7 +24,7 @@ #pragma once -#include +#include #include @@ -32,37 +32,45 @@ namespace eCAL { namespace Subscriber { - namespace SHM + namespace Layer { - struct Configuration + namespace SHM { - bool enable; //!< enable layer - }; - } + struct Configuration + { + bool enable { true }; //!< enable layer (Default: true) + }; + } - namespace UDP - { - struct Configuration + namespace UDP { - bool enable; //!< enable layer - }; - } + struct Configuration + { + bool enable { true }; //!< enable layer (Default: true) + }; + } + + namespace TCP + { + struct Configuration + { + bool enable { false }; //!< enable layer (Default: false) + }; + } - namespace TCP - { struct Configuration { - bool enable; //!< enable layer + SHM::Configuration shm; + UDP::Configuration udp; + TCP::Configuration tcp; }; } struct Configuration { - ECAL_API Configuration(); + Layer::Configuration layer; - SHM::Configuration shm; - UDP::Configuration udp; - TCP::Configuration tcp; + bool drop_out_of_order_messages { true }; //!< Enable dropping of payload messages that arrive out of order }; } } diff --git a/ecal/core/include/ecal/config/time.h b/ecal/core/include/ecal/config/time.h index dbe62028c3..3c88e4a4c5 100644 --- a/ecal/core/include/ecal/config/time.h +++ b/ecal/core/include/ecal/config/time.h @@ -32,14 +32,14 @@ namespace eCAL { struct Configuration { - std::string timesync_module_rt{}; /*!< Time synchronisation interface name (dynamic library) - The name will be extended with platform suffix (32|64), debug suffix (d) and platform extension (.dll|.so) - Available modules are: - - ecaltime-localtime local system time without synchronization - - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux - (device configuration in ecaltime.ini) - (Default: ecaltime-localtime)*/ - std::string timesync_module_replay{}; //!< (Default: "") + std::string timesync_module_rt { "ecaltime-localtime" }; /*!< Time synchronisation interface name (dynamic library) + The name will be extended with platform suffix (32|64), debug suffix (d) and platform extension (.dll|.so) + Available modules are: + - ecaltime-localtime local system time without synchronization + - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux + (device configuration in ecaltime.ini) + (Default: ecaltime-localtime)*/ + std::string timesync_module_replay { "" }; //!< (Default: "") }; } } diff --git a/ecal/core/include/ecal/config/transport_layer.h b/ecal/core/include/ecal/config/transport_layer.h index 41aa5ad5ae..86c93d8123 100644 --- a/ecal/core/include/ecal/config/transport_layer.h +++ b/ecal/core/include/ecal/config/transport_layer.h @@ -22,73 +22,86 @@ * @brief eCAL configuration for the transport layer **/ -// TODO PG: Deprecated when configuration is implemented in all modules? #pragma once #include +#include namespace eCAL { namespace TransportLayer { - namespace TCPPubSub - { + namespace UDP + { + namespace Network + { + struct Configuration + { + Types::IpAddressV4 group { "239.0.0.1" }; //!< UDP multicast group base (Default: 239.0.0.1) + unsigned int ttl { 3U }; /*!< UDP ttl value, also known as hop limit, is used in determining + the intermediate routers being traversed towards the destination (Default: 3) */ + }; + } + + namespace Local + { + struct Configuration + { + Types::IpAddressV4 group { "127.255.255.255" }; //!< UDP multicast group base (Default: 127.255.255.255) + unsigned int ttl { 1U }; /*!< UDP ttl value, also known as hop limit, is used in determining + the intermediate routers being traversed towards the destination (Default: 1) */ + }; + } + struct Configuration { - size_t num_executor_reader{}; //!< Tcp_pubsub reader amount of threads that shall execute workload (Default: 4) - size_t num_executor_writer{}; //!< Tcp_pubsub writer amount of threads that shall execute workload (Default: 4) - size_t max_reconnections{}; //!< Tcp_pubsub reconnection attemps the session will try to reconnect in (Default: 5) - }; + Types::UdpConfigVersion config_version { Types::UdpConfigVersion::V2 }; /*!< UDP configuration version (Since eCAL 5.12.) + v1: default behavior + v2: new behavior, comes with a bit more intuitive handling regarding masking of the groups (Default: v2) */ + unsigned int port { 14002 }; /*!< UDP multicast port number (Default: 14002) */ + Types::UDPMode mode { Types::UDPMode::LOCAL }; /*!< Valid modes: local, network (Default: local)*/ + Types::IpAddressV4 mask { "255.255.255.240" }; /*!< v1: Mask maximum number of dynamic multicast group (Default: 0.0.0.1-0.0.0.255) + v2: masks are now considered like routes masking (Default: 255.0.0.0-255.255.255.255)*/ + + // TODO PG: are these minimum limits correct? + Types::ConstrainedInteger<5242880, 1024> send_buffer { 5242880 }; //!< UDP send buffer in bytes (Default: 5242880) + Types::ConstrainedInteger<5242880, 1024> receive_buffer { 5242880 }; //!< UDP receive buffer in bytes (Default: 5242880) + bool join_all_interfaces { false }; /*!< Linux specific setting to enable joining multicast groups on all network interfacs + independent of their link state. Enabling this makes sure that eCAL processes + receive data if they are started before network devices are up and running. (Default: false)*/ + bool npcap_enabled { false }; //!< Enable to receive UDP traffic with the Npcap based receiver (Default: false) + + Network::Configuration network; + const Local::Configuration local; + + ECAL_API Configuration& operator=(const Configuration& other); + }; } - namespace SHM + namespace TCP { struct Configuration { - std::string host_group_name{}; /*!< Common host group name that enables interprocess mechanisms across - (virtual) host borders (e.g, Docker); by default equivalent to local host name (Default: "")*/ - Types::ConstrainedInteger<4096, 4096> memfile_minsize{}; //!< Default memory file size for new publisher (Default: 4096) - Types::ConstrainedInteger<50, 1, 100> memfile_reserve{}; //!< Dynamic file size reserve before recreating memory file if topic size changes in % (Default: 50) - unsigned int memfile_ack_timeout{}; //!< Publisher timeout for ack event from subscriber that memory file content is processed (Default: 0) - Types::ConstrainedInteger<0, 1> memfile_buffer_count{}; //!< Number of parallel used memory file buffers for 1:n publish/subscribe ipc connections (Default = 1) - bool drop_out_of_order_messages{}; //!< (Default: ) - bool memfile_zero_copy{}; //!< Allow matching subscriber to access memory file without copying its content in advance (Default: false) + size_t number_executor_reader { 4 }; //!< Reader amount of threads that shall execute workload (Default: 4) + size_t number_executor_writer { 4 }; //!< Writer amount of threads that shall execute workload (Default: 4) + size_t max_reconnections { 5 }; //!< Reconnection attemps the session will try to reconnect in (Default: 5) }; } - namespace UDPMC + namespace SHM { struct Configuration { - Types::UdpConfigVersion config_version{}; /*!< UDP configuration version (Since eCAL 5.12.) - v1: default behavior - v2: new behavior, comes with a bit more intuitive handling regarding masking of the groups (Default: v1) */ - Types::IpAddressV4 group{}; //!< UDP multicast group base (Default: 239.0.0.1) - Types::IpAddressV4 mask{}; /*!< v1: Mask maximum number of dynamic multicast group (Default: 0.0.0.1-0.0.0.255) - v2: masks are now considered like routes masking (Default: 255.0.0.0-255.255.255.255)*/ - Types::ConstrainedInteger<14000, 10> port{}; /*!< UDP multicast port number (eCAL will use at least the 2 following port - numbers too, so modify in steps of 10 (e.g. 1010, 1020 ...)(Default: 14000) */ - unsigned int ttl{}; /*!< UDP ttl value, also known as hop limit, is used in determining - the intermediate routers being traversed towards the destination(Default: 2) */ - // TODO PG: are these minimum limits correct? - Types::ConstrainedInteger<5242880, 1024> sndbuf{}; //!< UDP send buffer in bytes (Default: 5242880) - Types::ConstrainedInteger<5242880, 1024> recbuf{}; //!< UDP receive buffer in bytes (Default: 5242880) - bool join_all_interfaces{}; /*!< Linux specific setting to enable joining multicast groups on all network interfacs - independent of their link state. Enabling this makes sure that eCAL processes - receive data if they are started before network devices are up and running. (Default: false)*/ - - bool npcap_enabled{}; //!< Enable to receive UDP traffic with the Npcap based receiver (Default: false) - }; + Types::ConstrainedInteger<4096, 4096> memfile_min_size_bytes { 4096 }; //!< Default memory file size for new publisher (Default: 4096) + Types::ConstrainedInteger<50, 1, 100> memfile_reserve_percent { 50 }; //!< Dynamic file size reserve before recreating memory file if topic size changes (Default: 50) + }; } - + struct Configuration { - bool network_enabled{}; /*!< true = all eCAL components communicate over network boundaries - false = local host only communication (Default: false) */ - bool drop_out_of_order_messages{}; //!< Enable dropping of payload messages that arrive out of order (Default: false) - UDPMC::Configuration mc_options{}; - TCPPubSub::Configuration tcp_options{}; - SHM::Configuration shm_options{}; + UDP::Configuration udp; + TCP::Configuration tcp; + SHM::Configuration shm; }; } } \ No newline at end of file diff --git a/ecal/core/include/ecal/config/user_arguments.h b/ecal/core/include/ecal/config/user_arguments.h index 6f64cdf16a..a5af144620 100644 --- a/ecal/core/include/ecal/config/user_arguments.h +++ b/ecal/core/include/ecal/config/user_arguments.h @@ -32,14 +32,10 @@ namespace eCAL { namespace Cli { - using ConfigKey2DMap = std::map>; //!< Config key storage: Map[Section][Option] = Value - struct Configuration { - std::vector config_keys{}; //!< will be deprecated soon - ConfigKey2DMap config_keys_map; //!< The config keys given via command line and the --config-keys parameter (Default: empty) - std::string specified_config{}; //!< The used eCAL ini file (Default: "") - bool dump_config{}; //!< If specified, output configuration via standart output (Default: false) + std::string user_yaml { "" }; //!< The used eCAL yaml file (Default: "") + bool dump_config { false }; //!< If specified, output configuration via standart output (Default: false) }; } } \ No newline at end of file diff --git a/ecal/core/include/ecal/ecal.h b/ecal/core/include/ecal/ecal.h index 0bfbc13886..8cd5bedaf9 100644 --- a/ecal/core/include/ecal/ecal.h +++ b/ecal/core/include/ecal/ecal.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/ecal/core/include/ecal/ecal_config.h b/ecal/core/include/ecal/ecal_config.h index 570d01e579..0ec57062c1 100644 --- a/ecal/core/include/ecal/ecal_config.h +++ b/ecal/core/include/ecal/ecal_config.h @@ -30,8 +30,16 @@ //@{ namespace eCAL { - ECAL_API Configuration& GetConfiguration(); - + ECAL_API Configuration& GetConfiguration (); + ECAL_API Registration::Configuration& GetRegistrationConfiguration (); + ECAL_API Monitoring::Configuration& GetMonitoringConfiguration (); + ECAL_API Logging::Configuration& GetLoggingConfiguration (); + ECAL_API Subscriber::Configuration& GetSubscriberConfiguration (); + ECAL_API Publisher::Configuration& GetPublisherConfiguration (); + ECAL_API Time::Configuration& GetTimesyncConfiguration (); + ECAL_API Service::Configuration& GetServiceConfiguration (); + ECAL_API Application::Configuration& GetApplicationConfiguration (); + namespace Config { ///////////////////////////////////// @@ -47,6 +55,7 @@ namespace eCAL ///////////////////////////////////// ECAL_API bool IsNetworkEnabled (); + ECAL_API bool IsShmRegistrationEnabled (); ECAL_API Types::UdpConfigVersion GetUdpMulticastConfigVersion (); ECAL_API std::string GetUdpMulticastGroup (); ECAL_API std::string GetUdpMulticastMask (); @@ -64,9 +73,9 @@ namespace eCAL ECAL_API bool IsNpcapEnabled (); - ECAL_API int GetTcpPubsubReaderThreadpoolSize (); - ECAL_API int GetTcpPubsubWriterThreadpoolSize (); - ECAL_API int GetTcpPubsubMaxReconnectionAttemps (); + ECAL_API size_t GetTcpPubsubReaderThreadpoolSize (); + ECAL_API size_t GetTcpPubsubWriterThreadpoolSize (); + ECAL_API size_t GetTcpPubsubMaxReconnectionAttemps (); ECAL_API std::string GetHostGroupName (); @@ -87,7 +96,6 @@ namespace eCAL // monitoring ///////////////////////////////////// - ECAL_API int GetMonitoringTimeoutMs (); ECAL_API std::string GetMonitoringFilterExcludeList (); ECAL_API std::string GetMonitoringFilterIncludeList (); ECAL_API eCAL_Logging_Filter GetConsoleLogFilter (); @@ -103,16 +111,6 @@ namespace eCAL ///////////////////////////////////// // publisher ///////////////////////////////////// - ECAL_API bool GetPublisherShmMode (); - ECAL_API bool GetPublisherTcpMode (); - ECAL_API bool GetPublisherUdpMulticastMode (); - - ECAL_API size_t GetMemfileMinsizeBytes (); - ECAL_API size_t GetMemfileOverprovisioningPercentage (); - ECAL_API int GetMemfileAckTimeoutMs (); - ECAL_API bool IsMemfileZerocopyEnabled (); - ECAL_API size_t GetMemfileBufferCount (); - ECAL_API bool IsTopicTypeSharingEnabled (); ECAL_API bool IsTopicDescriptionSharingEnabled (); @@ -127,8 +125,6 @@ namespace eCAL ///////////////////////////////////// namespace Experimental { - ECAL_API bool IsShmMonitoringEnabled (); - ECAL_API bool IsNetworkMonitoringDisabled (); ECAL_API size_t GetShmMonitoringQueueSize (); ECAL_API std::string GetShmMonitoringDomain (); ECAL_API bool GetDropOutOfOrderMessages (); diff --git a/ecal/core/include/ecal/ecal_publisher.h b/ecal/core/include/ecal/ecal_publisher.h index c79f134b7a..7009246e8b 100644 --- a/ecal/core/include/ecal/ecal_publisher.h +++ b/ecal/core/include/ecal/ecal_publisher.h @@ -24,16 +24,16 @@ #pragma once -#include #include #include #include #include -#include #include #include +#include #include +#include #include #include @@ -85,7 +85,7 @@ namespace eCAL * @param data_type_info_ Topic data type information (encoding, type, descriptor). * @param config_ Optional configuration parameters. **/ - ECAL_API CPublisher(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = {}); + ECAL_API CPublisher(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = GetPublisherConfiguration()); /** * @brief Constructor. @@ -93,7 +93,7 @@ namespace eCAL * @param topic_name_ Unique topic name. * @param config_ Optional configuration parameters. **/ - ECAL_API explicit CPublisher(const std::string& topic_name_, const Publisher::Configuration& config_ = {}); + ECAL_API explicit CPublisher(const std::string& topic_name_, const Publisher::Configuration& config_ = GetPublisherConfiguration()); /** * @brief Destructor. @@ -129,7 +129,7 @@ namespace eCAL * * @return True if it succeeds, false if it fails. **/ - ECAL_API bool Create(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = {}); + ECAL_API bool Create(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = GetPublisherConfiguration()); /** * @brief Creates this object. diff --git a/ecal/core/include/ecal/ecal_registration.h b/ecal/core/include/ecal/ecal_registration.h new file mode 100644 index 0000000000..5afd1ffdec --- /dev/null +++ b/ecal/core/include/ecal/ecal_registration.h @@ -0,0 +1,344 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @file ecal_registration.h + * @brief eCAL registration interface +**/ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace eCAL +{ + namespace Registration + { + // enumeration of quality bits used for detecting how good a data information is + enum class DescQualityFlags : std::uint8_t + { + NO_QUALITY = 0, //!< Special value for initialization + DESCRIPTION_AVAILABLE = 0x1 << 3, //!< Having a type descriptor available + ENCODING_AVAILABLE = 0x1 << 2, //!< Having a type encoding + TYPENAME_AVAILABLE = 0x1 << 1, //!< Having a type name available + INFO_COMES_FROM_PRODUCER = 0x1 << 0 //!< Info is coming from the producer (like a publisher, service) + }; + + constexpr inline DescQualityFlags operator~ (DescQualityFlags a) { return static_cast( ~static_cast::type>(a) ); } + constexpr inline DescQualityFlags operator| (DescQualityFlags a, DescQualityFlags b) { return static_cast( static_cast::type>(a) | static_cast::type>(b) ); } + constexpr inline DescQualityFlags operator& (DescQualityFlags a, DescQualityFlags b) { return static_cast( static_cast::type>(a) & static_cast::type>(b) ); } + constexpr inline DescQualityFlags operator^ (DescQualityFlags a, DescQualityFlags b) { return static_cast( static_cast::type>(a) ^ static_cast::type>(b) ); } + inline DescQualityFlags& operator|= (DescQualityFlags& a, DescQualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) |= static_cast::type>(b) ); } + inline DescQualityFlags& operator&= (DescQualityFlags& a, DescQualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) &= static_cast::type>(b) ); } + inline DescQualityFlags& operator^= (DescQualityFlags& a, DescQualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) ^= static_cast::type>(b) ); } + + using TopicId = std::uint64_t; + struct SQualityTopicInfo + { + SDataTypeInformation info; + DescQualityFlags quality = DescQualityFlags::NO_QUALITY; + + bool operator<(const SQualityTopicInfo& other) const + { + return std::tie(quality, info) < std::tie(other.quality, info); + } + }; + using QualityTopicInfoMultiMap = std::multimap; + using QualityTopicInfoSet = std::set; + + using ServiceId = std::uint64_t; + struct SQualityServiceInfo + { + SServiceMethodInformation info; + DescQualityFlags request_quality = DescQualityFlags::NO_QUALITY; + DescQualityFlags response_quality = DescQualityFlags::NO_QUALITY; + + bool operator<(const SQualityServiceInfo& other) const + { + return std::tie(request_quality, response_quality) < std::tie(other.request_quality, other.response_quality); + } + }; + struct SServiceMethod + { + std::string service_name; + std::string method_name; + + bool operator<(const SServiceMethod& other) const + { + return std::tie(service_name, method_name) < std::tie(other.service_name, other.method_name); + } + }; + using QualityServiceInfoMultimap = std::multimap; + using SQualityServiceInfoSet = std::set; + + /** + * @brief Get complete snapshot of all known publisher. + * + * @return Set of topic id's. + **/ + ECAL_API std::set GetPublisherIDs(); + + /** + * @brief Get data type information with quality for specific publisher. + * + * @return True if information could be queried. + **/ + ECAL_API bool GetPublisherInfo(const STopicId& id_, SQualityTopicInfo& topic_info_); + + /** + * @brief Get complete snapshot of all known subscriber. + * + * @return Set of topic id's. + **/ + ECAL_API std::set GetSubscriberIDs(); + + /** + * @brief Get data type information with quality for specific subscriber. + * + * @return True if information could be queried. + **/ + ECAL_API bool GetSubscriberInfo(const STopicId& id_, SQualityTopicInfo& topic_info_); + + /** + * @brief Get complete snapshot of all known services. + * + * @return Set of service id's. + **/ + ECAL_API std::set GetServiceIDs(); + + /** + * @brief Get service method information with quality for specific service. + * + * @return True if information could be queried. + **/ + ECAL_API bool GetServiceInfo(const SServiceId& id_, SQualityServiceInfo& service_info_); + + /** + * @brief Get complete snapshot of all known clients. + * + * @return Set of service id's. + **/ + ECAL_API std::set GetClientIDs(); + + /** + * @brief Get service method information with quality for specific client. + * + * @return True if information could be queried. + **/ + ECAL_API bool GetClientInfo(const SServiceId& id_, SQualityServiceInfo& service_info_); + + /** + * @brief Get complete snapshot of data type information with quality and topic id for all known publisher. + * + * @return MultiMap containing the quality datatype information and the topic id's. + **/ + ECAL_API QualityTopicInfoMultiMap GetPublishers(); + + /** + * @brief Get data type information with quality and topic id for this publishers. + * + * @param topic_name_ Topic name. + * + * @return Set containing the quality datatype information for this publisher. + **/ + ECAL_API QualityTopicInfoSet GetPublishers(const std::string& topic_name_); + + /** + * @brief Get complete snapshot of data type information with quality and topic id for all known subscribers. + * + * @return MultiMap containing the quality datatype information and the topic id's. + **/ + ECAL_API QualityTopicInfoMultiMap GetSubscribers(); + + /** + * @brief Get data type information with quality and topic id for this subscriber. + * + * @param topic_name_ Topic name. + * + * @return Set containing the quality datatype information for this subscriber. + **/ + ECAL_API QualityTopicInfoSet GetSubscribers(const std::string& topic_name_); + + /** + * @brief Get highest quality data type information out of a set of quality data type information. + * + * @param quality_topic_info_set_ Set of quality data type information + * + * @return Highest quality data type information. + **/ + ECAL_API SDataTypeInformation GetHighestQualityDataTypeInformation(const QualityTopicInfoSet& quality_topic_info_set_); + + /** + * @brief Get complete snapshot of service method information with quality and service id for all known services. + * + * @return MultiMap containing the quality datatype information and the service id's. + **/ + ECAL_API QualityServiceInfoMultimap GetServices(); + + /** + * @brief Get complete snapshot of service method information with quality and client id for all known clients. + * + * @return MultiMap containing the quality datatype information and the client id's. + **/ + ECAL_API QualityServiceInfoMultimap GetClients(); + + /** + * @brief Get highest quality service method type information out of a set of quality service method information. + * + * @param quality_service_info_set_ Set of quality service method information + * + * @return Highest quality service method information. + **/ + ECAL_API SServiceMethodInformation GetHighestQualityServiceMethodInformation(const SQualityServiceInfoSet& quality_service_info_set_); + + /** + * @brief Get complete topic map. + * + * @param data_type_info_map_ Map to store the datatype information. + * Map { TopicName -> SDataTypeInformation } mapping of all currently known publisher/subscriber. + **/ + ECAL_API void GetTopics(std::map& data_type_info_map_); + + /** + * @brief Get complete quality topic map. + * + * @param quality_topic_info_map_ Map to store the quality datatype information. + * Map { TopicName -> SQualityDataTypeInformation } mapping of all currently known publisher/subscriber. + **/ + ECAL_API void GetTopics(std::map& quality_topic_info_map_); + + /** + * @brief Get all topic names. + * + * @param topic_names_ Set to store the topic names. + **/ + ECAL_API void GetTopicNames(std::set& topic_names_); + + /** + * @brief Gets description of the specified topic. + * + * @param topic_name_ Topic name. + * @param data_type_info_ SDataTypeInformation to be filled by this function. + * + * @return True if TopicInformation for specified topic could be retrieved, false otherwise. + **/ + ECAL_API bool GetTopicDataTypeInformation(const std::string& topic_name_, SDataTypeInformation& data_type_info_); + + /** + * @brief Get complete service map. + * + * @param service_method_info_map_ Map to store the service/method descriptions. + * Map { (ServiceName, MethodName) -> SServiceMethodInformation } mapping of all currently known services. + **/ + ECAL_API void GetServices(std::map& service_method_info_map_); + + /** + * @brief Get complete quality service map. + * + * @param quality_service_info_map_ Map to store the quality service/method descriptions. + * Map { (ServiceName, MethodName) -> SQualityServiceMethodInformation } mapping of all currently known services. + **/ + ECAL_API void GetServices(std::map& quality_service_info_map_); + + /** + * @brief Get all service/method names. + * + * @param service_method_names_ Set to store the service/method names (Set { (ServiceName, MethodName) }). + **/ + ECAL_API void GetServiceMethodNames(std::set& service_method_names_); + + /** + * @brief Gets service method request and response type names. + * + * @param service_name_ Service name. + * @param method_name_ Method name. + * @param req_type_ String to store request type. + * @param resp_type_ String to store response type. + * + * @return True if succeeded. + **/ + ECAL_API bool GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_); + + /** + * @brief Gets service method request and response descriptions. + * + * @param service_name_ Service name. + * @param method_name_ Method name. + * @param req_desc_ String to store request description. + * @param resp_desc_ String to store response description. + * + * @return True if succeeded. + **/ + ECAL_API bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_); + + /** + * @brief Get complete client map. + * + * @param client_method_info_map_ Map to store the client/method descriptions. + * Map { (ClientName, MethodName) -> SServiceMethodInformation } mapping of all currently known clients. + **/ + ECAL_API void GetClients(std::map& client_method_info_map_); + + /** + * @brief Get complete quality client map. + * + * @param quality_client_info_map_ Map to store the quality client/method descriptions. + * Map { (ClientName, MethodName) -> SQualityServiceMethodInformation } mapping of all currently known clients. + **/ + ECAL_API void GetClients(std::map& quality_client_info_map_); + + /** + * @brief Get all client/method names. + * + * @param client_method_names_ Set to store the client/method names (Set { (ClientName, MethodName) }). + **/ + ECAL_API void GetClientMethodNames(std::set& client_method_names_); + + /** + * @brief Gets client method request and response type names. + * + * @param client_name_ Client name. + * @param method_name_ Method name. + * @param req_type_ String to store request type. + * @param resp_type_ String to store response type. + * + * @return True if succeeded. + **/ + ECAL_API bool GetClientTypeNames(const std::string& client_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_); + + /** + * @brief Gets client method request and response descriptions. + * + * @param client_name_ Client name. + * @param method_name_ Method name. + * @param req_desc_ String to store request description. + * @param resp_desc_ String to store response description. + * + * @return True if succeeded. + **/ + ECAL_API bool GetClientDescription(const std::string& client_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_); + } +} diff --git a/ecal/core/include/ecal/ecal_subscriber.h b/ecal/core/include/ecal/ecal_subscriber.h index 15f9cc3d43..bcbbc50eb6 100644 --- a/ecal/core/include/ecal/ecal_subscriber.h +++ b/ecal/core/include/ecal/ecal_subscriber.h @@ -97,15 +97,15 @@ namespace eCAL * @param data_type_info_ Topic data type information (encoding, type, descriptor). * @param config_ Optional configuration parameters. **/ - ECAL_API CSubscriber(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Subscriber::Configuration& config_ = {}); + ECAL_API CSubscriber(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Subscriber::Configuration& config_ = GetSubscriberConfiguration()); /** * @brief Constructor. * * @param topic_name_ Unique topic name. - * @param data_type_info_ Topic data type information (encoding, type, descriptor). + * @param config_ Optional configuration parameters. **/ - ECAL_API explicit CSubscriber(const std::string& topic_name_, const Subscriber::Configuration& config_ = {}); + ECAL_API explicit CSubscriber(const std::string& topic_name_, const Subscriber::Configuration& config_ = GetSubscriberConfiguration()); /** * @brief Destructor. @@ -141,7 +141,7 @@ namespace eCAL * * @return True if it succeeds, false if it fails. **/ - ECAL_API bool Create(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Subscriber::Configuration& config_ = {}); + ECAL_API bool Create(const std::string& topic_name_, const SDataTypeInformation& data_type_info_, const Subscriber::Configuration& config_ = GetSubscriberConfiguration()); /** * @brief Creates this object. @@ -241,6 +241,13 @@ namespace eCAL **/ ECAL_API bool IsCreated() const {return(m_created);} + /** + * @brief Query if the subscriber is published. + * + * @return true if published, false if not. + **/ + ECAL_API bool IsPublished() const; + /** * @brief Query the number of publishers. * diff --git a/ecal/core/include/ecal/ecal_types.h b/ecal/core/include/ecal/ecal_types.h index 5e32297c9e..e2e14b16e6 100644 --- a/ecal/core/include/ecal/ecal_types.h +++ b/ecal/core/include/ecal/ecal_types.h @@ -82,4 +82,49 @@ namespace eCAL } //!< @endcond }; + + namespace Registration + { + struct SEntityId + { + std::string entity_id; // unique id within that process + int32_t process_id = 0; // process id which produced the sample + std::string host_name; // host which produced the sample + + bool operator==(const SEntityId& other) const { + return entity_id == other.entity_id && + process_id == other.process_id && + host_name == other.host_name; + } + + bool operator<(const SEntityId& other) const + { + return std::tie(process_id, entity_id, host_name) + < std::tie(other.process_id, other.entity_id, other.host_name); + } + }; + + struct STopicId + { + SEntityId topic_id; + std::string topic_name; + + bool operator<(const STopicId& other) const + { + return std::tie(topic_id, topic_name) < std::tie(other.topic_id, other.topic_name); + } + }; + + struct SServiceId + { + SEntityId service_id; + std::string service_name; + std::string method_name; + + bool operator<(const SServiceId& other) const + { + return std::tie(service_id, service_name, method_name) < std::tie(other.service_id, other.service_name, other.method_name); + } + }; + } } diff --git a/ecal/core/include/ecal/ecal_util.h b/ecal/core/include/ecal/ecal_util.h index 0a666ee344..ba4fa07c09 100644 --- a/ecal/core/include/ecal/ecal_util.h +++ b/ecal/core/include/ecal/ecal_util.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,82 +25,18 @@ #pragma once #include -#include -#include -#include -#include -#include #include -#include #include namespace eCAL { - namespace Util - { - // enumeration of quality bits used for detecting how good a data information is - enum class DescQualityFlags : std::uint8_t - { - NO_QUALITY = 0, //!< Special value for initialization - DESCRIPTION_AVAILABLE = 0x1 << 3, //!< Having a type descriptor available - ENCODING_AVAILABLE = 0x1 << 2, //!< Having a type encoding - TYPENAME_AVAILABLE = 0x1 << 1, //!< Having a type name available - INFO_COMES_FROM_PRODUCER = 0x1 << 0 //!< Info is coming from the producer (like a publisher, service) - }; - - constexpr inline DescQualityFlags operator~ (DescQualityFlags a) { return static_cast( ~static_cast::type>(a) ); } - constexpr inline DescQualityFlags operator| (DescQualityFlags a, DescQualityFlags b) { return static_cast( static_cast::type>(a) | static_cast::type>(b) ); } - constexpr inline DescQualityFlags operator& (DescQualityFlags a, DescQualityFlags b) { return static_cast( static_cast::type>(a) & static_cast::type>(b) ); } - constexpr inline DescQualityFlags operator^ (DescQualityFlags a, DescQualityFlags b) { return static_cast( static_cast::type>(a) ^ static_cast::type>(b) ); } - inline DescQualityFlags& operator|= (DescQualityFlags& a, DescQualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) |= static_cast::type>(b) ); } - inline DescQualityFlags& operator&= (DescQualityFlags& a, DescQualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) &= static_cast::type>(b) ); } - inline DescQualityFlags& operator^= (DescQualityFlags& a, DescQualityFlags b) { return reinterpret_cast( reinterpret_cast::type&>(a) ^= static_cast::type>(b) ); } - - using TopicId = std::uint64_t; - struct SQualityTopicInfo - { - TopicId id = 0; - SDataTypeInformation info; - DescQualityFlags quality = DescQualityFlags::NO_QUALITY; - - bool operator<(const SQualityTopicInfo& other) const - { - return std::tie(quality, id) < std::tie(other.quality, other.id); - } - }; - using QualityTopicInfoMultiMap = std::multimap; - using QualityTopicInfoSet = std::set; - - using ServiceId = std::uint64_t; - struct SQualityServiceInfo - { - ServiceId id = 0; - SServiceMethodInformation info; - DescQualityFlags request_quality = DescQualityFlags::NO_QUALITY; - DescQualityFlags response_quality = DescQualityFlags::NO_QUALITY; - - bool operator<(const SQualityServiceInfo& other) const - { - return std::tie(request_quality, response_quality, id) < std::tie(other.request_quality, other.response_quality, other.id); - } - }; - struct SServiceMethod - { - std::string service_name; - std::string method_name; - - bool operator<(const SServiceMethod& other) const - { - return std::tie(service_name, method_name) < std::tie(other.service_name, other.method_name); - } - }; - using QualityServiceInfoMultimap = std::multimap; - using SQualityServiceInfoSet = std::set; + namespace Util + { /** * @brief Retrieve eCAL configuration path. * This is path is for the global eCAL configuration files - * like ecal.ini. + * like ecal.yaml. * This path is read only for standard users. * * @return eCAL configuration path. @@ -181,197 +117,6 @@ namespace eCAL **/ ECAL_API void PubShareDescription(bool state_); - /** - * @brief Get complete snapshot of data type information with quality and topic id for all known publisher. - * - * @return MultiMap containing the quality datatype information and the topic id's. - **/ - ECAL_API QualityTopicInfoMultiMap GetPublishers(); - - /** - * @brief Get data type information with quality and topic id for this publishers. - * - * @param topic_name_ Topic name. - * - * @return Set containing the quality datatype information for this publisher. - **/ - ECAL_API QualityTopicInfoSet GetPublishers(const std::string& topic_name_); - - /** - * @brief Get complete snapshot of data type information with quality and topic id for all known subscribers. - * - * @return MultiMap containing the quality datatype information and the topic id's. - **/ - ECAL_API QualityTopicInfoMultiMap GetSubscribers(); - - /** - * @brief Get data type information with quality and topic id for this subscriber. - * - * @param topic_name_ Topic name. - * - * @return Set containing the quality datatype information for this subscriber. - **/ - ECAL_API QualityTopicInfoSet GetSubscribers(const std::string& topic_name_); - - /** - * @brief Get highest quality data type information out of a set of quality data type information. - * - * @param quality_topic_info_set_ Set of quality data type information - * - * @return Highest quality data type information. - **/ - ECAL_API SDataTypeInformation GetHighestQualityDataTypeInformation(const QualityTopicInfoSet& quality_topic_info_set_); - - /** - * @brief Get complete snapshot of service method information with quality and service id for all known services. - * - * @return MultiMap containing the quality datatype information and the service id's. - **/ - ECAL_API QualityServiceInfoMultimap GetServices(); - - /** - * @brief Get complete snapshot of service method information with quality and client id for all known clients. - * - * @return MultiMap containing the quality datatype information and the client id's. - **/ - ECAL_API QualityServiceInfoMultimap GetClients(); - - /** - * @brief Get highest quality service method type information out of a set of quality service method information. - * - * @param quality_service_info_set_ Set of quality service method information - * - * @return Highest quality service method information. - **/ - ECAL_API SServiceMethodInformation GetHighestQualityServiceMethodInformation(const SQualityServiceInfoSet& quality_service_info_set_); - - /** - * @brief Get complete topic map. - * - * @param data_type_info_map_ Map to store the datatype information. - * Map { TopicName -> SDataTypeInformation } mapping of all currently known publisher/subscriber. - **/ - ECAL_API void GetTopics(std::map& data_type_info_map_); - - /** - * @brief Get complete quality topic map. - * - * @param quality_topic_info_map_ Map to store the quality datatype information. - * Map { TopicName -> SQualityDataTypeInformation } mapping of all currently known publisher/subscriber. - **/ - ECAL_API void GetTopics(std::map& quality_topic_info_map_); - - /** - * @brief Get all topic names. - * - * @param topic_names_ Set to store the topic names. - **/ - ECAL_API void GetTopicNames(std::set& topic_names_); - - /** - * @brief Gets description of the specified topic. - * - * @param topic_name_ Topic name. - * @param data_type_info_ SDataTypeInformation to be filled by this function. - * - * @return True if TopicInformation for specified topic could be retrieved, false otherwise. - **/ - ECAL_API bool GetTopicDataTypeInformation(const std::string& topic_name_, SDataTypeInformation& data_type_info_); - - /** - * @brief Get complete service map. - * - * @param service_method_info_map_ Map to store the service/method descriptions. - * Map { (ServiceName, MethodName) -> SServiceMethodInformation } mapping of all currently known services. - **/ - ECAL_API void GetServices(std::map& service_method_info_map_); - - /** - * @brief Get complete quality service map. - * - * @param quality_service_info_map_ Map to store the quality service/method descriptions. - * Map { (ServiceName, MethodName) -> SQualityServiceMethodInformation } mapping of all currently known services. - **/ - ECAL_API void GetServices(std::map& quality_service_info_map_); - - /** - * @brief Get all service/method names. - * - * @param service_method_names_ Set to store the service/method names (Set { (ServiceName, MethodName) }). - **/ - ECAL_API void GetServiceMethodNames(std::set& service_method_names_); - - /** - * @brief Gets service method request and response type names. - * - * @param service_name_ Service name. - * @param method_name_ Method name. - * @param req_type_ String to store request type. - * @param resp_type_ String to store response type. - * - * @return True if succeeded. - **/ - ECAL_API bool GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_); - - /** - * @brief Gets service method request and response descriptions. - * - * @param service_name_ Service name. - * @param method_name_ Method name. - * @param req_desc_ String to store request description. - * @param resp_desc_ String to store response description. - * - * @return True if succeeded. - **/ - ECAL_API bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_); - - /** - * @brief Get complete client map. - * - * @param client_method_info_map_ Map to store the client/method descriptions. - * Map { (ClientName, MethodName) -> SServiceMethodInformation } mapping of all currently known clients. - **/ - ECAL_API void GetClients(std::map& client_method_info_map_); - - /** - * @brief Get complete quality client map. - * - * @param quality_client_info_map_ Map to store the quality client/method descriptions. - * Map { (ClientName, MethodName) -> SQualityServiceMethodInformation } mapping of all currently known clients. - **/ - ECAL_API void GetClients(std::map& quality_client_info_map_); - - /** - * @brief Get all client/method names. - * - * @param client_method_names_ Set to store the client/method names (Set { (ClientName, MethodName) }). - **/ - ECAL_API void GetClientMethodNames(std::set& client_method_names_); - - /** - * @brief Gets client method request and response type names. - * - * @param client_name_ Client name. - * @param method_name_ Method name. - * @param req_type_ String to store request type. - * @param resp_type_ String to store response type. - * - * @return True if succeeded. - **/ - ECAL_API bool GetClientTypeNames(const std::string& client_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_); - - /** - * @brief Gets client method request and response descriptions. - * - * @param client_name_ Client name. - * @param method_name_ Method name. - * @param req_desc_ String to store request description. - * @param resp_desc_ String to store response description. - * - * @return True if succeeded. - **/ - ECAL_API bool GetClientDescription(const std::string& client_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_); - /** * @brief Splits the topic type (eCAL < 5.12) into encoding and types (>= eCAL 5.12) * diff --git a/ecal/core/include/ecal/ecalc.h b/ecal/core/include/ecal/ecalc.h index fb74dd87f0..b9ff42d094 100644 --- a/ecal/core/include/ecal/ecalc.h +++ b/ecal/core/include/ecal/ecalc.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/ecal/core/include/ecal/msg/dynamic.h b/ecal/core/include/ecal/msg/dynamic.h index 78603f594f..1d09b6df4c 100644 --- a/ecal/core/include/ecal/msg/dynamic.h +++ b/ecal/core/include/ecal/msg/dynamic.h @@ -293,7 +293,7 @@ namespace eCAL if (!m_datatype_info_received) { SDataTypeInformation datatype_info_received; - auto received_info = eCAL::Util::GetTopicDataTypeInformation(m_topic_name, datatype_info_received); + auto received_info = eCAL::Registration::GetTopicDataTypeInformation(m_topic_name, datatype_info_received); // empty datatype informations are not valid to do reflection on! if (received_info && datatype_info_received != SDataTypeInformation{}) { diff --git a/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h b/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h index 717ab81fe0..2ff702f89a 100644 --- a/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h +++ b/ecal/core/include/ecal/msg/protobuf/dynamic_publisher.h @@ -62,7 +62,7 @@ namespace eCAL * @param msg_ Protobuf message object. * @param config_ Optional configuration parameters. **/ - CDynamicPublisher(const std::string& topic_name_, const std::shared_ptr& msg_, const eCAL::Publisher::Configuration& config_ = {}) + CDynamicPublisher(const std::string& topic_name_, const std::shared_ptr& msg_, const eCAL::Publisher::Configuration& config_ = eCAL::GetPublisherConfiguration()) : CMsgPublisher(topic_name_, GetTopicInformationFromMessage(msg_.get()), config_) , m_msg{ msg_ } {} diff --git a/ecal/core/include/ecal/msg/protobuf/publisher.h b/ecal/core/include/ecal/msg/protobuf/publisher.h index b6b79bd869..daab06f0bd 100644 --- a/ecal/core/include/ecal/msg/protobuf/publisher.h +++ b/ecal/core/include/ecal/msg/protobuf/publisher.h @@ -107,7 +107,7 @@ namespace eCAL // where the vtable is not created yet, or it's destructed. // Probably we can handle the Message publishers differently. One message publisher class and then one class for payloads and getting type // descriptor information. - explicit CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) : eCAL::CPublisher(topic_name_, CPublisher::GetDataTypeInformation(), config_) + explicit CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = GetPublisherConfiguration()) : eCAL::CPublisher(topic_name_, CPublisher::GetDataTypeInformation(), config_) { } @@ -144,7 +144,7 @@ namespace eCAL * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) + bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = GetPublisherConfiguration()) { return(eCAL::CPublisher::Create(topic_name_, GetDataTypeInformation(), config_)); } @@ -160,8 +160,7 @@ namespace eCAL size_t Send(const T& msg_, long long time_ = DEFAULT_TIME_ARGUMENT) { CPayload payload{ msg_ }; - eCAL::CPublisher::Send(payload, time_); - return(0); + return eCAL::CPublisher::Send(payload, time_); } private: diff --git a/ecal/core/include/ecal/msg/publisher.h b/ecal/core/include/ecal/msg/publisher.h index 74e993c6c9..5497c9bf95 100644 --- a/ecal/core/include/ecal/msg/publisher.h +++ b/ecal/core/include/ecal/msg/publisher.h @@ -63,7 +63,7 @@ namespace eCAL * @param data_type_info_ Topic data type information (encoding, type, descriptor). * @param config_ Optional configuration parameters. **/ - CMsgPublisher(const std::string& topic_name_, const struct SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = {}) : CPublisher(topic_name_, data_type_info_, config_) + CMsgPublisher(const std::string& topic_name_, const struct SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = GetPublisherConfiguration()) : CPublisher(topic_name_, data_type_info_, config_) { } @@ -74,7 +74,7 @@ namespace eCAL * @param topic_name_ Unique topic name. * @param config_ Optional configuration parameters. **/ - explicit CMsgPublisher(const std::string& topic_name_, const Publisher::Configuration& config_ = {}) : CMsgPublisher(topic_name_, GetDataTypeInformation(), config_) + explicit CMsgPublisher(const std::string& topic_name_, const Publisher::Configuration& config_ = GetPublisherConfiguration()) : CMsgPublisher(topic_name_, GetDataTypeInformation(), config_) { } @@ -109,7 +109,7 @@ namespace eCAL * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_, const struct SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = {}) + bool Create(const std::string& topic_name_, const struct SDataTypeInformation& data_type_info_, const Publisher::Configuration& config_ = GetPublisherConfiguration()) { return(CPublisher::Create(topic_name_, data_type_info_, config_)); } diff --git a/ecal/core/include/ecal/msg/string/publisher.h b/ecal/core/include/ecal/msg/string/publisher.h index 03cbf886c3..6d28061842 100644 --- a/ecal/core/include/ecal/msg/string/publisher.h +++ b/ecal/core/include/ecal/msg/string/publisher.h @@ -60,7 +60,7 @@ namespace eCAL // call the function via its class because it's a virtual function that is called in constructor/destructor,- // where the vtable is not created yet, or it's destructed. - explicit CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) : CMsgPublisher(topic_name_, GetDataTypeInformation(), config_) + explicit CPublisher(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = GetPublisherConfiguration()) : CMsgPublisher(topic_name_, GetDataTypeInformation(), config_) { } @@ -92,7 +92,7 @@ namespace eCAL * * @return True if it succeeds, false if it fails. **/ - bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = {}) + bool Create(const std::string& topic_name_, const eCAL::Publisher::Configuration& config_ = GetPublisherConfiguration()) { return(CMsgPublisher::Create(topic_name_, GetDataTypeInformation(), config_)); } diff --git a/ecal/core/include/ecal/msg/subscriber.h b/ecal/core/include/ecal/msg/subscriber.h index b59d926daa..a0e09bad43 100644 --- a/ecal/core/include/ecal/msg/subscriber.h +++ b/ecal/core/include/ecal/msg/subscriber.h @@ -254,12 +254,13 @@ namespace eCAL * @brief Constructor. * * @param topic_name_ Unique topic name. + * @param config_ Optional configuration parameters. **/ - CMessageSubscriber(const std::string& topic_name_) : CSubscriber() + CMessageSubscriber(const std::string& topic_name_, const Subscriber::Configuration& config_ = GetSubscriberConfiguration()) : CSubscriber() , m_deserializer() { SDataTypeInformation topic_info = m_deserializer.GetDataTypeInformation(); - CSubscriber::Create(topic_name_, topic_info); + CSubscriber::Create(topic_name_, topic_info, config_); } ~CMessageSubscriber() noexcept @@ -418,7 +419,4 @@ namespace eCAL MsgReceiveCallbackT m_cb_callback; Deserializer m_deserializer; }; - - - } diff --git a/ecal/core/include/ecal/types/ecal_custom_data_types.h b/ecal/core/include/ecal/types/ecal_custom_data_types.h index 8162c099ac..00ab7b7cbd 100644 --- a/ecal/core/include/ecal/types/ecal_custom_data_types.h +++ b/ecal/core/include/ecal/types/ecal_custom_data_types.h @@ -46,13 +46,18 @@ namespace eCAL class IpAddressV4 { public: - ECAL_API IpAddressV4(); ECAL_API IpAddressV4(const std::string& ip_address_); - std::string Get() const; + ECAL_API std::string Get() const; - ECAL_API IpAddressV4& operator=(const std::string& ip_string); - ECAL_API operator std::string(); + ECAL_API IpAddressV4& operator=(const std::string& ip_string_); + ECAL_API IpAddressV4& operator=(const char* ip_string_); + ECAL_API operator std::string() const; + ECAL_API bool operator==(const eCAL::Types::IpAddressV4& rhs) const; + ECAL_API friend bool operator==(eCAL::Types::IpAddressV4 lhs, const char* ip_string_); + ECAL_API friend bool operator==(const char* ip_string_, eCAL::Types::IpAddressV4 rhs); + ECAL_API friend bool operator==(eCAL::Types::IpAddressV4 lhs, const std::string& ip_string_); + ECAL_API friend bool operator==(const std::string& ip_string_, eCAL::Types::IpAddressV4 rhs); private: ECAL_API void validateIpString(const std::string& ip_address_); @@ -89,7 +94,10 @@ namespace eCAL }; operator int() const { return m_size; }; - bool operator==(const ConstrainedInteger& other) const { return this->m_size == other; }; + bool operator==(const ConstrainedInteger& other) const { return this->m_size == other; }; + bool operator==(const unsigned int value) const { return this->m_size == static_cast(value); }; + bool operator==(const int value) const { return this->m_size == value; }; + private: int m_size{}; @@ -100,5 +108,12 @@ namespace eCAL V1 = 1, V2 = 2 }; + + enum class UDPMode + { + NETWORK, + LOCAL + }; + } } \ No newline at end of file diff --git a/ecal/core/include/ecal/types/monitoring.h b/ecal/core/include/ecal/types/monitoring.h index f371719484..aff7793d15 100644 --- a/ecal/core/include/ecal/types/monitoring.h +++ b/ecal/core/include/ecal/types/monitoring.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,9 +64,9 @@ namespace eCAL struct TLayer { - eTLayerType type = tl_none; // + +namespace eCAL +{ + namespace Monitoring + { + SAttributes BuildMonitoringAttributes(const Monitoring::Configuration& config_); + } +} \ No newline at end of file diff --git a/ecal/core/src/builder/registration_attribute_builder.cpp b/ecal/core/src/builder/registration_attribute_builder.cpp new file mode 100644 index 0000000000..a77022a96a --- /dev/null +++ b/ecal/core/src/builder/registration_attribute_builder.cpp @@ -0,0 +1,65 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "registration_attribute_builder.h" +#include "ecal/ecal_process.h" + +namespace eCAL +{ + Registration::SAttributes BuildRegistrationAttributes(const eCAL::Registration::Configuration& reg_config_, const eCAL::TransportLayer::UDP::Configuration& tl_udp_confi_, int process_id_) + { + Registration::SAttributes attr; + + attr.timeout = std::chrono::milliseconds(reg_config_.registration_timeout); + attr.refresh = reg_config_.registration_refresh; + attr.network_enabled = reg_config_.network_enabled; + attr.loopback = reg_config_.loopback; + + // TODO: Check the usage further down of host_group_name -> logic currenty missleading + if (reg_config_.host_group_name.empty()) + { + attr.host_group_name = eCAL::Process::GetHostName(); + } + else + { + attr.host_group_name = reg_config_.host_group_name; + } + + attr.process_id = process_id_; + + attr.shm_enabled = reg_config_.layer.shm.enable; + attr.udp_enabled = reg_config_.layer.udp.enable; + + attr.shm.domain = reg_config_.layer.shm.domain; + attr.shm.queue_size = reg_config_.layer.shm.queue_size; + + attr.udp.port = reg_config_.layer.udp.port; + attr.udp.sendbuffer = tl_udp_confi_.send_buffer; + attr.udp.receivebuffer = tl_udp_confi_.receive_buffer; + attr.udp.mode = tl_udp_confi_.mode; + + attr.udp.network.group = tl_udp_confi_.network.group; + attr.udp.network.ttl = tl_udp_confi_.network.ttl; + + attr.udp.local.group = tl_udp_confi_.local.group; + attr.udp.local.ttl = tl_udp_confi_.local.ttl; + + return attr; + } +} \ No newline at end of file diff --git a/ecal/core/src/builder/registration_attribute_builder.h b/ecal/core/src/builder/registration_attribute_builder.h new file mode 100644 index 0000000000..a2d82853f7 --- /dev/null +++ b/ecal/core/src/builder/registration_attribute_builder.h @@ -0,0 +1,29 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include "registration/attributes/registration_attributes.h" + +#include + +namespace eCAL +{ + Registration::SAttributes BuildRegistrationAttributes(const eCAL::Registration::Configuration& reg_config_, const eCAL::TransportLayer::UDP::Configuration& tl_udp_confi_, int process_id_); +} diff --git a/ecal/core/src/cimpl/ecal_registration_cimpl.cpp b/ecal/core/src/cimpl/ecal_registration_cimpl.cpp new file mode 100644 index 0000000000..b01590e934 --- /dev/null +++ b/ecal/core/src/cimpl/ecal_registration_cimpl.cpp @@ -0,0 +1,179 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @file ecal_registration_cimpl.cpp + * @brief eCAL registration c interface +**/ + +#include +#include + +#include "ecal_common_cimpl.h" + +extern "C" +{ + ECALC_API int eCAL_Registration_GetTopicTypeName(const char* topic_name_, void* topic_type_, int topic_type_len_) + { + if (topic_name_ == nullptr) return(0); + if (topic_type_ == nullptr) return(0); + eCAL::SDataTypeInformation topic_info; + if (eCAL::Registration::GetTopicDataTypeInformation(topic_name_, topic_info)) + { + return(CopyBuffer(topic_type_, topic_type_len_, topic_info.name)); + } + return(0); + } + + ECALC_API int eCAL_Registration_GetTopicEncoding(const char* topic_name_, void* topic_encoding_, int topic_encoding_len_) + { + if (topic_name_ == nullptr) return(0); + if (topic_encoding_ == nullptr) return(0); + eCAL::SDataTypeInformation topic_info; + if (eCAL::Registration::GetTopicDataTypeInformation(topic_name_, topic_info)) + { + return(CopyBuffer(topic_encoding_, topic_encoding_len_, topic_info.encoding)); + } + return(0); + } + + ECALC_API int eCAL_Registration_GetTopicDescription(const char* topic_name_, void* topic_desc_, int topic_desc_len_) + { + if (topic_name_ == nullptr) return(0); + if (topic_desc_ == nullptr) return(0); + eCAL::SDataTypeInformation topic_info; + if (eCAL::Registration::GetTopicDataTypeInformation(topic_name_, topic_info)) + { + return(CopyBuffer(topic_desc_, topic_desc_len_, topic_info.descriptor)); + } + return(0); + } + + ECALC_API int eCAL_Registration_GetServiceRequestTypeName(const char* service_name_, const char* method_name_, void* req_type_, int req_type_len_) + { + if (service_name_ == nullptr) return(0); + if (method_name_ == nullptr) return(0); + if (req_type_ == nullptr) return(0); + std::string req_type; + std::string resp_type; + if (eCAL::Registration::GetServiceTypeNames(service_name_, method_name_, req_type, resp_type)) + { + return(CopyBuffer(req_type_, req_type_len_, req_type)); + } + return 0; + } + + ECALC_API int eCAL_Registration_GetServiceResponseTypeName(const char* service_name_, const char* method_name_, void* resp_type_, int resp_type_len_) + { + if (service_name_ == nullptr) return(0); + if (method_name_ == nullptr) return(0); + if (resp_type_ == nullptr) return(0); + std::string req_type; + std::string resp_type; + if (eCAL::Registration::GetServiceTypeNames(service_name_, method_name_, req_type, resp_type)) + { + return(CopyBuffer(resp_type_, resp_type_len_, resp_type)); + } + return 0; + } + + ECALC_API int eCAL_Registration_GetServiceRequestDescription(const char* service_name_, const char* method_name_, void* req_desc_, int req_desc_len_) + { + if (service_name_ == nullptr) return(0); + if (method_name_ == nullptr) return(0); + if (req_desc_ == nullptr) return(0); + std::string req_desc; + std::string resp_desc; + if (eCAL::Registration::GetServiceDescription(service_name_, method_name_, req_desc, resp_desc)) + { + return(CopyBuffer(req_desc_, req_desc_len_, req_desc)); + } + return 0; + } + + ECALC_API int eCAL_Registration_GetServiceResponseDescription(const char* service_name_, const char* method_name_, void* resp_desc_, int resp_desc_len_) + { + if (service_name_ == nullptr) return(0); + if (method_name_ == nullptr) return(0); + if (resp_desc_ == nullptr) return(0); + std::string req_desc; + std::string resp_desc; + if (eCAL::Registration::GetServiceDescription(service_name_, method_name_, req_desc, resp_desc)) + { + return(CopyBuffer(resp_desc_, resp_desc_len_, resp_desc)); + } + return 0; + } + + ECALC_API int eCAL_Registration_GetClientRequestTypeName(const char* client_name_, const char* method_name_, void* req_type_, int req_type_len_) + { + if (client_name_ == nullptr) return(0); + if (method_name_ == nullptr) return(0); + if (req_type_ == nullptr) return(0); + std::string req_type; + std::string resp_type; + if (eCAL::Registration::GetClientTypeNames(client_name_, method_name_, req_type, resp_type)) + { + return(CopyBuffer(req_type_, req_type_len_, req_type)); + } + return 0; + } + + ECALC_API int eCAL_Registration_GetClientResponseTypeName(const char* client_name_, const char* method_name_, void* resp_type_, int resp_type_len_) + { + if (client_name_ == nullptr) return(0); + if (method_name_ == nullptr) return(0); + if (resp_type_ == nullptr) return(0); + std::string req_type; + std::string resp_type; + if (eCAL::Registration::GetClientTypeNames(client_name_, method_name_, req_type, resp_type)) + { + return(CopyBuffer(resp_type_, resp_type_len_, resp_type)); + } + return 0; + } + + ECALC_API int eCAL_Registration_GetClientRequestDescription(const char* client_name_, const char* method_name_, void* req_desc_, int req_desc_len_) + { + if (client_name_ == nullptr) return(0); + if (method_name_ == nullptr) return(0); + if (req_desc_ == nullptr) return(0); + std::string req_desc; + std::string resp_desc; + if (eCAL::Registration::GetClientDescription(client_name_, method_name_, req_desc, resp_desc)) + { + return(CopyBuffer(req_desc_, req_desc_len_, req_desc)); + } + return 0; + } + + ECALC_API int eCAL_Registration_GetClientResponseDescription(const char* client_name_, const char* method_name_, void* resp_desc_, int resp_desc_len_) + { + if (client_name_ == nullptr) return(0); + if (method_name_ == nullptr) return(0); + if (resp_desc_ == nullptr) return(0); + std::string req_desc; + std::string resp_desc; + if (eCAL::Registration::GetClientDescription(client_name_, method_name_, req_desc, resp_desc)) + { + return(CopyBuffer(resp_desc_, resp_desc_len_, resp_desc)); + } + return 0; + } +} diff --git a/ecal/core/src/cimpl/ecal_util_cimpl.cpp b/ecal/core/src/cimpl/ecal_util_cimpl.cpp index da01819ac9..cdf16d0dbe 100644 --- a/ecal/core/src/cimpl/ecal_util_cimpl.cpp +++ b/ecal/core/src/cimpl/ecal_util_cimpl.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,152 +56,4 @@ extern "C" { eCAL::Util::EnableLoopback(state_ != 0); } - - ECALC_API int eCAL_Util_GetTopicTypeName(const char* topic_name_, void* topic_type_, int topic_type_len_) - { - if (topic_name_ == nullptr) return(0); - if (topic_type_ == nullptr) return(0); - eCAL::SDataTypeInformation topic_info; - if (eCAL::Util::GetTopicDataTypeInformation(topic_name_, topic_info)) - { - return(CopyBuffer(topic_type_, topic_type_len_, topic_info.name)); - } - return(0); - } - - ECALC_API int eCAL_Util_GetTopicEncoding(const char* topic_name_, void* topic_encoding_, int topic_encoding_len_) - { - if (topic_name_ == nullptr) return(0); - if (topic_encoding_ == nullptr) return(0); - eCAL::SDataTypeInformation topic_info; - if (eCAL::Util::GetTopicDataTypeInformation(topic_name_, topic_info)) - { - return(CopyBuffer(topic_encoding_, topic_encoding_len_, topic_info.encoding)); - } - return(0); - } - - ECALC_API int eCAL_Util_GetTopicDescription(const char* topic_name_, void* topic_desc_, int topic_desc_len_) - { - if (topic_name_ == nullptr) return(0); - if (topic_desc_ == nullptr) return(0); - eCAL::SDataTypeInformation topic_info; - if (eCAL::Util::GetTopicDataTypeInformation(topic_name_, topic_info)) - { - return(CopyBuffer(topic_desc_, topic_desc_len_, topic_info.descriptor)); - } - return(0); - } - - ECALC_API int eCAL_Util_GetServiceRequestTypeName(const char* service_name_, const char* method_name_, void* req_type_, int req_type_len_) - { - if (service_name_ == nullptr) return(0); - if (method_name_ == nullptr) return(0); - if (req_type_ == nullptr) return(0); - std::string req_type; - std::string resp_type; - if (eCAL::Util::GetServiceTypeNames(service_name_, method_name_, req_type, resp_type)) - { - return(CopyBuffer(req_type_, req_type_len_, req_type)); - } - return 0; - } - - ECALC_API int eCAL_Util_GetServiceResponseTypeName(const char* service_name_, const char* method_name_, void* resp_type_, int resp_type_len_) - { - if (service_name_ == nullptr) return(0); - if (method_name_ == nullptr) return(0); - if (resp_type_ == nullptr) return(0); - std::string req_type; - std::string resp_type; - if (eCAL::Util::GetServiceTypeNames(service_name_, method_name_, req_type, resp_type)) - { - return(CopyBuffer(resp_type_, resp_type_len_, resp_type)); - } - return 0; - } - - ECALC_API int eCAL_Util_GetServiceRequestDescription(const char* service_name_, const char* method_name_, void* req_desc_, int req_desc_len_) - { - if (service_name_ == nullptr) return(0); - if (method_name_ == nullptr) return(0); - if (req_desc_ == nullptr) return(0); - std::string req_desc; - std::string resp_desc; - if (eCAL::Util::GetServiceDescription(service_name_, method_name_, req_desc, resp_desc)) - { - return(CopyBuffer(req_desc_, req_desc_len_, req_desc)); - } - return 0; - } - - ECALC_API int eCAL_Util_GetServiceResponseDescription(const char* service_name_, const char* method_name_, void* resp_desc_, int resp_desc_len_) - { - if (service_name_ == nullptr) return(0); - if (method_name_ == nullptr) return(0); - if (resp_desc_ == nullptr) return(0); - std::string req_desc; - std::string resp_desc; - if (eCAL::Util::GetServiceDescription(service_name_, method_name_, req_desc, resp_desc)) - { - return(CopyBuffer(resp_desc_, resp_desc_len_, resp_desc)); - } - return 0; - } - - ECALC_API int eCAL_Util_GetClientRequestTypeName(const char* client_name_, const char* method_name_, void* req_type_, int req_type_len_) - { - if (client_name_ == nullptr) return(0); - if (method_name_ == nullptr) return(0); - if (req_type_ == nullptr) return(0); - std::string req_type; - std::string resp_type; - if (eCAL::Util::GetClientTypeNames(client_name_, method_name_, req_type, resp_type)) - { - return(CopyBuffer(req_type_, req_type_len_, req_type)); - } - return 0; - } - - ECALC_API int eCAL_Util_GetClientResponseTypeName(const char* client_name_, const char* method_name_, void* resp_type_, int resp_type_len_) - { - if (client_name_ == nullptr) return(0); - if (method_name_ == nullptr) return(0); - if (resp_type_ == nullptr) return(0); - std::string req_type; - std::string resp_type; - if (eCAL::Util::GetClientTypeNames(client_name_, method_name_, req_type, resp_type)) - { - return(CopyBuffer(resp_type_, resp_type_len_, resp_type)); - } - return 0; - } - - ECALC_API int eCAL_Util_GetClientRequestDescription(const char* client_name_, const char* method_name_, void* req_desc_, int req_desc_len_) - { - if (client_name_ == nullptr) return(0); - if (method_name_ == nullptr) return(0); - if (req_desc_ == nullptr) return(0); - std::string req_desc; - std::string resp_desc; - if (eCAL::Util::GetClientDescription(client_name_, method_name_, req_desc, resp_desc)) - { - return(CopyBuffer(req_desc_, req_desc_len_, req_desc)); - } - return 0; - } - - ECALC_API int eCAL_Util_GetClientResponseDescription(const char* client_name_, const char* method_name_, void* resp_desc_, int resp_desc_len_) - { - if (client_name_ == nullptr) return(0); - if (method_name_ == nullptr) return(0); - if (resp_desc_ == nullptr) return(0); - std::string req_desc; - std::string resp_desc; - if (eCAL::Util::GetClientDescription(client_name_, method_name_, req_desc, resp_desc)) - { - return(CopyBuffer(resp_desc_, resp_desc_len_, resp_desc)); - } - return 0; - } } diff --git a/ecal/core/src/config/configuration_reader.cpp b/ecal/core/src/config/configuration_reader.cpp new file mode 100644 index 0000000000..1173c091ae --- /dev/null +++ b/ecal/core/src/config/configuration_reader.cpp @@ -0,0 +1,122 @@ +#include "configuration_reader.h" + +namespace +{ + void MapConfiguration(const YAML::Node& node_, eCAL::Configuration& config_) + { + YAML::AssignValue(config_.transport_layer, node_, "transport_layer"); + YAML::AssignValue(config_.publisher, node_, "publisher"); + YAML::AssignValue(config_.subscriber, node_, "subscriber"); + YAML::AssignValue(config_.registration, node_, "registration"); + YAML::AssignValue(config_.monitoring, node_, "monitoring"); + YAML::AssignValue(config_.timesync, node_, "time"); + YAML::AssignValue(config_.service, node_, "service"); + YAML::AssignValue(config_.application, node_, "application"); + YAML::AssignValue(config_.logging, node_, "logging"); + } +} + + +namespace eCAL +{ + namespace Config + { + void YamlFileToConfig(const std::string& filename_, eCAL::Configuration& config_) + { + const YAML::Node yaml = YAML::LoadFile(filename_); + + MapConfiguration(yaml, config_); + } + + void YamlStringToConfig(const std::string& yaml_string_, eCAL::Configuration& config_) + { + const YAML::Node yaml = YAML::Load(yaml_string_); + + MapConfiguration(yaml, config_); + } + + bool ConfigToYamlFile(const std::string& file_name_, const eCAL::Configuration& config_) + { + const YAML::Node node(config_); + std::ofstream file(file_name_); + if (file.is_open()) + { + file << node; + file.close(); + return true; + } + + return false; + } + + void MergeYamlNodes(YAML::Node& base, const YAML::Node& other) + { + std::stack> nodes; + nodes.emplace(base, other); + + while (!nodes.empty()) + { + const std::pair nodePair = nodes.top(); + nodes.pop(); + + YAML::Node baseNode = nodePair.first; + YAML::Node otherNode = nodePair.second; + + for (YAML::const_iterator it = otherNode.begin(); it != otherNode.end(); ++it) + { + const YAML::Node key = it->first; + const YAML::Node value = it->second; + + std::string key_as_string; + + switch (key.Type()) + { + case YAML::NodeType::Scalar: + key_as_string = key.as(); + break; + default: + continue; + break; + } + + if (baseNode[key_as_string]) + { + if (value.IsMap() && baseNode[key_as_string].IsMap()) + { + nodes.emplace(baseNode[key_as_string], value); // Push nested nodes to stack + } + else + { + baseNode[key_as_string] = value; // Overwrite value for non-map nodes + } + } + else + { + baseNode[key_as_string] = value; // Add new key-value pairs + } + } + } + } + + bool MergeYamlIntoConfiguration(const std::string& file_name_ , eCAL::Configuration& config_) + { + YAML::Node config_node(config_); + + YAML::Node node_to_merge; + try + { + node_to_merge = YAML::LoadFile(file_name_); + } + catch (std::exception& e) + { + std::cout << "Error during reading yml file: " << e.what() << "\n"; + return false; + } + + eCAL::Config::MergeYamlNodes(config_node, node_to_merge); + MapConfiguration(config_node, config_); + + return true; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/config/configuration_reader.h b/ecal/core/src/config/configuration_reader.h new file mode 100644 index 0000000000..c92abb8978 --- /dev/null +++ b/ecal/core/src/config/configuration_reader.h @@ -0,0 +1,60 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief Global eCAL configuration interface +**/ + +#ifndef ECAL_CONFIGURATION_READER +#define ECAL_CONFIGURATION_READER + +#include +#ifndef YAML_CPP_STATIC_DEFINE +#define YAML_CPP_STATIC_DEFINE +#endif +#include + +#include "configuration_to_yaml.h" + +#include +#include +#include + +namespace eCAL +{ + namespace Config + { + // Read a yaml file and convert it to an eCAL configuration + void YamlFileToConfig(const std::string& filename_, eCAL::Configuration& config_); + + // Read a yaml string and convert it to an eCAL configuration + void YamlStringToConfig(const std::string& yaml_string_, eCAL::Configuration& config_); + + // Write an eCAL configuration to a yaml file + bool ConfigToYamlFile(const std::string& file_name_, const eCAL::Configuration& config_); + + // Merge two yaml nodes, priority second parameter + void MergeYamlNodes(YAML::Node& base, const YAML::Node& other); + + // Merge a yaml file into an existing eCAL configuration + bool MergeYamlIntoConfiguration(const std::string& file_name_ , eCAL::Configuration& config_); + } +} + +#endif // ECAL_CONFIGURATION_READER \ No newline at end of file diff --git a/ecal/core/src/config/configuration_to_yaml.cpp b/ecal/core/src/config/configuration_to_yaml.cpp new file mode 100644 index 0000000000..d1bea3cab3 --- /dev/null +++ b/ecal/core/src/config/configuration_to_yaml.cpp @@ -0,0 +1,725 @@ +#include "configuration_to_yaml.h" + +// utility functions for yaml node handling +namespace YAML +{ + template + void AssignValue(MEM& member, const YAML::Node& node_, const char* key) + { + if (node_[key]) + member = node_[key].as(); + } + + // Operator overload for assigning a ConstrainedInteger to a YAML::Node + template + YAML::Node operator<<(YAML::Node node, const eCAL::Types::ConstrainedInteger& constrainedInt) + { + node = static_cast(constrainedInt); + return node; + } + + eCAL_Logging_Filter ParseLogLevel(const std::vector& filter_) + { + // create excluding filter list + char filter_mask = log_level_none; + for (const auto& it : filter_) + { + if (it == "all") filter_mask |= log_level_all; + if (it == "info") filter_mask |= log_level_info; + if (it == "warning") filter_mask |= log_level_warning; + if (it == "error") filter_mask |= log_level_error; + if (it == "fatal") filter_mask |= log_level_fatal; + if (it == "debug1") filter_mask |= log_level_debug1; + if (it == "debug2") filter_mask |= log_level_debug2; + if (it == "debug3") filter_mask |= log_level_debug3; + if (it == "debug4") filter_mask |= log_level_debug4; + } + + return(filter_mask); + } + + std::vector LogLevelToVector(eCAL_Logging_Filter filter_mask) + { + std::vector filter; + if ((filter_mask & log_level_all) != 0) filter.emplace_back("all"); + if ((filter_mask & log_level_info) != 0) filter.emplace_back("info"); + if ((filter_mask & log_level_warning) != 0) filter.emplace_back("warning"); + if ((filter_mask & log_level_error) != 0) filter.emplace_back("error"); + if ((filter_mask & log_level_fatal) != 0) filter.emplace_back("fatal"); + if ((filter_mask & log_level_debug1) != 0) filter.emplace_back("debug1"); + if ((filter_mask & log_level_debug2) != 0) filter.emplace_back("debug2"); + if ((filter_mask & log_level_debug3) != 0) filter.emplace_back("debug3"); + if ((filter_mask & log_level_debug4) != 0) filter.emplace_back("debug4"); + + return filter; + } + + eCAL::Publisher::Configuration::LayerPriorityVector transformLayerStrToEnum(const std::vector& string_vector_) + { + eCAL::Publisher::Configuration::LayerPriorityVector layer_priority_vector; + for (const auto& layer_as_string : string_vector_) + { + if (layer_as_string == "shm") layer_priority_vector.emplace_back(eCAL::TLayer::tlayer_shm); + if (layer_as_string == "udp") layer_priority_vector.emplace_back(eCAL::TLayer::tlayer_udp_mc); + if (layer_as_string == "tcp") layer_priority_vector.emplace_back(eCAL::TLayer::tlayer_tcp); + } + + return layer_priority_vector; + } + + std::vector transformLayerEnumToStr(const eCAL::Publisher::Configuration::LayerPriorityVector& enum_vector_) + { + std::vector layer_priority_vector; + for (const auto& layer_as_enum : enum_vector_) + { + switch (layer_as_enum) + { + case eCAL::TLayer::tlayer_shm: + layer_priority_vector.emplace_back("shm"); + break; + case eCAL::TLayer::tlayer_udp_mc: + layer_priority_vector.emplace_back("udp"); + break; + case eCAL::TLayer::tlayer_tcp: + layer_priority_vector.emplace_back("tcp"); + break; + default: + break; + } + } + + return layer_priority_vector; + } +} + +namespace YAML +{ + /* + ___ _ __ __ _ + / _ \___ ___ _(_)__ / /________ _/ /_(_)__ ___ + / , _/ -_) _ `/ (_-::encode(const eCAL::Registration::Layer::UDP::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + node["port"] = config_.port; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Registration::Layer::UDP::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + AssignValue(config_.port, node_, "port"); + return true; + } + + + Node convert::encode(const eCAL::Registration::Layer::SHM::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + node["domain"] = config_.domain; + node["queue_size"] = config_.queue_size; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Registration::Layer::SHM::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + AssignValue(config_.domain, node_, "domain"); + AssignValue(config_.queue_size, node_, "queue_size"); + return true; + } + + Node convert::encode(const eCAL::Registration::Layer::Configuration& config_) + { + Node node; + node["shm"] = config_.shm; + node["udp"] = config_.udp; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Registration::Layer::Configuration& config_) + { + AssignValue(config_.shm, node_, "shm"); + AssignValue(config_.udp, node_, "udp"); + return true; + } + + Node convert::encode(const eCAL::Registration::Configuration& config_) + { + Node node; + node["registration_timeout"] = config_.registration_timeout; + node["registration_refresh"] = config_.registration_refresh; + node["network_enabled"] = config_.network_enabled; + node["loopback"] = config_.loopback; + node["host_group_name"] = config_.host_group_name; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Registration::Configuration& config_) + { + AssignValue(config_.registration_timeout, node_, "registration_timeout"); + AssignValue(config_.registration_refresh, node_, "registration_refresh"); + AssignValue(config_.network_enabled, node_, "network_enabled"); + AssignValue(config_.loopback, node_, "loopback"); + AssignValue(config_.layer, node_, "layer"); + + // By default the host_group_name is set with the current host name. + // If the user does not specify the host group name in the yaml, leave it like it is. + std::string host_group_name; + AssignValue(host_group_name, node_, "host_group_name"); + if (!host_group_name.empty()) config_.host_group_name = host_group_name; + + return true; + } + + + /* + __ ___ _ __ _ + / |/ /__ ___ (_) /____ ____(_)__ ___ _ + / /|_/ / _ \/ _ \/ / __/ _ \/ __/ / _ \/ _ `/ + /_/ /_/\___/_//_/_/\__/\___/_/ /_/_//_/\_, / + /___/ + */ + + Node convert::encode(const eCAL::Monitoring::Configuration& config_) + { + Node node; + node["filter_excl"] = config_.filter_excl; + node["filter_incl"] = config_.filter_incl; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::Monitoring::Configuration& config_) + { + AssignValue(config_.filter_excl, node_, "filter_excl"); + AssignValue(config_.filter_incl, node_, "filter_incl"); + return true; + } + + + /* + ______ __ __ + /_ __/______ ____ ___ ___ ___ ____/ /_/ / ___ ___ _____ ____ + / / / __/ _ `/ _ \(_-::encode(const eCAL::TransportLayer::SHM::Configuration& config_) + { + Node node; + node["memfile_min_size_bytes"] << config_.memfile_min_size_bytes; + node["memfile_reserve_percent"] << config_.memfile_reserve_percent; + return node; + } + + bool convert::decode(const Node& node_, eCAL::TransportLayer::SHM::Configuration& config_) + { + AssignValue(config_.memfile_min_size_bytes, node_, "memfile_min_size_bytes"); + AssignValue(config_.memfile_reserve_percent, node_, "memfile_reserve_percent"); + return true; + } + + Node convert::encode(const eCAL::TransportLayer::TCP::Configuration& config_) + { + Node node; + node["number_executor_reader"] = config_.number_executor_reader; + node["number_executor_writer"] = config_.number_executor_writer; + node["max_reconnections"] = config_.max_reconnections; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::TransportLayer::TCP::Configuration& config_) + { + AssignValue(config_.number_executor_reader, node_, "number_executor_reader"); + AssignValue(config_.number_executor_writer, node_, "number_executor_writer"); + AssignValue(config_.max_reconnections, node_, "max_reconnections"); + return true; + } + + Node convert::encode(const eCAL::TransportLayer::UDP::Network::Configuration& config_) + { + Node node; + node["group"] = config_.group.Get(); + node["ttl"] = config_.ttl; + return node; + } + + bool convert::decode(const Node& node_, eCAL::TransportLayer::UDP::Network::Configuration& config_) + { + AssignValue(config_.group, node_, "group"); + AssignValue(config_.ttl, node_, "ttl"); + return true; + } + + Node convert::encode(const eCAL::TransportLayer::UDP::Configuration& config_) + { + Node node; + node["config_version"] = config_.config_version == eCAL::Types::UdpConfigVersion::V1 ? "v1" : "v2"; + node["mode"] = config_.mode == eCAL::Types::UDPMode::LOCAL ? "local" : "network"; + node["port"] = config_.port; + node["mask"] = config_.mask.Get(); + node["send_buffer"] << config_.send_buffer; + node["receive_buffer"] << config_.receive_buffer; + node["join_all_interfaces"] = config_.join_all_interfaces; + node["npcap_enabled"] = config_.npcap_enabled; + node["network"] = config_.network; + return node; + } + + bool convert::decode(const Node& node_, eCAL::TransportLayer::UDP::Configuration& config_) + { + std::string temp_string = "v2"; + AssignValue(temp_string, node_, "config_version"); + config_.config_version = temp_string == "v2" ? eCAL::Types::UdpConfigVersion::V2 : eCAL::Types::UdpConfigVersion::V1; + temp_string = "local"; + AssignValue(temp_string, node_, "mode"); + config_.mode = temp_string == "local" ? eCAL::Types::UDPMode::LOCAL : eCAL::Types::UDPMode::NETWORK; + AssignValue(config_.port, node_, "port"); + AssignValue(config_.mask, node_, "mask"); + AssignValue(config_.send_buffer, node_, "send_buffer"); + AssignValue(config_.receive_buffer, node_, "receive_buffer"); + AssignValue(config_.join_all_interfaces, node_, "join_all_interfaces"); + AssignValue(config_.npcap_enabled, node_, "npcap_enabled"); + + AssignValue(config_.network, node_, "network"); + return true; + } + + Node convert::encode(const eCAL::TransportLayer::Configuration& config_) + { + Node node; + node["shm"] = config_.shm; + node["udp"] = config_.udp; + node["tcp"] = config_.tcp; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::TransportLayer::Configuration& config_) + { + AssignValue(config_.shm, node_, "shm"); + AssignValue(config_.udp, node_, "udp"); + AssignValue(config_.tcp, node_, "tcp"); + return true; + } + + + /* + ___ __ ___ __ + / _ \__ __/ / / (_)__ / / ___ ____ + / ___/ // / _ \/ / (_-::encode(const eCAL::Publisher::Layer::SHM::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + node["zero_copy_mode"] = config_.zero_copy_mode; + node["acknowledge_timeout_ms"] = config_.acknowledge_timeout_ms; + node["memfile_buffer_count"] = config_.memfile_buffer_count; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Publisher::Layer::SHM::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + AssignValue(config_.zero_copy_mode, node_, "zero_copy_mode"); + AssignValue(config_.acknowledge_timeout_ms, node_, "acknowledge_timeout_ms"); + AssignValue(config_.memfile_buffer_count, node_, "memfile_buffer_count"); + return true; + } + + Node convert::encode(const eCAL::Publisher::Layer::UDP::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::Publisher::Layer::UDP::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + return true; + } + + Node convert::encode(const eCAL::Publisher::Layer::TCP::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::Publisher::Layer::TCP::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + return true; + } + + Node convert::encode(const eCAL::Publisher::Layer::Configuration& config_) + { + Node node; + node["shm"] = config_.shm; + node["udp"] = config_.udp; + node["tcp"] = config_.tcp; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Publisher::Layer::Configuration& config_) + { + AssignValue(config_.shm, node_, "shm"); + AssignValue(config_.udp, node_, "udp"); + AssignValue(config_.tcp, node_, "tcp"); + return true; + } + + Node convert::encode(const eCAL::Publisher::Configuration& config_) + { + Node node; + node["share_topic_description"] = config_.share_topic_description; + node["share_topic_type"] = config_.share_topic_type; + node["layer"] = config_.layer; + node["priority_local"] = transformLayerEnumToStr(config_.layer_priority_local); + node["priority_network"] = transformLayerEnumToStr(config_.layer_priority_remote); + return node; + } + + bool convert::decode(const Node& node_, eCAL::Publisher::Configuration& config_) + { + AssignValue(config_.share_topic_description, node_, "share_topic_description"); + AssignValue(config_.share_topic_type, node_, "share_topic_type"); + + std::vector tmp; + AssignValue>(tmp, node_, "priority_local"); + config_.layer_priority_local = transformLayerStrToEnum(tmp); + tmp.clear(); + AssignValue>(tmp, node_, "priority_network"); + config_.layer_priority_remote = transformLayerStrToEnum(tmp); + + AssignValue(config_.layer, node_, "layer"); + return true; + } + + + /* + ____ __ _ __ + / __/_ __/ / ___ ________(_) / ___ ____ + _\ \/ // / _ \(_-::encode(const eCAL::Subscriber::Layer::SHM::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Subscriber::Layer::SHM::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + return true; + } + + Node convert::encode(const eCAL::Subscriber::Layer::UDP::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Subscriber::Layer::UDP::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + return true; + } + + Node convert::encode(const eCAL::Subscriber::Layer::TCP::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Subscriber::Layer::TCP::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + return true; + } + + Node convert::encode(const eCAL::Subscriber::Layer::Configuration& config_) + { + Node node; + node["shm"] = config_.shm; + node["udp"] = config_.udp; + node["tcp"] = config_.tcp; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Subscriber::Layer::Configuration& config_) + { + AssignValue(config_.shm, node_, "shm"); + AssignValue(config_.udp, node_, "udp"); + AssignValue(config_.tcp, node_, "tcp"); + return true; + } + + Node convert::encode(const eCAL::Subscriber::Configuration& config_) + { + Node node; + node["layer"] = config_.layer; + node["drop_out_of_order_message"] = config_.drop_out_of_order_messages; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Subscriber::Configuration& config_) + { + AssignValue(config_.layer, node_, "layer"); + AssignValue(config_.drop_out_of_order_messages, node_, "dropt_out_of_order_messages"); + return true; + } + + + /* + _______ + /_ __(_)_ _ ___ + / / / / ' \/ -_) + /_/ /_/_/_/_/\__/ + */ + + Node convert::encode(const eCAL::Time::Configuration& config_) + { + Node node; + node["replay"] = config_.timesync_module_replay; + node["rt"] = config_.timesync_module_rt; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::Time::Configuration& config_) + { + AssignValue(config_.timesync_module_replay, node_, "replay"); + AssignValue(config_.timesync_module_rt, node_, "rt"); + return true; + } + + + /* + ____ _ + / __/__ _____ __(_)______ + _\ \/ -_) __/ |/ / / __/ -_) + /___/\__/_/ |___/_/\__/\__/ + */ + + Node convert::encode(const eCAL::Service::Configuration& config_) + { + Node node; + node["protocol_v0"] = config_.protocol_v0; + node["protocol_v1"] = config_.protocol_v1; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::Service::Configuration& config_) + { + AssignValue(config_.protocol_v0, node_, "protocol_v0"); + AssignValue(config_.protocol_v1, node_, "protocol_v1"); + return true; + } + + + /* + ___ ___ __ _ + / _ | ___ ___ / (_)______ _/ /_(_)__ ___ + / __ |/ _ \/ _ \/ / / __/ _ `/ __/ / _ \/ _ \ + /_/ |_/ .__/ .__/_/_/\__/\_,_/\__/_/\___/_//_/ + /_/ /_/ + */ + + Node convert::encode(const eCAL::Application::Startup::Configuration& config_) + { + Node node; + node["emulator"] = config_.terminal_emulator; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::Application::Startup::Configuration& config_) + { + AssignValue(config_.terminal_emulator, node_, "emulator"); + + return true; + } + + Node convert::encode(const eCAL::Application::Sys::Configuration& config_) + { + Node node; + node["filter_excl"] = config_.filter_excl; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::Application::Sys::Configuration& config_) + { + AssignValue(config_.filter_excl, node_, "filter_excl"); + return true; + } + + Node convert::encode(const eCAL::Application::Configuration& config_) + { + Node node; + node["terminal"] = config_.startup; + node["sys"] = config_.sys; + + return node; + } + + bool convert::decode(const Node& node_, eCAL::Application::Configuration& config_) + { + AssignValue(config_.startup, node_, "terminal"); + AssignValue(config_.sys, node_, "sys"); + return true; + } + + /* + __ _ + / / ___ ___ ____ _(_)__ ___ _ + / /__/ _ \/ _ `/ _ `/ / _ \/ _ `/ + /____/\___/\_, /\_, /_/_//_/\_, / + /___//___/ /___/ + */ + + Node convert::encode(const eCAL::Logging::Sinks::UDP::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + node["port"] = config_.port; + node["level"] = LogLevelToVector(config_.filter_log_udp); + return node; + } + + bool convert::decode(const Node& node_, eCAL::Logging::Sinks::UDP::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + AssignValue(config_.port, node_, "port"); + + std::vector tmp; + AssignValue>(tmp, node_, "level"); + config_.filter_log_udp = ParseLogLevel(tmp); + return true; + } + + Node convert::encode(const eCAL::Logging::Sinks::Console::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + node["level"] = LogLevelToVector(config_.filter_log_con); + return node; + } + + bool convert::decode(const Node& node_, eCAL::Logging::Sinks::Console::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + std::vector tmp; + AssignValue>(tmp, node_, "level"); + config_.filter_log_con = ParseLogLevel(tmp); + return true; + } + + Node convert::encode(const eCAL::Logging::Sinks::File::Configuration& config_) + { + Node node; + node["enable"] = config_.enable; + node["path"] = config_.path; + node["level"] = LogLevelToVector(config_.filter_log_file); + return node; + } + + bool convert::decode(const Node& node_, eCAL::Logging::Sinks::File::Configuration& config_) + { + AssignValue(config_.enable, node_, "enable"); + AssignValue(config_.path, node_, "path"); + + std::vector tmp; + AssignValue>(tmp, node_, "level"); + config_.filter_log_file = ParseLogLevel(tmp); + return true; + } + + Node convert::encode(const eCAL::Logging::Sinks::Configuration& config_) + { + Node node; + node["console"] = config_.console; + node["file"] = config_.file; + node["udp"] = config_.udp; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Logging::Sinks::Configuration& config_) + { + AssignValue(config_.console, node_, "console"); + AssignValue(config_.file, node_, "file"); + AssignValue(config_.udp, node_, "udp"); + return true; + } + + Node convert::encode(const eCAL::Logging::Configuration& config_) + { + Node node; + node["sinks"] = config_.sinks; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Logging::Configuration& config_) + { + AssignValue(config_.sinks, node_, "sinks"); + return true; + } + + + /* + __ ___ _ ____ __ _ + / |/ /__ _(_)__ _______ ___ / _(_)__ ___ _________ _/ /_(_)__ ___ + / /|_/ / _ `/ / _ \ / __/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \ + /_/ /_/\_,_/_/_//_/ \__/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/ + /___/ + */ + + Node convert::encode(const eCAL::Configuration& config_) + { + Node node; + node["publisher"] = config_.publisher; + node["subscriber"] = config_.subscriber; + node["registration"] = config_.registration; + node["monitoring"] = config_.monitoring; + node["time"] = config_.timesync; + node["service"] = config_.service; + node["application"] = config_.application; + node["logging"] = config_.logging; + return node; + } + + bool convert::decode(const Node& node_, eCAL::Configuration& config_) + { + AssignValue(config_.transport_layer, node_, "transport_layer"); + AssignValue(config_.publisher, node_, "publisher"); + AssignValue(config_.subscriber, node_, "subscriber"); + AssignValue(config_.registration, node_, "registration"); + AssignValue(config_.monitoring, node_, "monitoring"); + AssignValue(config_.timesync, node_, "time"); + AssignValue(config_.service, node_, "service"); + AssignValue(config_.application, node_, "application"); + AssignValue(config_.logging, node_, "logging"); + return true; + } +} \ No newline at end of file diff --git a/ecal/core/src/config/configuration_to_yaml.h b/ecal/core/src/config/configuration_to_yaml.h new file mode 100644 index 0000000000..d6930d7a8a --- /dev/null +++ b/ecal/core/src/config/configuration_to_yaml.h @@ -0,0 +1,365 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief Utility class for parsing cmd line arguments into eCAL useful structures. +**/ + +#ifndef CONFIGURATION_TO_YAML_H +#define CONFIGURATION_TO_YAML_H + +#include + +#ifndef YAML_CPP_STATIC_DEFINE +#define YAML_CPP_STATIC_DEFINE +#endif +#include + +namespace YAML +{ + // Utility function to be used also in other files + template + void AssignValue(MEM& member, const YAML::Node& node_, const char* key); + + + /* + ___ _ __ __ _ + / _ \___ ___ _(_)__ / /________ _/ /_(_)__ ___ + / , _/ -_) _ `/ (_- + struct convert + { + static Node encode(const eCAL::Registration::Layer::UDP::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Registration::Layer::UDP::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Registration::Layer::SHM::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Registration::Layer::SHM::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Registration::Layer::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Registration::Layer::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Registration::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Registration::Configuration& config_); + }; + + + /* + __ ___ _ __ _ + / |/ /__ ___ (_) /____ ____(_)__ ___ _ + / /|_/ / _ \/ _ \/ / __/ _ \/ __/ / _ \/ _ `/ + /_/ /_/\___/_//_/_/\__/\___/_/ /_/_//_/\_, / + /___/ + */ + template<> + struct convert + { + static Node encode(const eCAL::Monitoring::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Monitoring::Configuration& config_); + }; + + + /* + ______ __ __ + /_ __/______ ____ ___ ___ ___ ____/ /_/ / ___ ___ _____ ____ + / / / __/ _ `/ _ \(_- + struct convert + { + static Node encode(const eCAL::TransportLayer::SHM::Configuration& config_); + + static bool decode(const Node& node_, eCAL::TransportLayer::SHM::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::TransportLayer::TCP::Configuration& config_); + + static bool decode(const Node& node_, eCAL::TransportLayer::TCP::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::TransportLayer::UDP::Network::Configuration& config_); + + static bool decode(const Node& node_, eCAL::TransportLayer::UDP::Network::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::TransportLayer::UDP::Configuration& config_); + + static bool decode(const Node& node_, eCAL::TransportLayer::UDP::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::TransportLayer::Configuration& config_); + + static bool decode(const Node& node_, eCAL::TransportLayer::Configuration& config_); + }; + + + /* + ___ __ ___ __ + / _ \__ __/ / / (_)__ / / ___ ____ + / ___/ // / _ \/ / (_- + struct convert + { + static Node encode(const eCAL::Publisher::Layer::SHM::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Publisher::Layer::SHM::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Publisher::Layer::UDP::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Publisher::Layer::UDP::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Publisher::Layer::TCP::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Publisher::Layer::TCP::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Publisher::Layer::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Publisher::Layer::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Publisher::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Publisher::Configuration& config_); + }; + + + /* + ____ __ _ __ + / __/_ __/ / ___ ________(_) / ___ ____ + _\ \/ // / _ \(_- + struct convert + { + static Node encode(const eCAL::Subscriber::Layer::SHM::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Subscriber::Layer::SHM::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Subscriber::Layer::UDP::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Subscriber::Layer::UDP::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Subscriber::Layer::TCP::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Subscriber::Layer::TCP::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Subscriber::Layer::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Subscriber::Layer::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Subscriber::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Subscriber::Configuration& config_); + }; + + + /* + _______ + /_ __(_)_ _ ___ + / / / / ' \/ -_) + /_/ /_/_/_/_/\__/ + */ + template<> + struct convert + { + static Node encode(const eCAL::Time::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Time::Configuration& config_); + }; + + + /* + ____ _ + / __/__ _____ __(_)______ + _\ \/ -_) __/ |/ / / __/ -_) + /___/\__/_/ |___/_/\__/\__/ + */ + template<> + struct convert + { + static Node encode(const eCAL::Service::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Service::Configuration& config_); + }; + + + /* + ___ ___ __ _ + / _ | ___ ___ / (_)______ _/ /_(_)__ ___ + / __ |/ _ \/ _ \/ / / __/ _ `/ __/ / _ \/ _ \ + /_/ |_/ .__/ .__/_/_/\__/\_,_/\__/_/\___/_//_/ + /_/ /_/ + */ + template<> + struct convert + { + static Node encode(const eCAL::Application::Startup::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Application::Startup::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Application::Sys::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Application::Sys::Configuration& config_); + }; + + + template<> + struct convert + { + static Node encode(const eCAL::Application::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Application::Configuration& config_); + }; + + /* + __ _ + / / ___ ___ ____ _(_)__ ___ _ + / /__/ _ \/ _ `/ _ `/ / _ \/ _ `/ + /____/\___/\_, /\_, /_/_//_/\_, / + /___//___/ /___/ + */ + template<> + struct convert + { + static Node encode(const eCAL::Logging::Sinks::UDP::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Logging::Sinks::UDP::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Logging::Sinks::Console::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Logging::Sinks::Console::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Logging::Sinks::File::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Logging::Sinks::File::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Logging::Sinks::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Logging::Sinks::Configuration& config_); + }; + + template<> + struct convert + { + static Node encode(const eCAL::Logging::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Logging::Configuration& config_); + }; + + + /* + __ ___ _ ____ __ _ + / |/ /__ _(_)__ _______ ___ / _(_)__ ___ _________ _/ /_(_)__ ___ + / /|_/ / _ `/ / _ \ / __/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \ + /_/ /_/\_,_/_/_//_/ \__/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/ + /___/ + */ + template<> + struct convert + { + static Node encode(const eCAL::Configuration& config_); + + static bool decode(const Node& node_, eCAL::Configuration& config_); + }; +} + +#endif // CONFIGURATION_TO_YAML_H \ No newline at end of file diff --git a/ecal/core/src/config/default_configuration.cpp b/ecal/core/src/config/default_configuration.cpp new file mode 100644 index 0000000000..9f7c3fbdd3 --- /dev/null +++ b/ecal/core/src/config/default_configuration.cpp @@ -0,0 +1,361 @@ +#include "default_configuration.h" + +#include "ecal/ecal_config.h" + +#include +#include + +namespace +{ + std::string quoteString(const std::string& str_) { + return std::string("\"") + str_ + std::string("\""); + } + + std::string logToArray(const eCAL_Logging_Filter& filter_) + { + std::string result = "["; + if ((filter_ & log_level_info) != 0) result += "\"info\", "; + if ((filter_ & log_level_warning) != 0) result += "\"warning\", "; + if ((filter_ & log_level_error) != 0) result += "\"error\", "; + if ((filter_ & log_level_fatal) != 0) result += "\"fatal\", "; + if ((filter_ & log_level_debug1) != 0) result += "\"debug1\", "; + if ((filter_ & log_level_debug2) != 0) result += "\"debug2\", "; + if ((filter_ & log_level_debug3) != 0) result += "\"debug3\", "; + if ((filter_ & log_level_debug4) != 0) result += "\"debug4\", "; + + if (result.size() == 1 && (filter_ & log_level_all) != 0) + { + result += "\"all\", "; + } + + if (result.size() > 1) + { + // remove the last ", " + result.pop_back(); + result.pop_back(); + } + + result += "]"; + return result; + } + + std::string quoteString(const eCAL::Publisher::Configuration::LayerPriorityVector& vector_) + { + std::string result = "["; + for (const auto& elem : vector_ ) + { + switch (elem) + { + case eCAL::TLayer::tlayer_shm: + result += "\"shm\", "; + break; + case eCAL::TLayer::tlayer_udp_mc: + result += "\"udp\", "; + break; + case eCAL::TLayer::tlayer_tcp: + result += "\"tcp\", "; + break; + default: + break; + } + } + + if (!vector_.empty()) + { + // remove the last ", " + result.pop_back(); + result.pop_back(); + } + + result += "]"; + return result; + } + + std::string quoteString(const eCAL::Types::UdpConfigVersion config_version_) { + switch (config_version_) + { + case eCAL::Types::UdpConfigVersion::V1: + return "\"v1\""; + break; + case eCAL::Types::UdpConfigVersion::V2: + return "\"v2\""; + break; + + default: + return ""; + break; + } + } + + std::string quoteString(const eCAL::Types::UDPMode mode_) + { + switch (mode_) + { + case eCAL::Types::UDPMode::LOCAL: + return "\"local\""; + break; + case eCAL::Types::UDPMode::NETWORK: + return "\"network\""; + break; + + default: + return ""; + break; + } + } + + std::string quoteString(const eCAL::Types::IpAddressV4& ip_) + { + return std::string("\"") + ip_.Get() + std::string("\""); + } +} + +namespace eCAL +{ + namespace Config + { + std::stringstream getConfigAsYamlSS(const eCAL::Configuration& config_) + { + std::stringstream ss; + ss << std::boolalpha; + ss << R"(# _____ _ _ ____ _ _ )" << "\n"; + ss << R"(# | ____|___| (_)_ __ ___ ___ ___ / ___| / \ | | )" << "\n"; + ss << R"(# | _| / __| | | '_ \/ __|/ _ \ _____ / _ \ | / _ \ | | )" << "\n"; + ss << R"(# | |__| (__| | | |_) \__ \ __/ |_____| | __/ |___ / ___ \| |___ )" << "\n"; + ss << R"(# |_____\___|_|_| .__/|___/\___| \___|\____/_/ \_\_____| )" << "\n"; + ss << R"(# |_| )" << "\n"; + ss << R"(# _ _ _ __ _ _ _ )" << "\n"; + ss << R"(# __ _| | ___ | |__ __ _| | ___ ___ _ __ / _(_) __ _ _ _ _ __ __ _| |_(_) ___ _ __ )" << "\n"; + ss << R"(# / _` | |/ _ \| '_ \ / _` | | / __/ _ \| '_ \| |_| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ )" << "\n"; + ss << R"(# | (_| | | (_) | |_) | (_| | | | (_| (_) | | | | _| | (_| | |_| | | | (_| | |_| | (_) | | | |)" << "\n"; + ss << R"(# \__, |_|\___/|_.__/ \__,_|_| \___\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_|)" << "\n"; + ss << R"(# |___/ |___/ )" << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# Registration layer configuration)" << "\n"; + ss << R"(registration:)" << "\n"; + ss << R"( # Topic registration refresh cylce (has to be smaller then registration timeout! Default: 1000))" << "\n"; + ss << R"( registration_refresh: )" << config_.registration.registration_refresh << "\n"; + ss << R"( # Timeout for topic registration in ms (internal, Default: 60000))" << "\n"; + ss << R"( registration_timeout: )" << config_.registration.registration_timeout << "\n"; + ss << R"( # Enable to receive registration information on the same local machine)" << "\n"; + ss << R"( loopback: )" << config_.registration.loopback << "\n"; + ss << R"( # Host group name that enables interprocess mechanisms across (virtual))" << "\n"; + ss << R"( # host borders (e.g, Docker); by default equivalent to local host name)" << "\n"; + ss << R"( host_group_name: )" << quoteString(config_.registration.host_group_name) << "\n"; + ss << R"( # true = all eCAL components communicate over network boundaries)" << "\n"; + ss << R"( # false = local host only communication (Default: false))" << "\n"; + ss << R"( network_enabled: )" << config_.registration.network_enabled << "\n"; + ss << R"()" << "\n"; + ss << R"( layer:)" << "\n"; + ss << R"( shm:)" << "\n"; + ss << R"( enable: )" << config_.registration.layer.shm.enable << "\n"; + ss << R"( # Domain name for shared memory based registration)" << "\n"; + ss << R"( domain: )" << quoteString(config_.registration.layer.shm.domain) << "\n"; + ss << R"( # Queue size of registration events)" << "\n"; + ss << R"( queue_size: )" << config_.registration.layer.shm.queue_size << "\n"; + ss << R"()" << "\n"; + ss << R"( udp:)" << "\n"; + ss << R"( enable: )" << config_.registration.layer.udp.enable << "\n"; + ss << R"( port: )" << config_.registration.layer.udp.port << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# Monitoring configuration)" << "\n"; + ss << R"(monitoring:)" << "\n"; + ss << R"( # Topics blacklist as regular expression (will not be monitored))" << "\n"; + ss << R"( filter_excl: )" << quoteString(config_.monitoring.filter_excl) << "\n"; + ss << R"( # Topics whitelist as regular expression (will be monitored only) (Default: ""))" << "\n"; + ss << R"( filter_incl: )" << quoteString(config_.monitoring.filter_incl) << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# Transport layer configuration)" << "\n"; + ss << R"(transport_layer:)" << "\n"; + ss << R"( udp:)" << "\n"; + ss << R"( # UDP configuration version (Since eCAL 5.12.))" << "\n"; + ss << R"( # v1: default behavior)" << "\n"; + ss << R"( # v2: new behavior, comes with a bit more intuitive handling regarding masking of the groups)" << "\n"; + ss << R"( config_version: )" << quoteString(config_.transport_layer.udp.config_version) << "\n"; + ss << R"( # Valid modes: local, network (Default: local))" << "\n"; + ss << R"( mode: )" << quoteString(config_.transport_layer.udp.mode) << "\n"; + ss << R"( # Multicast port number)" << "\n"; + ss << R"( port: )" << config_.transport_layer.udp.port << "\n"; + ss << R"( # v1: Mask maximum number of dynamic multicast group (range 0.0.0.1-0.0.0.255))" << "\n"; + ss << R"( # v2: Masks are now considered like routes masking (range 255.0.0.0-255.255.255.255))" << "\n"; + ss << R"( mask: )" << quoteString(config_.transport_layer.udp.mask) << "\n"; + ss << R"( # Send buffer in bytes)" << "\n"; + ss << R"( send_buffer: )" << config_.transport_layer.udp.send_buffer << "\n"; + ss << R"( # Receive buffer in bytes)" << "\n"; + ss << R"( receive_buffer: )" << config_.transport_layer.udp.receive_buffer << "\n"; + ss << R"( # Linux specific setting to join all network interfaces independend of their link state.)" << "\n"; + ss << R"( # Enabling ensures that eCAL processes receive data when they are started before the)" << "\n"; + ss << R"( # network devices are up and running.)" << "\n"; + ss << R"( join_all_interfaces: )" << config_.transport_layer.udp.join_all_interfaces << "\n"; + ss << R"( # Windows specific setting to enable receiving UDP traffic with the Npcap based receiver)" << "\n"; + ss << R"( npcap_enabled: )" << config_.transport_layer.udp.npcap_enabled << "\n"; + ss << R"()" << "\n"; + ss << R"( # In local mode multicast group and ttl are set by default and are not adjustable)" << "\n"; + ss << R"( local:)" << "\n"; + ss << R"( # Multicast group base. All registration and logging is sent on this address)" << "\n"; + ss << R"( # group: "127.0.0.1")" << "\n"; + ss << R"( # TTL (hop limit) is used to determine the amount of routers being traversed towards the destination)" << "\n"; + ss << R"( # ttl: 0)" << "\n"; + ss << R"()" << "\n"; + ss << R"( network:)" << "\n"; + ss << R"( # Multicast group base. All registration and logging is sent on this address)" << "\n"; + ss << R"( group: )" << quoteString(config_.transport_layer.udp.network.group) << "\n"; + ss << R"( # TTL (hop limit) is used to determine the amount of routers being traversed towards the destination)" << "\n"; + ss << R"( ttl: )" << config_.transport_layer.udp.network.ttl << "\n"; + ss << R"()" << "\n"; + ss << R"( tcp: )" << "\n"; + ss << R"( # Reader amount of threads that shall execute workload)" << "\n"; + ss << R"( number_executor_reader: )" << config_.transport_layer.tcp.number_executor_reader << "\n"; + ss << R"( # Writer amount of threads that shall execute workload)" << "\n"; + ss << R"( number_executor_writer: )" << config_.transport_layer.tcp.number_executor_writer << "\n"; + ss << R"( # Reconnection attemps the session will try to reconnect in case of an issue)" << "\n"; + ss << R"( max_reconnections: )" << config_.transport_layer.tcp.max_reconnections << "\n"; + ss << R"()" << "\n"; + ss << R"( shm:)" << "\n"; + ss << R"( # Default memory file size for new publisher)" << "\n"; + ss << R"( memfile_min_size_bytes: )" << config_.transport_layer.shm.memfile_min_size_bytes << "\n"; + ss << R"( # Dynamic file size reserve before recreating memory file if topic size changes)" << "\n"; + ss << R"( memfile_reserve_percent: )" << config_.transport_layer.shm.memfile_reserve_percent << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# Publisher specific base settings)" << "\n"; + ss << R"(publisher:)" << "\n"; + ss << R"( layer:)" << "\n"; + ss << R"( # Base configuration for shared memory publisher)" << "\n"; + ss << R"( shm:)" << "\n"; + ss << R"( # Enable layer)" << "\n"; + ss << R"( enable: )" << config_.publisher.layer.shm.enable << "\n"; + ss << R"( # Enable zero copy shared memory transport mode)" << "\n"; + ss << R"( zero_copy_mode: )" << config_.publisher.layer.shm.zero_copy_mode << "\n"; + ss << R"( # Force connected subscribers to send acknowledge event after processing the message.)" << "\n"; + ss << R"( # The publisher send call is blocked on this event with this timeout (0 == no handshake).)" << "\n"; + ss << R"( acknowledge_timeout_ms: )" << config_.publisher.layer.shm.acknowledge_timeout_ms << "\n"; + ss << R"( # Maximum number of used buffers (needs to be greater than 1, default = 1))" << "\n"; + ss << R"( memfile_buffer_count: )" << config_.publisher.layer.shm.memfile_buffer_count << "\n"; + ss << R"()" << "\n"; + ss << R"( # Base configuration for UDP publisher)" << "\n"; + ss << R"( udp:)" << "\n"; + ss << R"( # Enable layer)" << "\n"; + ss << R"( enable: )" << config_.publisher.layer.udp.enable << "\n"; + ss << R"()" << "\n"; + ss << R"( # Base configuration for TCP publisher)" << "\n"; + ss << R"( tcp:)" << "\n"; + ss << R"( # Enable layer)" << "\n"; + ss << R"( enable: )" << config_.publisher.layer.shm.enable << "\n"; + ss << R"()" << "\n"; + ss << R"( # Share topic type via registration)" << "\n"; + ss << R"( share_topic_type: )" << config_.publisher.share_topic_type << "\n"; + ss << R"( # Share topic description via registration)" << "\n"; + ss << R"( share_topic_description: )" << config_.publisher.share_topic_description << "\n"; + ss << R"( # Priority list for layer usage in local mode (Default: SHM > UDP > TCP))" << "\n"; + ss << R"( priority_local: )" << quoteString(config_.publisher.layer_priority_local) << "\n"; + ss << R"( # Priority list for layer usage in cloud mode (Default: UDP > TCP))" << "\n"; + ss << R"( priority_network: )" << quoteString(config_.publisher.layer_priority_remote) << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# Subscriber specific base configuration)" << "\n"; + ss << R"(subscriber:)" << "\n"; + ss << R"( layer:)" << "\n"; + ss << R"( # Base configuration for shared memory subscriber)" << "\n"; + ss << R"( shm:)" << "\n"; + ss << R"( # Enable layer)" << "\n"; + ss << R"( enable: )" << config_.subscriber.layer.shm.enable << "\n"; + ss << R"()" << "\n"; + ss << R"( # Base configuration for UDP subscriber)" << "\n"; + ss << R"( udp:)" << "\n"; + ss << R"( # Enabler layer)" << "\n"; + ss << R"( enable: )" << config_.subscriber.layer.udp.enable << "\n"; + ss << R"()" << "\n"; + ss << R"( # Base configuration for TCP subscriber)" << "\n"; + ss << R"( tcp:)" << "\n"; + ss << R"( # Enable layer)" << "\n"; + ss << R"( enable: )" << config_.subscriber.layer.tcp.enable << "\n"; + ss << R"()" << "\n"; + ss << R"( # Enable dropping of payload messages that arrive out of order)" << "\n"; + ss << R"( drop_out_of_order_messages: )" << config_.subscriber.drop_out_of_order_messages << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# Time configuration)" << "\n"; + ss << R"(time:)" << "\n"; + ss << R"( # Time synchronisation interface name (dynamic library))" << "\n"; + ss << R"( # The name will be extended with platform suffix (32|64), debug suffix (d) and platform extension (.dll|.so))" << "\n"; + ss << R"( # Available modules are:)" << "\n"; + ss << R"( # - ecaltime-localtime local system time without synchronization)" << "\n"; + ss << R"( # - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux)" << "\n"; + ss << R"( # (device configuration in ecaltime.ini))" << "\n"; + ss << R"( rt: )" << quoteString(config_.timesync.timesync_module_rt) << "\n"; + ss << R"( # Specify the module name for replaying)" << "\n"; + ss << R"( replay: )" << quoteString(config_.timesync.timesync_module_replay) << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# Service configuration)" << "\n"; + ss << R"(service:)" << "\n"; + ss << R"( # Support service protocol v0, eCAL 5.11 and older)" << "\n"; + ss << R"( protocol_v0: )" << config_.service.protocol_v0 << "\n"; + ss << R"( # Support service protocol v1, eCAL 5.12 and newer)" << "\n"; + ss << R"( protocol_v1: )" << config_.service.protocol_v1 << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# eCAL Application Configuration)" << "\n"; + ss << R"(application:)" << "\n"; + ss << R"( # Configuration for eCAL Sys)" << "\n"; + ss << R"( sys:)" << "\n"; + ss << R"( # Apps blacklist to be excluded when importing tasks from cloud)" << "\n"; + ss << R"( filter_excl: )" << quoteString(config_.application.sys.filter_excl) << "\n"; + ss << R"( # Process specific configuration)" << "\n"; + ss << R"( terminal:)" << "\n"; + ss << R"( # Linux only command for starting applications with an external terminal emulator. )" << "\n"; + ss << R"( # e.g. /usr/bin/x-terminal-emulator -e)" << "\n"; + ss << R"( # /usr/bin/gnome-terminal -x)" << "\n"; + ss << R"( # /usr/bin/xterm -e)" << "\n"; + ss << R"( # If empty, the command will be ignored.)" << "\n"; + ss << R"( emulator: )" << quoteString(config_.application.startup.terminal_emulator) << "\n"; + ss << R"()" << "\n"; + ss << R"()" << "\n"; + ss << R"(# Logging configuration)" << "\n"; + ss << R"(logging:)" << "\n"; + ss << R"( sinks:)" << "\n"; + ss << R"( # Console logging configuration)" << "\n"; + ss << R"( console:)" << "\n"; + ss << R"( # Enable console logging)" << "\n"; + ss << R"( enable: )" << config_.logging.sinks.console.enable << "\n"; + ss << R"( # Log level for console output)" << "\n"; + ss << R"( level: )" << logToArray(config_.logging.sinks.console.filter_log_con) << "\n"; + ss << R"( # File logging configuration)" << "\n"; + ss << R"( file:)" << "\n"; + ss << R"( # Enable file logging)" << "\n"; + ss << R"( enable: )" << config_.logging.sinks.file.enable << "\n"; + ss << R"( # Log level for file output)" << "\n"; + ss << R"( level: )" << logToArray(config_.logging.sinks.file.filter_log_file) << "\n"; + ss << R"( # Log file path)" << "\n"; + ss << R"( path: )" << quoteString(config_.logging.sinks.file.path) << "\n"; + ss << R"( # UDP logging configuration)" << "\n"; + ss << R"( udp:)" << "\n"; + ss << R"( # Enable UDP logging)" << "\n"; + ss << R"( enable: )" << config_.logging.sinks.udp.enable << "\n"; + ss << R"( # Log level for UDP output)" << "\n"; + ss << R"( level: )" << logToArray(config_.logging.sinks.udp.filter_log_udp) << "\n"; + ss << R"( # UDP)" << "\n"; + ss << R"( port: )" << config_.logging.sinks.udp.port << "\n"; + ss << R"()" << "\n"; + + return ss; + } + + bool dumpConfigToFile(const eCAL::Configuration& config_, const std::string& file_path_) + { + std::ofstream file(file_path_); + if (!file.is_open()) + { + return false; + } + + file << getConfigAsYamlSS(config_).str(); + file.close(); + return true; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/config/default_configuration.h b/ecal/core/src/config/default_configuration.h new file mode 100644 index 0000000000..2ff7b09b0e --- /dev/null +++ b/ecal/core/src/config/default_configuration.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ecal/ecal_config.h" + +#include +#include + +namespace eCAL +{ + namespace Config + { + std::stringstream getConfigAsYamlSS(const eCAL::Configuration& config_ = eCAL::GetConfiguration()); + + bool dumpConfigToFile(const eCAL::Configuration& config_ = eCAL::GetConfiguration(), const std::string& file_path_ = ECAL_DEFAULT_CFG); + } +} \ No newline at end of file diff --git a/ecal/core/src/config/ecal_cmd_parser.cpp b/ecal/core/src/config/ecal_cmd_parser.cpp index b2567657b4..0d9c26af50 100644 --- a/ecal/core/src/config/ecal_cmd_parser.cpp +++ b/ecal/core/src/config/ecal_cmd_parser.cpp @@ -1,9 +1,26 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + #include "config/ecal_cmd_parser.h" #include "ecal/ecal_defs.h" #include "ecal_def.h" -#include "ecal_utils/filesystem.h" -#include "util/getenvvar.h" #if ECAL_CORE_COMMAND_LINE #include "util/advanced_tclap_output.h" @@ -11,179 +28,6 @@ #include -// for cwd -#ifdef ECAL_OS_WINDOWS - #include - // to remove deprecated warning - #define getcwd _getcwd -#endif -#ifdef ECAL_OS_LINUX - #include -#endif - -namespace -{ - // copied and adapted from ecal_config_reader.cpp -#ifdef ECAL_OS_WINDOWS - const char path_separator('\\'); -#endif /* ECAL_OS_WINDOWS */ -#ifdef ECAL_OS_LINUX - const char path_separator('/'); -#endif /* ECAL_OS_LINUX */ - - bool setPathSep(std::string& file_path_) - { - if (!file_path_.empty()) - { - if (file_path_.back() != path_separator) - { - file_path_ += path_separator; - } - return true; - } - - return false; - } - - std::string eCALDataEnvPath() - { - std::string ecal_data_path = getEnvVar("ECAL_DATA"); - setPathSep(ecal_data_path); - return ecal_data_path; - } - - std::string cwdPath() - { - std::string cwd_path = { getcwd(nullptr, 0) }; - - if (cwd_path.empty()) - throw std::runtime_error("getcwd() : cannot read current working directory."); - - setPathSep(cwd_path); - return cwd_path; - } - - std::string eCALDataCMakePath() - { - std::string cmake_data_path; -#ifdef ECAL_OS_LINUX - const std::string ecal_install_config_dir(ECAL_INSTALL_CONFIG_DIR); - const std::string ecal_install_prefix(ECAL_INSTALL_PREFIX); - - if ((!ecal_install_config_dir.empty() && (ecal_install_config_dir[0] == path_separator)) - || ecal_install_prefix.empty()) - { - cmake_data_path = ecal_install_config_dir; - } - else if (!ecal_install_prefix.empty()) - { - cmake_data_path = ecal_install_prefix + path_separator + ecal_install_config_dir; - } - setPathSep(cmake_data_path); -#endif /* ECAL_OS_LINUX */ - return cmake_data_path; - } - - std::string eCALDataSystemPath() - { - std::string system_data_path; -#ifdef ECAL_OS_WINDOWS - system_data_path = getEnvVar("ProgramData"); - if(setPathSep(system_data_path)) - { - system_data_path += std::string("eCAL"); - setPathSep(system_data_path); - } -#endif /* ECAL_OS_WINDOWS */ - -#ifdef ECAL_OS_LINUX - system_data_path = "/etc/ecal"; - setPathSep(system_data_path); -#endif /* ECAL_OS_LINUX */ - return system_data_path; - } - - void appendFileNameToPathIfPathIsValid(std::string& path_, const std::string& file_name_) - { - if (!path_.empty()) - path_ += file_name_; - } - - void parseConfigKeysToMap(const std::vector& config_keys_, eCAL::Cli::ConfigKey2DMap& map_) - { - // each string has the format "section/key:value" - for (const auto& full_key : config_keys_) - { - auto sec_pos = full_key.find_last_of('/'); - if (sec_pos == std::string::npos) continue; - const std::string section = full_key.substr(0, sec_pos); - std::string key = full_key.substr(sec_pos+1); - - auto val_pos = key.find_first_of(':'); - if (val_pos == std::string::npos) continue; - const std::string value = key.substr(val_pos+1); - key = key.substr(0, val_pos); - - map_[section][key] = value; - } - } - - bool isValidConfigFilePath(const std::string& file_path_) - { - // check existence of user defined file - const EcalUtils::Filesystem::FileStatus ecal_ini_status(file_path_, EcalUtils::Filesystem::Current); - return ecal_ini_status.IsOk() && (ecal_ini_status.GetType() == EcalUtils::Filesystem::Type::RegularFile); - } - - std::string checkForValidConfigFilePath(const std::string& config_file_) - { - // differences to ecal_config_reader implementation are: - // 1. does not use the default ini file name, instead uses the specified file - // 2. searches relative to the executable path and takes it as highest priority - // 3. throws a runtime error, if it cannot find the specified file - - // ----------------------------------------------------------- - // precedence 1: relative path to executable - // ----------------------------------------------------------- - std::string cwd_directory_path = cwdPath(); - appendFileNameToPathIfPathIsValid(cwd_directory_path, config_file_); - - // ----------------------------------------------------------- - // precedence 2: ECAL_DATA variable (windows and linux) - // ----------------------------------------------------------- - std::string ecal_data_path = eCALDataEnvPath(); - appendFileNameToPathIfPathIsValid(ecal_data_path, config_file_); - - // ----------------------------------------------------------- - // precedence 3: cmake configured data paths (linux only) - // ----------------------------------------------------------- - std::string cmake_data_path = eCALDataCMakePath(); - appendFileNameToPathIfPathIsValid(cmake_data_path, config_file_); - - // ----------------------------------------------------------- - // precedence 4: system data path - // ----------------------------------------------------------- - std::string system_data_path = eCALDataSystemPath(); - appendFileNameToPathIfPathIsValid(system_data_path, config_file_); - - // Check for first directory which contains the ini file. - std::vector search_directories{ cwd_directory_path, ecal_data_path, cmake_data_path, system_data_path }; - - auto it = std::find_if(search_directories.begin(), search_directories.end(), isValidConfigFilePath); - // We should have encountered a valid path - if (it != search_directories.end()) - return (*it); - - // Check if user specified complete path, in case all other precedence paths exist - if (isValidConfigFilePath(config_file_)) - { - return std::string(config_file_); - } - - // If valid path is not encountered, throw error - throw std::runtime_error("[CMD Parser] Specified config file: \"" + config_file_ + "\" not found."); - } -} namespace eCAL { @@ -193,13 +37,13 @@ namespace eCAL : m_dump_config{false} {} - CmdParser::CmdParser(std::vector& arguments_) + CmdParser::CmdParser(const std::vector& arguments_) : CmdParser() { parseArguments(arguments_); } - void CmdParser::parseArguments(std::vector& arguments_) + void CmdParser::parseArguments(const std::vector& arguments_) { #if ECAL_CORE_COMMAND_LINE if (!arguments_.empty()) @@ -209,22 +53,21 @@ namespace eCAL // define command line arguments TCLAP::SwitchArg dump_config_arg ("", "ecal-dump-config", "Dump current configuration.", false); - TCLAP::ValueArg default_ini_file_arg("", "ecal-ini-file", "Load default configuration from that file.", false, ECAL_DEFAULT_CFG, "string"); - TCLAP::MultiArg set_config_key_arg ("", "ecal-set-config-key", "Overwrite a specific configuration key (ecal-set-config-key \"section/key:value\".", false, "string"); + TCLAP::ValueArg default_ini_file_arg("", "ecal-config-file", "Load default configuration from that file.", false, ECAL_DEFAULT_CFG, "string"); TCLAP::UnlabeledMultiArg dummy_arg("__dummy__", "Dummy", false, ""); // Dummy arg to eat all unrecognized arguments cmd.add(dump_config_arg); cmd.add(default_ini_file_arg); - cmd.add(set_config_key_arg); cmd.add(dummy_arg); CustomTclap::AdvancedTclapOutput advanced_tclap_output(&std::cout, 75); advanced_tclap_output.setArgumentHidden(&dummy_arg, true); cmd.setOutput(&advanced_tclap_output); + std::vector arguments = arguments_; // parse command line - cmd.parse(arguments_); + cmd.parse(arguments); // set globals if (dump_config_arg.isSet()) @@ -233,22 +76,13 @@ namespace eCAL } if (default_ini_file_arg.isSet()) { - m_user_ini = checkForValidConfigFilePath(default_ini_file_arg.getValue()); - } - if (set_config_key_arg.isSet()) - { - m_config_keys = set_config_key_arg.getValue(); - parseConfigKeysToMap(set_config_key_arg.getValue(), m_config_key_map); + m_user_ini = default_ini_file_arg.getValue(); } } #endif - m_task_parameter = arguments_; } - bool CmdParser::getDumpConfig() const { return m_dump_config; }; - std::vector& CmdParser::getConfigKeys() { return m_config_keys; }; - std::vector& CmdParser::getTaskParameter() { return m_task_parameter; }; - std::string& CmdParser::getUserIni() { return m_user_ini; }; - Cli::ConfigKey2DMap& CmdParser::getConfigKeysMap() { return m_config_key_map; }; + bool CmdParser::getDumpConfig() const { return m_dump_config; } + std::string& CmdParser::getUserIni() { return m_user_ini; } } } diff --git a/ecal/core/src/config/ecal_cmd_parser.h b/ecal/core/src/config/ecal_cmd_parser.h index dd1ed957bf..bc0dec7c86 100644 --- a/ecal/core/src/config/ecal_cmd_parser.h +++ b/ecal/core/src/config/ecal_cmd_parser.h @@ -44,22 +44,16 @@ namespace eCAL class CmdParser { public: - CmdParser(std::vector& arguments_); + CmdParser(const std::vector& arguments_); CmdParser(); - void parseArguments(std::vector& arguments_); + void parseArguments(const std::vector& arguments_); bool getDumpConfig() const; - std::vector& getConfigKeys(); - std::vector& getTaskParameter(); std::string& getUserIni(); - Cli::ConfigKey2DMap& getConfigKeysMap(); private: - std::vector m_config_keys; - Cli::ConfigKey2DMap m_config_key_map; bool m_dump_config; - std::vector m_task_parameter; std::string m_user_ini; }; } diff --git a/ecal/core/src/config/ecal_config.cpp b/ecal/core/src/config/ecal_config.cpp index 14f23cf803..536d968d5b 100644 --- a/ecal/core/src/config/ecal_config.cpp +++ b/ecal/core/src/config/ecal_config.cpp @@ -21,65 +21,6 @@ #include #include -#include "ecal_config_reader_hlp.h" -#include "ecal_def.h" - - -namespace -{ - void tokenize(const std::string& str, std::vector& tokens, - const std::string& delimiters = " ", bool trimEmpty = false) - { - std::string::size_type pos, lastPos = 0; - - for (;;) - { - pos = str.find_first_of(delimiters, lastPos); - if (pos == std::string::npos) - { - pos = str.length(); - if (pos != lastPos || !trimEmpty) - { - tokens.emplace_back(std::string(str.data() + lastPos, pos - lastPos)); - } - break; - } - else - { - if (pos != lastPos || !trimEmpty) - { - tokens.emplace_back(std::string(str.data() + lastPos, pos - lastPos)); - } - } - lastPos = pos + 1; - } - } - - - eCAL_Logging_Filter ParseLogLevel(const std::string& filter_) - { - // tokenize it - std::vector token_filter_; - tokenize(filter_, token_filter_, " ,;"); - // create excluding filter list - char filter_mask = log_level_none; - for (auto& it : token_filter_) - { - if (it == "all") filter_mask |= log_level_all; - if (it == "info") filter_mask |= log_level_info; - if (it == "warning") filter_mask |= log_level_warning; - if (it == "error") filter_mask |= log_level_error; - if (it == "fatal") filter_mask |= log_level_fatal; - if (it == "debug1") filter_mask |= log_level_debug1; - if (it == "debug2") filter_mask |= log_level_debug2; - if (it == "debug3") filter_mask |= log_level_debug3; - if (it == "debug4") filter_mask |= log_level_debug4; - } - return(filter_mask); - } - -} - namespace eCAL { @@ -89,38 +30,39 @@ namespace eCAL // common ///////////////////////////////////// - ECAL_API std::string GetLoadedEcalIniPath () { return GetConfiguration().GetIniFilePath(); } - ECAL_API int GetRegistrationTimeoutMs () { return GetConfiguration().registration.getTimeoutMS(); } - ECAL_API int GetRegistrationRefreshMs () { return GetConfiguration().registration.getRefreshMS(); } + ECAL_API std::string GetLoadedEcalIniPath () { return GetConfiguration().GetYamlFilePath(); } + ECAL_API int GetRegistrationTimeoutMs () { return GetConfiguration().registration.registration_timeout; } + ECAL_API int GetRegistrationRefreshMs () { return GetConfiguration().registration.registration_refresh; } ///////////////////////////////////// // network ///////////////////////////////////// - ECAL_API bool IsNetworkEnabled () { return GetConfiguration().transport_layer.network_enabled; } + ECAL_API bool IsNetworkEnabled () { return GetConfiguration().registration.network_enabled; } + ECAL_API bool IsShmRegistrationEnabled () { return GetConfiguration().registration.layer.shm.enable; } - ECAL_API Types::UdpConfigVersion GetUdpMulticastConfigVersion () { return GetConfiguration().transport_layer.mc_options.config_version; } + ECAL_API Types::UdpConfigVersion GetUdpMulticastConfigVersion () { return GetConfiguration().transport_layer.udp.config_version; } - ECAL_API std::string GetUdpMulticastGroup () { return GetConfiguration().transport_layer.mc_options.group; } - ECAL_API std::string GetUdpMulticastMask () { return GetConfiguration().transport_layer.mc_options.mask; } - ECAL_API int GetUdpMulticastPort () { return GetConfiguration().transport_layer.mc_options.port; } - ECAL_API int GetUdpMulticastTtl () { return GetConfiguration().transport_layer.mc_options.ttl; } + ECAL_API std::string GetUdpMulticastGroup () { return GetConfiguration().transport_layer.udp.network.group; } + ECAL_API std::string GetUdpMulticastMask () { return GetConfiguration().transport_layer.udp.mask; } + ECAL_API int GetUdpMulticastPort () { return GetConfiguration().transport_layer.udp.port; } + ECAL_API int GetUdpMulticastTtl () { return GetConfiguration().transport_layer.udp.network.ttl; } - ECAL_API int GetUdpMulticastSndBufSizeBytes () { return GetConfiguration().transport_layer.mc_options.sndbuf; } - ECAL_API int GetUdpMulticastRcvBufSizeBytes () { return GetConfiguration().transport_layer.mc_options.recbuf; } - ECAL_API bool IsUdpMulticastJoinAllIfEnabled () { return GetConfiguration().transport_layer.mc_options.join_all_interfaces; } + ECAL_API int GetUdpMulticastSndBufSizeBytes () { return GetConfiguration().transport_layer.udp.send_buffer; } + ECAL_API int GetUdpMulticastRcvBufSizeBytes () { return GetConfiguration().transport_layer.udp.receive_buffer; } + ECAL_API bool IsUdpMulticastJoinAllIfEnabled () { return GetConfiguration().transport_layer.udp.join_all_interfaces; } - ECAL_API bool IsUdpMulticastRecEnabled () { return GetConfiguration().subscriber.udp.enable; } - ECAL_API bool IsShmRecEnabled () { return GetConfiguration().subscriber.shm.enable; } - ECAL_API bool IsTcpRecEnabled () { return GetConfiguration().subscriber.tcp.enable; } + ECAL_API bool IsUdpMulticastRecEnabled () { return GetConfiguration().subscriber.layer.udp.enable; } + ECAL_API bool IsShmRecEnabled () { return GetConfiguration().subscriber.layer.shm.enable; } + ECAL_API bool IsTcpRecEnabled () { return GetConfiguration().subscriber.layer.tcp.enable; } - ECAL_API bool IsNpcapEnabled () { return GetConfiguration().transport_layer.mc_options.npcap_enabled; } + ECAL_API bool IsNpcapEnabled () { return GetConfiguration().transport_layer.udp.npcap_enabled; } - ECAL_API int GetTcpPubsubReaderThreadpoolSize () { return static_cast(GetConfiguration().transport_layer.tcp_options.num_executor_reader); } - ECAL_API int GetTcpPubsubWriterThreadpoolSize () { return static_cast(GetConfiguration().transport_layer.tcp_options.num_executor_writer); } - ECAL_API int GetTcpPubsubMaxReconnectionAttemps () { return static_cast(GetConfiguration().transport_layer.tcp_options.max_reconnections); } + ECAL_API size_t GetTcpPubsubReaderThreadpoolSize () { return GetConfiguration().transport_layer.tcp.number_executor_reader;} + ECAL_API size_t GetTcpPubsubWriterThreadpoolSize () { return GetConfiguration().transport_layer.tcp.number_executor_writer;} + ECAL_API size_t GetTcpPubsubMaxReconnectionAttemps () { return GetConfiguration().transport_layer.tcp.max_reconnections;} - ECAL_API std::string GetHostGroupName () { return GetConfiguration().transport_layer.shm_options.host_group_name; } + ECAL_API std::string GetHostGroupName () { return GetConfiguration().registration.host_group_name; } ///////////////////////////////////// // time @@ -139,12 +81,11 @@ namespace eCAL // monitoring ///////////////////////////////////// - ECAL_API int GetMonitoringTimeoutMs () { return GetConfiguration().monitoring.monitoring_timeout; } ECAL_API std::string GetMonitoringFilterExcludeList () { return GetConfiguration().monitoring.filter_excl; } ECAL_API std::string GetMonitoringFilterIncludeList () { return GetConfiguration().monitoring.filter_incl; } - ECAL_API eCAL_Logging_Filter GetConsoleLogFilter () { return GetConfiguration().logging.filter_log_con; } - ECAL_API eCAL_Logging_Filter GetFileLogFilter () { return GetConfiguration().logging.filter_log_file; } - ECAL_API eCAL_Logging_Filter GetUdpLogFilter () { return GetConfiguration().logging.filter_log_udp; } + ECAL_API eCAL_Logging_Filter GetConsoleLogFilter () { return GetConfiguration().logging.sinks.console.filter_log_con; } + ECAL_API eCAL_Logging_Filter GetFileLogFilter () { return GetConfiguration().logging.sinks.file.filter_log_file; } + ECAL_API eCAL_Logging_Filter GetUdpLogFilter () { return GetConfiguration().logging.sinks.udp.filter_log_udp; } ///////////////////////////////////// // sys @@ -155,19 +96,8 @@ namespace eCAL ///////////////////////////////////// // publisher ///////////////////////////////////// - - ECAL_API bool GetPublisherUdpMulticastMode () { return GetConfiguration().publisher.udp.enable; } - ECAL_API bool GetPublisherShmMode () { return GetConfiguration().publisher.shm.enable; } - ECAL_API bool GetPublisherTcpMode () { return GetConfiguration().publisher.tcp.enable; } - - ECAL_API size_t GetMemfileMinsizeBytes () { return GetConfiguration().transport_layer.shm_options.memfile_minsize; } - ECAL_API size_t GetMemfileOverprovisioningPercentage () { return GetConfiguration().transport_layer.shm_options.memfile_reserve; } - ECAL_API int GetMemfileAckTimeoutMs () { return GetConfiguration().transport_layer.shm_options.memfile_ack_timeout; } - ECAL_API bool IsMemfileZerocopyEnabled () { return GetConfiguration().transport_layer.shm_options.memfile_zero_copy; } - ECAL_API size_t GetMemfileBufferCount () { return GetConfiguration().transport_layer.shm_options.memfile_buffer_count; } - - ECAL_API bool IsTopicTypeSharingEnabled () { return GetConfiguration().registration.share_ttype; } - ECAL_API bool IsTopicDescriptionSharingEnabled () { return GetConfiguration().registration.share_tdesc; } + ECAL_API bool IsTopicTypeSharingEnabled () { return GetConfiguration().publisher.share_topic_type; } + ECAL_API bool IsTopicDescriptionSharingEnabled () { return GetConfiguration().publisher.share_topic_description; } ///////////////////////////////////// // service @@ -181,11 +111,9 @@ namespace eCAL namespace Experimental { - ECAL_API bool IsShmMonitoringEnabled () { return (GetConfiguration().monitoring.monitoring_mode & Monitoring::Types::Mode::shm_monitoring) != 0; } - ECAL_API bool IsNetworkMonitoringDisabled () { return !GetConfiguration().monitoring.network_monitoring; } - ECAL_API size_t GetShmMonitoringQueueSize () { return GetConfiguration().monitoring.shm_options.shm_monitoring_queue_size; } - ECAL_API std::string GetShmMonitoringDomain () { return GetConfiguration().monitoring.shm_options.shm_monitoring_domain;} - ECAL_API bool GetDropOutOfOrderMessages () { return GetConfiguration().transport_layer.drop_out_of_order_messages; } + ECAL_API size_t GetShmMonitoringQueueSize () { return GetConfiguration().registration.layer.shm.queue_size; } + ECAL_API std::string GetShmMonitoringDomain () { return GetConfiguration().registration.layer.shm.domain;} + ECAL_API bool GetDropOutOfOrderMessages () { return GetConfiguration().subscriber.drop_out_of_order_messages; } } } } diff --git a/ecal/core/src/config/ecal_config_initializer.cpp b/ecal/core/src/config/ecal_config_initializer.cpp index 2441eccd40..3a78dc4c6a 100644 --- a/ecal/core/src/config/ecal_config_initializer.cpp +++ b/ecal/core/src/config/ecal_config_initializer.cpp @@ -25,247 +25,430 @@ #include "ecal_global_accessors.h" #include "ecal_def.h" -#include "config/ecal_config_reader.h" #include "ecal/ecal_process.h" #include "config/ecal_cmd_parser.h" +#ifdef ECAL_CORE_CONFIGURATION + #include "configuration_reader.h" + #include "configuration_to_yaml.h" +#endif + +// for cwd +#ifdef ECAL_OS_WINDOWS + #include + // to remove deprecated warning + #define getcwd _getcwd +#endif +#ifdef ECAL_OS_LINUX + #include + #include + #include + #include +#endif + +#include "ecal_utils/filesystem.h" +#include "util/getenvvar.h" +#include "ecal_utils/ecal_utils.h" +#include "ecal/ecal_log.h" + #include +#include + +namespace +{ + // copied and adapted from ecal_config_reader.cpp +#ifdef ECAL_OS_WINDOWS + const char path_separator('\\'); +#endif /* ECAL_OS_WINDOWS */ +#ifdef ECAL_OS_LINUX + const char path_separator('/'); +#endif /* ECAL_OS_LINUX */ + + bool setPathSep(std::string& file_path_) + { + if (!file_path_.empty()) + { + if (file_path_.back() != path_separator) + { + file_path_ += path_separator; + } + return true; + } + + return false; + } + + std::string eCALDataEnvPath() + { + std::string ecal_data_path = getEnvVar("ECAL_DATA"); + setPathSep(ecal_data_path); + return ecal_data_path; + } + + std::string cwdPath() + { + std::string cwd_path = { getcwd(nullptr, 0) }; + + setPathSep(cwd_path); + return cwd_path; + } + + std::string eCALDataCMakePath() + { + std::string cmake_data_path; +#ifdef ECAL_OS_LINUX + const std::string ecal_install_config_dir(ECAL_INSTALL_CONFIG_DIR); + const std::string ecal_install_prefix(ECAL_INSTALL_PREFIX); + + if ((!ecal_install_config_dir.empty() && (ecal_install_config_dir[0] == path_separator)) + || ecal_install_prefix.empty()) + { + cmake_data_path = ecal_install_config_dir; + } + else if (!ecal_install_prefix.empty()) + { + cmake_data_path = ecal_install_prefix + path_separator + ecal_install_config_dir; + } + setPathSep(cmake_data_path); +#endif /* ECAL_OS_LINUX */ + return cmake_data_path; + } + + std::string eCALDataSystemPath() + { + std::string system_data_path; +#ifdef ECAL_OS_WINDOWS + system_data_path = getEnvVar("ProgramData"); + if(setPathSep(system_data_path)) + { + system_data_path += std::string("eCAL"); + setPathSep(system_data_path); + } +#endif /* ECAL_OS_WINDOWS */ + +#ifdef ECAL_OS_LINUX + system_data_path = "/etc/ecal"; + setPathSep(system_data_path); +#endif /* ECAL_OS_LINUX */ + return system_data_path; + } + + bool isValidConfigFilePath(const std::string& path_, const std::string& file_name_) + { + const std::string file_path = path_ + file_name_; + const EcalUtils::Filesystem::FileStatus ecal_ini_status(file_path, EcalUtils::Filesystem::Current); + return ecal_ini_status.IsOk() && (ecal_ini_status.GetType() == EcalUtils::Filesystem::Type::RegularFile); + } + + std::string findValidConfigPath(std::vector paths_, const std::string& file_name_) + { + auto it = std::find_if(paths_.begin(), paths_.end(), [&file_name_](const std::string& path_) + { + return isValidConfigFilePath(path_, file_name_); + }); + + // We should have encountered a valid path + if (it != paths_.end()) + return (*it); + + // If valid path is not encountered, defaults should be used + return std::string(""); + } + + std::vector getEcalDefaultPaths() + { + std::vector ecal_default_paths; + // ----------------------------------------------------------- + // precedence 1: ECAL_DATA variable (windows and linux) + // ----------------------------------------------------------- + ecal_default_paths.emplace_back(eCALDataEnvPath()); + + // ----------------------------------------------------------- + // precedence 2: cmake configured data paths (linux only) + // ----------------------------------------------------------- + ecal_default_paths.emplace_back(eCALDataCMakePath()); + + // ----------------------------------------------------------- + // precedence 3: system data path + // ----------------------------------------------------------- + ecal_default_paths.emplace_back(eCALDataSystemPath()); + return ecal_default_paths; + } -constexpr const char* COMMON = "common"; -constexpr const char* MONITORING = "monitoring"; -constexpr const char* NETWORK = "network"; -constexpr const char* EXPERIMENTAL = "experimental"; -constexpr const char* PUBLISHER = "publisher"; -constexpr const char* SYS = "sys"; -constexpr const char* TIME = "time"; -constexpr const char* SERVICE = "service"; -constexpr const char* PROCESS = "process"; - -namespace { - void tokenize(const std::string& str, std::vector& tokens, - const std::string& delimiters = " ", bool trimEmpty = false) + std::string checkForValidConfigFilePath(const std::string& config_file_) { - std::string::size_type pos = 0; - std::string::size_type lastPos = 0; + // ----------------------------------------------------------- + // precedence 0: relative path to executable + // ----------------------------------------------------------- + const std::string cwd_directory_path = cwdPath(); + + std::vector ecal_default_paths = getEcalDefaultPaths(); + ecal_default_paths.emplace(ecal_default_paths.begin(), cwd_directory_path); + + const std::string found_path = findValidConfigPath(ecal_default_paths, config_file_); + + // check in case user provided whole path + if (found_path.empty()) + { + return isValidConfigFilePath(config_file_, "") ? config_file_ : found_path; + } - for (;;) + return found_path + config_file_; + } + + std::vector ConvertArgcArgvToVector(int argc_, char** argv_) + { + std::vector arguments; + if (argc_ > 0 && argv_ != nullptr) { - pos = str.find_first_of(delimiters, lastPos); - if (pos == std::string::npos) + for (size_t i = 0; i < static_cast(argc_); ++i) { - pos = str.length(); - if (pos != lastPos || !trimEmpty) + if (argv_[i] != nullptr) { - tokens.emplace_back(str.data() + lastPos, pos - lastPos); + arguments.emplace_back(argv_[i]); } - break; + } + } + return arguments; + } +} + +namespace eCAL +{ + void Configuration::InitFromFile(const std::string& yaml_path_) + { + const std::string yaml_path = checkForValidConfigFilePath(yaml_path_); + if (!yaml_path.empty()) + { +#ifdef ECAL_CORE_CONFIGURATION + eCAL::Config::YamlFileToConfig(yaml_path, *this); + ecal_yaml_file_path = yaml_path; +#else + eCAL::Logging::Log(log_level_warning, "Yaml file found at \"" + yaml_path + "\" but eCAL core configuration is not enabled."); +#endif } else { - if (pos != lastPos || !trimEmpty) - { - tokens.emplace_back(str.data() + lastPos, pos - lastPos); - } + eCAL::Logging::Log(log_level_warning, "Specified yaml configuration path not valid:\"" + yaml_path_ + "\". Using default configuration."); } - lastPos = pos + 1; } - } - eCAL_Logging_Filter ParseLogLevel(const std::string& filter_) - { - // tokenize it - std::vector token_filter_; - tokenize(filter_, token_filter_, " ,;"); - // create excluding filter list - char filter_mask = log_level_none; - for (auto& it : token_filter_) + Configuration::Configuration(int argc_ , char **argv_) + : Configuration(ConvertArgcArgvToVector(argc_, argv_)) { - if (it == "all") filter_mask |= log_level_all; - if (it == "info") filter_mask |= log_level_info; - if (it == "warning") filter_mask |= log_level_warning; - if (it == "error") filter_mask |= log_level_error; - if (it == "fatal") filter_mask |= log_level_fatal; - if (it == "debug1") filter_mask |= log_level_debug1; - if (it == "debug2") filter_mask |= log_level_debug2; - if (it == "debug3") filter_mask |= log_level_debug3; - if (it == "debug4") filter_mask |= log_level_debug4; } - return(filter_mask); - }; -} -namespace eCAL -{ - void Configuration::InitConfig(std::string ini_path_ /*= std::string("")*/) + Configuration::Configuration(const std::vector& args_) + : Configuration() { - CConfig iniConfig; - if (!command_line_arguments.config_keys.empty()) - iniConfig.OverwriteKeys(command_line_arguments.config_keys); + Config::CmdParser parser(args_); + + command_line_arguments.user_yaml = parser.getUserIni(); + command_line_arguments.dump_config = parser.getDumpConfig(); - if (!ini_path_.empty()) + if (!command_line_arguments.user_yaml.empty()) { - iniConfig.AddFile(ini_path_); - ecal_ini_file_path = ini_path_; + InitFromFile(command_line_arguments.user_yaml); } + else + { + InitFromConfig(); + } + } - // transport layer options - auto& transportLayerOptions = transport_layer; - transportLayerOptions.network_enabled = iniConfig.get(NETWORK, "network_enabled", NET_ENABLED); - transportLayerOptions.drop_out_of_order_messages = iniConfig.get(EXPERIMENTAL, "drop_out_of_order_messages", EXP_DROP_OUT_OF_ORDER_MESSAGES); + void Configuration::InitFromConfig() + { + InitFromFile(g_default_ini_file); + } - auto& multicastOptions = transportLayerOptions.mc_options; - - const std::string udp_config_version_string = iniConfig.get(NETWORK, "multicast_config_version", "v1"); - if (udp_config_version_string == "v1") - multicastOptions.config_version = Types::UdpConfigVersion::V1; - if (udp_config_version_string == "v2") - multicastOptions.config_version = Types::UdpConfigVersion::V2; - - multicastOptions.group = iniConfig.get(NETWORK, "multicast_group", NET_UDP_MULTICAST_GROUP); - multicastOptions.mask = iniConfig.get(NETWORK, "multicast_mask", NET_UDP_MULTICAST_MASK); - multicastOptions.port = iniConfig.get(NETWORK, "multicast_port", NET_UDP_MULTICAST_PORT); - multicastOptions.ttl = iniConfig.get(NETWORK, "multicast_ttl", NET_UDP_MULTICAST_TTL); - multicastOptions.recbuf = iniConfig.get(NETWORK, "multicast_rcvbuf", NET_UDP_MULTICAST_RCVBUF); - multicastOptions.sndbuf = iniConfig.get(NETWORK, "multicast_sndbuf", NET_UDP_MULTICAST_SNDBUF); - multicastOptions.join_all_interfaces = iniConfig.get(NETWORK, "multicast_join_all_if", NET_UDP_MULTICAST_JOIN_ALL_IF_ENABLED); - multicastOptions.npcap_enabled = iniConfig.get(NETWORK, "npcap_enabled", NET_NPCAP_ENABLED); - - auto& tcpPubSubOptions = transportLayerOptions.tcp_options; - tcpPubSubOptions.num_executor_reader = iniConfig.get(NETWORK, "tcp_pubsup_num_executor_reader", NET_TCP_PUBSUB_NUM_EXECUTOR_READER); - tcpPubSubOptions.num_executor_writer = iniConfig.get(NETWORK, "tcp_pubsup_num_executor_writer", NET_TCP_PUBSUB_NUM_EXECUTOR_WRITER); - tcpPubSubOptions.max_reconnections = iniConfig.get(NETWORK, "tcp_pubsup_max_reconnections", NET_TCP_PUBSUB_MAX_RECONNECTIONS); - - auto& shmOptions = transportLayerOptions.shm_options; - shmOptions.host_group_name = iniConfig.get(NETWORK, "host_group_name", NET_HOST_GROUP_NAME); - shmOptions.memfile_minsize = iniConfig.get(PUBLISHER, "memfile_minsize", PUB_MEMFILE_MINSIZE); - shmOptions.memfile_reserve = iniConfig.get(PUBLISHER, "memfile_reserve", PUB_MEMFILE_RESERVE); - shmOptions.memfile_ack_timeout = iniConfig.get(PUBLISHER, "memfile_ack_timeout", PUB_MEMFILE_ACK_TO); - shmOptions.memfile_buffer_count = iniConfig.get(PUBLISHER, "memfile_buffer_count", PUB_MEMFILE_BUF_COUNT); - shmOptions.drop_out_of_order_messages = iniConfig.get(EXPERIMENTAL, "drop_out_of_order_messages", EXP_DROP_OUT_OF_ORDER_MESSAGES); - shmOptions.memfile_zero_copy = iniConfig.get(PUBLISHER, "memfile_zero_copy", PUB_MEMFILE_ZERO_COPY); - - // registration options - auto registrationTimeout = iniConfig.get(COMMON, "registration_timeout", CMN_REGISTRATION_TO); - auto registrationRefresh = iniConfig.get(COMMON, "registration_refresh", CMN_REGISTRATION_REFRESH); - registration = Registration::Configuration(registrationTimeout, registrationRefresh); - auto& registrationOptions = registration; - registrationOptions.share_tdesc = iniConfig.get(PUBLISHER, "share_tdesc", PUB_SHARE_TDESC); - registrationOptions.share_ttype = iniConfig.get(PUBLISHER, "share_ttype", PUB_SHARE_TTYPE); - - // monitoring options - auto& monitoringOptions = monitoring; - auto monitoringMode = iniConfig.get(EXPERIMENTAL, "shm_monitoring_enabled", false) ? Monitoring::Types::Mode::shm_monitoring : Monitoring::Types::Mode::none; - monitoringOptions.monitoring_mode = static_cast(monitoringMode); - monitoringOptions.monitoring_timeout = iniConfig.get(MONITORING, "timeout", MON_TIMEOUT);; - monitoringOptions.network_monitoring = iniConfig.get(EXPERIMENTAL, "network_monitoring", EXP_NETWORK_MONITORING_ENABLED); - monitoringOptions.filter_excl = iniConfig.get(MONITORING, "filter_excl", MON_FILTER_EXCL); - monitoringOptions.filter_incl = iniConfig.get(MONITORING, "filter_incl", MON_FILTER_INCL); - - // auto& udpMonitoringOptions = monitoringOptions.udp_options; - // TODO: Nothing here yet - - auto& shmMonitoringOptions = monitoringOptions.shm_options; - shmMonitoringOptions.shm_monitoring_domain = iniConfig.get(EXPERIMENTAL, "shm_monitoring_domain", EXP_SHM_MONITORING_DOMAIN); - shmMonitoringOptions.shm_monitoring_queue_size = iniConfig.get(EXPERIMENTAL, "shm_monitoring_queue_size", EXP_SHM_MONITORING_QUEUE_SIZE); - - // subscriber options - auto& subscriberOptions = subscriber; - subscriberOptions.shm.enable = iniConfig.get(NETWORK, "shm_rec_enabled", NET_SHM_REC_ENABLED) != 0; - subscriberOptions.tcp.enable = iniConfig.get(NETWORK, "tcp_rec_enabled", NET_TCP_REC_ENABLED) != 0; - subscriberOptions.udp.enable = iniConfig.get(NETWORK, "udp_mc_rec_enabled", NET_UDP_MC_REC_ENABLED) != 0; - - // publisher options - auto& publisherOptions = publisher; - publisherOptions.shm.enable = iniConfig.get(PUBLISHER, "use_shm", static_cast(PUB_USE_SHM)) != 0; - publisherOptions.shm.zero_copy_mode = iniConfig.get(PUBLISHER, "memfile_zero_copy", PUB_MEMFILE_ZERO_COPY); - publisherOptions.shm.acknowledge_timeout_ms = iniConfig.get(PUBLISHER, "memfile_ack_timeout", PUB_MEMFILE_ACK_TO); - publisherOptions.shm.memfile_min_size_bytes = iniConfig.get(PUBLISHER, "memfile_minsize", PUB_MEMFILE_MINSIZE); - publisherOptions.shm.memfile_reserve_percent = iniConfig.get(PUBLISHER, "memfile_reserve", PUB_MEMFILE_RESERVE); - publisherOptions.shm.memfile_buffer_count = iniConfig.get(PUBLISHER, "memfile_buffer_count", PUB_MEMFILE_BUF_COUNT); - - publisherOptions.udp.enable = iniConfig.get(PUBLISHER, "use_udp_mc", static_cast(PUB_USE_UDP_MC)) != 0; - // TODO PG: Add here when its available in config file - publisherOptions.udp.loopback = false; - publisherOptions.udp.sndbuf_size_bytes = iniConfig.get(NETWORK, "multicast_sndbuf", NET_UDP_MULTICAST_SNDBUF); - - publisherOptions.share_topic_description = iniConfig.get(PUBLISHER, "share_tdesc", PUB_SHARE_TDESC); - publisherOptions.share_topic_type = iniConfig.get(PUBLISHER, "share_ttype", PUB_SHARE_TTYPE); + Configuration::Configuration() + { + eCAL::InitGlobals(); + } - publisherOptions.tcp.enable = iniConfig.get(PUBLISHER, "use_tcp", static_cast(PUB_USE_TCP)) != 0; + std::string Configuration::GetYamlFilePath() + { + return ecal_yaml_file_path; + } - // timesync options - auto& timesyncOptions = timesync; - timesyncOptions.timesync_module_rt = iniConfig.get(TIME, "timesync_module_rt", TIME_SYNC_MODULE); - timesyncOptions.timesync_module_replay = iniConfig.get(TIME, "timesync_module_replay", TIME_SYNC_MOD_REPLAY); + Configuration& GetConfiguration() + { + return g_ecal_configuration; + } - // service options - auto& serviceOptions = service; - serviceOptions.protocol_v0 = iniConfig.get(SERVICE, "protocol_v0", SERVICE_PROTOCOL_V0); - serviceOptions.protocol_v1 = iniConfig.get(SERVICE, "protocol_v1", SERVICE_PROTOCOL_V1); + Registration::Configuration& GetRegistrationConfiguration() + { + return GetConfiguration().registration; + } - // sys options - auto& sysConfig = application.sys; - sysConfig.filter_excl = iniConfig.get(SYS, "filter_excl", SYS_FILTER_EXCL); + Monitoring::Configuration& GetMonitoringConfiguration() + { + return GetConfiguration().monitoring; + } - // process options - auto& startupConfig = application.startup; - startupConfig.terminal_emulator = iniConfig.get(PROCESS, "terminal_emulator", PROCESS_TERMINAL_EMULATOR); + Logging::Configuration& GetLoggingConfiguration() + { + return GetConfiguration().logging; + } - auto& loggingOptions = logging; - // needs to be adapted when switching from simpleini - loggingOptions.filter_log_con = ParseLogLevel(iniConfig.get(MONITORING, "filter_log_con", "info,warning,error,fatal")); - loggingOptions.filter_log_file = ParseLogLevel(iniConfig.get(MONITORING, "filter_log_file", "")); - loggingOptions.filter_log_udp = ParseLogLevel(iniConfig.get(MONITORING, "filter_log_udp", "info,warning,error,fatal")); - }; + Subscriber::Configuration& GetSubscriberConfiguration() + { + return GetConfiguration().subscriber; + } - Configuration::Configuration(int argc_ , char **argv_) + Publisher::Configuration& GetPublisherConfiguration() { - std::vector arguments; - if (argc_ > 0 && argv_ != nullptr) - { - for (size_t i = 0; i < static_cast(argc_); ++i) - if (argv_[i] != nullptr) - arguments.emplace_back(argv_[i]); - } - Init(arguments); + return GetConfiguration().publisher; } - Configuration::Configuration(std::vector& args_) - { - Init(args_); + Time::Configuration& GetTimesyncConfiguration() + { + return GetConfiguration().timesync; } - void Configuration::Init(std::vector& arguments_) + Service::Configuration& GetServiceConfiguration() { - Config::CmdParser parser(arguments_); - - command_line_arguments.config_keys = parser.getConfigKeys(); - command_line_arguments.specified_config = parser.getUserIni(); - command_line_arguments.dump_config = parser.getDumpConfig(); - command_line_arguments.config_keys_map = parser.getConfigKeysMap(); + return GetConfiguration().service; + } - InitConfig(command_line_arguments.specified_config); + Application::Configuration& GetApplicationConfiguration() + { + return GetConfiguration().application; } +} + + +// Utils definitions from former ecal_config_reader.cpp +namespace +{ + bool fileexists(const std::string& fname_) + { + const std::ifstream infile(fname_); + return infile.good(); + } - void Configuration::InitConfigWithDefaultIni() + bool direxists(const std::string& path_) + { + const EcalUtils::Filesystem::FileStatus status(path_, EcalUtils::Filesystem::Current); + return (status.IsOk() && (status.GetType() == EcalUtils::Filesystem::Type::Dir)); + } + + void createdir(const std::string& path_) + { + EcalUtils::Filesystem::MkDir(path_, EcalUtils::Filesystem::Current); + } + +} + +namespace eCAL +{ + namespace Util + { + ECAL_API std::string GeteCALConfigPath() { - InitConfig(g_default_ini_file); + // Check for first directory which contains the ini file. + const std::vector search_directories = getEcalDefaultPaths(); + + return findValidConfigPath(search_directories, ECAL_DEFAULT_CFG); } - Configuration::Configuration() - : Configuration(0, nullptr) + ECAL_API std::string GeteCALHomePath() { - InitConfig(); + std::string home_path; + +#ifdef ECAL_OS_WINDOWS + // check ECAL_HOME + home_path = getEnvVar("ECAL_HOME"); + if (!home_path.empty()) + { + if (*home_path.rbegin() != path_separator) home_path += path_separator; + } + if (!std::string(ECAL_HOME_PATH_WINDOWS).empty()) //-V815 + { + home_path += path_separator; + home_path += ECAL_HOME_PATH_WINDOWS; + } +#endif /* ECAL_OS_WINDOWS */ + +#ifdef ECAL_OS_LINUX + const char *hdir = nullptr; + hdir = getenv("HOME"); + if (hdir == nullptr) { + hdir = getpwuid(getuid())->pw_dir; + } + home_path += hdir; + if (!std::string(ECAL_HOME_PATH_LINUX).empty()) + { + home_path += "/"; + home_path += ECAL_HOME_PATH_LINUX; + } +#endif /* ECAL_OS_LINUX */ + + // create if not exists + if (!direxists(home_path)) + { + createdir(home_path); + } + + home_path += path_separator; + return(home_path); } - std::string Configuration::GetIniFilePath() + ECAL_API std::string GeteCALUserSettingsPath() { - return ecal_ini_file_path; + std::string settings_path; +#ifdef ECAL_OS_WINDOWS + settings_path = GeteCALConfigPath(); +#endif /* ECAL_OS_WINDOWS */ + +#ifdef ECAL_OS_LINUX + settings_path = GeteCALHomePath(); +#endif /* ECAL_OS_LINUX */ + settings_path += std::string(ECAL_SETTINGS_PATH); + + if (!direxists(settings_path)) + { + createdir(settings_path); + } + + settings_path += path_separator; + return(settings_path); } - Configuration& GetConfiguration() + ECAL_API std::string GeteCALLogPath() { - return g_ecal_configuration; - }; -} \ No newline at end of file + std::string log_path; +#ifdef ECAL_OS_WINDOWS + log_path = GeteCALConfigPath(); +#endif /* ECAL_OS_WINDOWS */ + +#ifdef ECAL_OS_LINUX + log_path = GeteCALHomePath(); +#endif /* ECAL_OS_LINUX */ + + log_path += std::string(ECAL_LOG_PATH); + + if (!direxists(log_path)) + { + createdir(log_path); + } + + log_path += path_separator; + return(log_path); + } + + ECAL_API std::string GeteCALActiveIniFile() + { + std::string ini_file = GeteCALConfigPath(); + ini_file += ECAL_DEFAULT_CFG; + return ini_file; + } + + ECAL_API std::string GeteCALDefaultIniFile() + { + return GeteCALActiveIniFile(); + } + + } +} \ No newline at end of file diff --git a/ecal/core/src/config/ecal_config_reader.cpp b/ecal/core/src/config/ecal_config_reader.cpp deleted file mode 100644 index 7b4be4de19..0000000000 --- a/ecal/core/src/config/ecal_config_reader.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2024 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Global config class -**/ - -#include -#include -#include - -#include "ecal_def.h" -#include "ecal_config_reader.h" -#include "ecal_global_accessors.h" -#include "util/getenvvar.h" - -#include -#include -#include -#include - -#include -#include -#include - -#ifdef ECAL_OS_LINUX -#include -#include -#include -#include -#endif - -#if ECAL_CORE_CONFIG_INIFILE -#include -#endif - -namespace -{ -#ifdef ECAL_OS_WINDOWS - const char path_sep('\\'); -#endif /* ECAL_OS_WINDOWS */ -#ifdef ECAL_OS_LINUX - const char path_sep('/'); -#endif /* ECAL_OS_LINUX */ - - bool fileexists(const std::string& fname_) - { - const std::ifstream infile(fname_); - return infile.good(); - } - - bool direxists(const std::string& path_) - { - const EcalUtils::Filesystem::FileStatus status(path_, EcalUtils::Filesystem::Current); - return (status.IsOk() && (status.GetType() == EcalUtils::Filesystem::Type::Dir)); - } - - void createdir(const std::string& path_) - { - EcalUtils::Filesystem::MkDir(path_, EcalUtils::Filesystem::Current); - } - - std::string eCALDataEnvPath() - { - std::string ecal_data_path = getEnvVar("ECAL_DATA"); - if (!ecal_data_path.empty()) - { - if (ecal_data_path.back() != path_sep) - ecal_data_path += path_sep; - } - return ecal_data_path; - } - - std::string eCALDataCMakePath() - { - std::string cmake_data_path; -#ifdef ECAL_OS_LINUX - std::string ecal_install_config_dir(ECAL_INSTALL_CONFIG_DIR); - std::string ecal_install_prefix(ECAL_INSTALL_PREFIX); - - if ((!ecal_install_config_dir.empty() && (ecal_install_config_dir[0] == path_sep)) - || ecal_install_prefix.empty()) - { - cmake_data_path = ecal_install_config_dir; - } - else if (!ecal_install_prefix.empty()) - { - cmake_data_path = ecal_install_prefix + path_sep + ecal_install_config_dir; - } - if (cmake_data_path.back() != path_sep) { cmake_data_path += path_sep; } -#endif /* ECAL_OS_LINUX */ - return cmake_data_path; - } - - std::string eCALDataSystemPath() - { - std::string system_data_path; -#ifdef ECAL_OS_WINDOWS - system_data_path = getEnvVar("ProgramData"); - if (!system_data_path.empty()) - { - if (system_data_path.back() != path_sep) - { - system_data_path += path_sep; - } - system_data_path += std::string("eCAL") + path_sep; - } -#endif /* ECAL_OS_WINDOWS */ - -#ifdef ECAL_OS_LINUX - system_data_path = "/etc/ecal/"; -#endif /* ECAL_OS_LINUX */ - return system_data_path; - } - - - // 1. The path is not empty - // 2. The ecal.ini exists in that directory - bool IsValidConfigFilePath(const std::string& path) - { - if (path.empty()) { return false; } - - // check existence of ecal.ini file - const EcalUtils::Filesystem::FileStatus ecal_ini_status(path + std::string(ECAL_DEFAULT_CFG), EcalUtils::Filesystem::Current); - if (ecal_ini_status.IsOk() && (ecal_ini_status.GetType() == EcalUtils::Filesystem::Type::RegularFile)) - { - return true; - } - - return false; - } - -} - -namespace eCAL -{ - namespace Util - { - ECAL_API std::string GeteCALHomePath() - { - std::string home_path; - -#ifdef ECAL_OS_WINDOWS - // check ECAL_HOME - home_path = getEnvVar("ECAL_HOME"); - if (!home_path.empty()) - { - if (*home_path.rbegin() != path_sep) home_path += path_sep; - } - if (!std::string(ECAL_HOME_PATH_WINDOWS).empty()) //-V815 - { - home_path += path_sep; - home_path += ECAL_HOME_PATH_WINDOWS; - } -#endif /* ECAL_OS_WINDOWS */ - -#ifdef ECAL_OS_LINUX - const char *hdir; - if ((hdir = getenv("HOME")) == NULL) { - hdir = getpwuid(getuid())->pw_dir; - } - home_path += hdir; - if (!std::string(ECAL_HOME_PATH_LINUX).empty()) - { - home_path += "/"; - home_path += ECAL_HOME_PATH_LINUX; - } -#endif /* ECAL_OS_LINUX */ - - // create if not exists - if (!direxists(home_path)) - { - createdir(home_path); - } - - home_path += path_sep; - return(home_path); - } - - ECAL_API std::string GeteCALConfigPath() - { - // ----------------------------------------------------------- - // precedence 1: ECAL_DATA variable (windows and linux) - // ----------------------------------------------------------- - const std::string ecal_data_path{ eCALDataEnvPath() }; - - // ----------------------------------------------------------- - // precedence 2: cmake configured data paths (linux only) - // ----------------------------------------------------------- - const std::string cmake_data_path{ eCALDataCMakePath() }; - - // ----------------------------------------------------------- - // precedence 3: system data path - // ----------------------------------------------------------- - const std::string system_data_path(eCALDataSystemPath()); - - // Check for first directory which contains the ini file. - std::vector search_directories{ ecal_data_path, cmake_data_path, system_data_path }; - - auto it = std::find_if(search_directories.begin(), search_directories.end(), IsValidConfigFilePath); - // We should have encountered a valid path - if (it != search_directories.end()) - return (*it); - - // If valid path is not encountered, defaults should be used - return std::string(""); - } - - ECAL_API std::string GeteCALUserSettingsPath() - { - std::string settings_path; -#ifdef ECAL_OS_WINDOWS - settings_path = GeteCALConfigPath(); -#endif /* ECAL_OS_WINDOWS */ - -#ifdef ECAL_OS_LINUX - settings_path = GeteCALHomePath(); -#endif /* ECAL_OS_LINUX */ - settings_path += std::string(ECAL_SETTINGS_PATH); - - if (!direxists(settings_path)) - { - createdir(settings_path); - } - - settings_path += path_sep; - return(settings_path); - } - - ECAL_API std::string GeteCALLogPath() - { - std::string log_path; -#ifdef ECAL_OS_WINDOWS - log_path = GeteCALConfigPath(); -#endif /* ECAL_OS_WINDOWS */ - -#ifdef ECAL_OS_LINUX - log_path = GeteCALHomePath(); -#endif /* ECAL_OS_LINUX */ - - log_path += std::string(ECAL_LOG_PATH); - - if (!direxists(log_path)) - { - createdir(log_path); - } - - log_path += path_sep; - return(log_path); - } - - ECAL_API std::string GeteCALActiveIniFile() - { - std::string ini_file = GeteCALConfigPath(); - ini_file += ECAL_DEFAULT_CFG; - return ini_file; - } - - ECAL_API std::string GeteCALDefaultIniFile() - { - return GeteCALActiveIniFile(); - } - } - - //////////////////////////////////////////////////////// - // CConfigImpl - //////////////////////////////////////////////////////// -#if ECAL_CORE_CONFIG_INIFILE - - class CConfigImpl : public CSimpleIni - { - public: - CConfigImpl() = default; - virtual ~CConfigImpl() = default; - - void OverwriteKeys(const std::vector& key_vec_) - { - m_overwrite_keys = key_vec_; - OverwriteKeysNow(); - } - - void OverwriteKeysNow() - { - // update command line keys - for (const auto& full_key : m_overwrite_keys) - { - auto sec_pos = full_key.find_last_of('/'); - if (sec_pos == std::string::npos) continue; - const std::string section = full_key.substr(0, sec_pos); - std::string key = full_key.substr(sec_pos+1); - - auto val_pos = key.find_first_of(':'); - if (val_pos == std::string::npos) continue; - const std::string value = key.substr(val_pos+1); - key = key.substr(0, val_pos); - - const SI_Error err = SetValue(section.c_str(), key.c_str(), value.c_str()); - if (err == SI_FAIL) - { - std::cout << "Error: Could not overwrite key " << key << " in section " << section << "."; - } - } - } - - bool AddFile(std::string& file_name_) - { - std::string cfg_fname = file_name_; - if (!fileexists(cfg_fname)) - { - cfg_fname = Util::GeteCALConfigPath() + cfg_fname; - } - - // load settings config file - bool loaded(false); - if (fileexists(cfg_fname)) - { - // apply file name to manager - loaded = LoadFile(cfg_fname.c_str()) == 0; - } - - // load error ? - if(!loaded) - { - std::cout << "eCAL::Config - Could not load ini file, using defaults. File name : " << cfg_fname << std::endl; - } - else - { - // return full name - file_name_ = cfg_fname; - } - - OverwriteKeysNow(); - - return loaded; - } - protected: - std::vector m_overwrite_keys; - }; - -#else // ECAL_CORE_CONFIG_INIFILE - - class CConfigImpl - { - public: - CConfigImpl() = default; - virtual ~CConfigImpl() = default; - - void OverwriteKeys(const std::vector& /*key_vec_*/) {} - void AddFile(std::string& /*file_name_*/) {} - - std::string GetValue(const std::string& /*section_*/, const std::string& /*key_*/, const std::string& default_) { return default_;} - long GetLongValue(const std::string& /*section_*/, const std::string& /*key_*/, long default_) { return default_; } - double GetDoubleValue(const std::string& /*section_*/, const std::string& /*key_*/, double default_) { return default_; } - }; - -#endif // ECAL_CORE_CONFIG_INIFILE - - //////////////////////////////////////////////////////// - // CConfigBase - //////////////////////////////////////////////////////// - CConfig::CConfig() : - m_impl(nullptr) - { - m_impl = std::make_unique(); - } - - CConfig::~CConfig() = default; - - void CConfig::OverwriteKeys(const std::vector& key_vec_) - { - m_impl->OverwriteKeys(key_vec_); - } - - bool CConfig::AddFile(std::string& ini_file_) - { - return m_impl->AddFile(ini_file_); - } - - bool CConfig::Validate() - { - // ------------------------------------------------------------------ - // UDP and TCP publlisher mode should not set to "auto (2)" both - // - // [publisher] - // use_tcp = 2 - // use_udp_mc = 2 - // ------------------------------------------------------------------ - { - const int use_tcp = get("publisher", "use_tcp", 0); - const int use_udp_mc = get("publisher", "use_udp_mc", 0); - if ((use_tcp == 2) && (use_udp_mc == 2)) - { - std::cerr << "eCAL config error: to set [publisher/use_tcp] and [publisher/use_udp_mc] both on auto mode (2) is not allowed" << std::endl; - return false; - } - } - - return true; - } - - bool CConfig::get(const std::string& section_, const std::string& key_, bool default_) - { - std::string default_s("false"); - if (default_) default_s = "true"; - std::string value_s = m_impl->GetValue(section_.c_str(), key_.c_str(), default_s.c_str()); - // cause warning C4244 with VS2017 - //std::transform(value_s.begin(), value_s.end(), value_s.begin(), tolower); - std::transform(value_s.begin(), value_s.end(), value_s.begin(), - [](char c) {return static_cast(::tolower(c)); }); - - if ((value_s == "true") || (value_s == "1")) return true; - return false; - } - - int CConfig::get(const std::string& section_, const std::string& key_, int default_) - { - return static_cast(m_impl->GetLongValue(section_.c_str(), key_.c_str(), static_cast(default_))); - } - - unsigned int CConfig::get(const std::string& section_, const std::string& key_, unsigned int default_) - { - return static_cast(m_impl->GetLongValue(section_.c_str(), key_.c_str(), static_cast(default_))); - } - - size_t CConfig::get(const std::string& section_, const std::string& key_, size_t default_) - { - return static_cast(m_impl->GetLongValue(section_.c_str(), key_.c_str(), static_cast(default_))); - } - - double CConfig::get(const std::string& section_, const std::string& key_, double default_) - { - return m_impl->GetDoubleValue(section_.c_str(), key_.c_str(), default_); - } - - std::string CConfig::get(const std::string& section_, const std::string& key_, const char* default_) - { - return m_impl->GetValue(section_.c_str(), key_.c_str(), default_); - } -} diff --git a/ecal/core/src/config/ecal_config_reader.h b/ecal/core/src/config/ecal_config_reader.h deleted file mode 100644 index a08fbea310..0000000000 --- a/ecal/core/src/config/ecal_config_reader.h +++ /dev/null @@ -1,57 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2024 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Global eCAL configuration interface -**/ - -#pragma once - -#include - -#include -#include -#include - -namespace eCAL -{ - class CConfigImpl; - class CConfig - { - public: - CConfig(); - virtual ~CConfig(); - - void OverwriteKeys(const std::vector& key_vec_); - bool AddFile(std::string& ini_file_); - - bool Validate(); - - // common getter - bool get(const std::string& section_, const std::string& key_, bool default_); - int get(const std::string& section_, const std::string& key_, int default_); - double get(const std::string& section_, const std::string& key_, double default_); - std::string get(const std::string& section_, const std::string& key_, const char* default_); - unsigned int get(const std::string& section_, const std::string& key_, unsigned int default_); - size_t get(const std::string& section_, const std::string& key_, size_t default_); - - private: - std::unique_ptr m_impl; - }; -} diff --git a/ecal/core/src/config/transport_layer.cpp b/ecal/core/src/config/transport_layer.cpp new file mode 100644 index 0000000000..1b569e6c8c --- /dev/null +++ b/ecal/core/src/config/transport_layer.cpp @@ -0,0 +1,44 @@ +/* =========================== LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * =========================== LICENSE ================================= + */ + +#include "ecal/config/transport_layer.h" + +namespace eCAL +{ + namespace TransportLayer + { + namespace UDP + { + Configuration& Configuration::operator=(const Configuration& other) + { + config_version = other.config_version; + join_all_interfaces = other.join_all_interfaces; + mask = other.mask; + mode = other.mode; + network = other.network; + npcap_enabled = other.npcap_enabled; + port = other.port; + receive_buffer = other.receive_buffer; + send_buffer = other.send_buffer; + + return *this; + } + } + } +} \ No newline at end of file diff --git a/ecal/core/src/ecal.cpp b/ecal/core/src/ecal.cpp index 03c2e1a434..54a5cf90ec 100644 --- a/ecal/core/src/ecal.cpp +++ b/ecal/core/src/ecal.cpp @@ -101,10 +101,6 @@ namespace eCAL int Initialize(int argc_ , char **argv_, const char *unit_name_, unsigned int components_) { eCAL::Configuration config(argc_, argv_); - - // Default behaviour: If not specified, try to use the default ini file - if (config.GetIniFilePath().empty()) - config.InitConfigWithDefaultIni(); return Initialize(config, unit_name_, components_); } @@ -121,10 +117,6 @@ namespace eCAL int Initialize(std::vector args_, const char *unit_name_, unsigned int components_) //-V826 { eCAL::Configuration config(args_); - - // Default behaviour: If not specified, try to use the default ini file - if (config.GetIniFilePath().empty()) - config.InitConfigWithDefaultIni(); return Initialize(config, unit_name_, components_); } @@ -140,11 +132,6 @@ namespace eCAL **/ int Initialize(eCAL::Configuration& config_, const char *unit_name_ /*= nullptr*/, unsigned int components_ /*= Init::Default*/) { - if (g_globals() == nullptr) - { - InitGlobals(); - } - g_ecal_configuration = config_; if (unit_name_ != nullptr) @@ -155,7 +142,7 @@ namespace eCAL g_globals_ctx_ref_cnt++; // (post)initialize single components - const int success = g_globals()->Initialize(components_, &GetConfiguration().command_line_arguments.config_keys); + const int success = g_globals()->Initialize(components_); if (config_.command_line_arguments.dump_config) { diff --git a/ecal/core/src/ecal_def.h b/ecal/core/src/ecal_def.h index a9c6789d0a..4690ab5eee 100644 --- a/ecal/core/src/ecal_def.h +++ b/ecal/core/src/ecal_def.h @@ -38,145 +38,19 @@ constexpr const char* ECAL_LOG_PATH = "logs"; constexpr const char* ECAL_SETTINGS_PATH = "cfg"; /* ini file name */ -constexpr const char* ECAL_DEFAULT_CFG = "ecal.ini"; - -/**********************************************************************************************/ -/* monitor settings */ -/**********************************************************************************************/ -/* timeout for automatic removing monitoring topics in ms */ -constexpr unsigned int MON_TIMEOUT = 5000U; -/* topics blacklist as regular expression (will not be monitored) */ -constexpr const char* MON_FILTER_EXCL = "^__.*$"; -/* topics whitelist as regular expression (will be monitored only) */ -constexpr const char* MON_FILTER_INCL = ""; - -/* logging filter settings */ -constexpr eCAL_Logging_Filter MON_LOG_FILTER_CON = (log_level_info | log_level_warning | log_level_error | log_level_fatal); -constexpr eCAL_Logging_Filter MON_LOG_FILTER_FILE = log_level_none; -constexpr eCAL_Logging_Filter MON_LOG_FILTER_UDP = (log_level_info | log_level_warning | log_level_error | log_level_fatal); - - -/**********************************************************************************************/ -/* sys settings */ -/**********************************************************************************************/ -/* sys app witch will not be imported from cloud */ -constexpr const char* SYS_FILTER_EXCL = "^eCALSysClient$|^eCALSysGUI$|^eCALSys$*"; - -/**********************************************************************************************/ -/* network settings */ -/**********************************************************************************************/ -/* network switch */ -constexpr bool NET_ENABLED = false; +constexpr const char* ECAL_DEFAULT_CFG = "ecal.yaml"; /* eCAL udp multicast defines */ -constexpr eCAL::Types::UdpConfigVersion NET_UDP_MULTICAST_CONFIG_VERSION = eCAL::Types::UdpConfigVersion::V1; -constexpr const char* NET_UDP_MULTICAST_GROUP = "239.0.0.1"; -constexpr const char* NET_UDP_MULTICAST_MASK = "0.0.0.15"; -constexpr unsigned int NET_UDP_MULTICAST_PORT = 14000U; -constexpr unsigned int NET_UDP_MULTICAST_TTL = 3U; -constexpr unsigned int NET_UDP_MULTICAST_PORT_REG_OFF = 0U; -constexpr unsigned int NET_UDP_MULTICAST_PORT_LOG_OFF = 1U; -constexpr unsigned int NET_UDP_MULTICAST_PORT_SAMPLE_OFF = 2U; -constexpr unsigned int NET_UDP_MULTICAST_SNDBUF = (5U*1024U*1024U); /* 5 MByte */ -constexpr unsigned int NET_UDP_MULTICAST_RCVBUF = (5U*1024U*1024U); /* 5 MByte */ -constexpr bool NET_UDP_MULTICAST_JOIN_ALL_IF_ENABLED = false; - -constexpr unsigned int NET_UDP_RECBUFFER_TIMEOUT = 1000U; /* ms */ -constexpr unsigned int NET_UDP_RECBUFFER_CLEANUP = 10U; /* ms */ - -/* overall udp multicast bandwidth limitation in bytes/s, -1 == no limitation*/ -constexpr int NET_BANDWIDTH_MAX_UDP = (-1); - -constexpr bool NET_TCP_REC_ENABLED = true; -constexpr bool NET_SHM_REC_ENABLED = true; - -constexpr bool NET_UDP_MC_REC_ENABLED = true; - -constexpr bool NET_NPCAP_ENABLED = false; - -constexpr unsigned int NET_TCP_PUBSUB_NUM_EXECUTOR_READER = 4U; -constexpr unsigned int NET_TCP_PUBSUB_NUM_EXECUTOR_WRITER = 4U; -constexpr unsigned int NET_TCP_PUBSUB_MAX_RECONNECTIONS = 5U; - -/* common host group name that enables interprocess mechanisms across (virtual) host borders (e.g, Docker); by default equivalent to local host name */ -constexpr const char* NET_HOST_GROUP_NAME = ""; - -/**********************************************************************************************/ -/* publisher settings */ -/**********************************************************************************************/ -/* use shared memory transport layer [auto = 2, on = 1, off = 0] */ -constexpr eCAL::TLayer::eSendMode PUB_USE_SHM = eCAL::TLayer::eSendMode::smode_auto; -/* use tcp transport layer [auto = 2, on = 1, off = 0] */ -constexpr eCAL::TLayer::eSendMode PUB_USE_TCP = eCAL::TLayer::eSendMode::smode_off; -/* use udp multicast transport layer [auto = 2, on = 1, off = 0] */ -constexpr eCAL::TLayer::eSendMode PUB_USE_UDP_MC = eCAL::TLayer::eSendMode::smode_auto; - -/* share topic type [ on = 1, off = 0] */ -constexpr bool PUB_SHARE_TTYPE = true; -/* share topic description [ on = 1, off = 0] */ -constexpr bool PUB_SHARE_TDESC = true; - -/* minimum size for created shared memory files */ -constexpr unsigned int PUB_MEMFILE_MINSIZE = (4U*1024U); -/* reserve buffer size before reallocation in % */ -constexpr unsigned int PUB_MEMFILE_RESERVE = 50U; +constexpr unsigned int NET_UDP_MULTICAST_PORT_REG_OFF = 0U; // to delete +constexpr unsigned int NET_UDP_MULTICAST_PORT_LOG_OFF = 1U; // to delete +constexpr unsigned int NET_UDP_MULTICAST_PORT_SAMPLE_OFF = 2U; // to delete /* timeout for create / open a memory file using mutex lock in ms */ constexpr unsigned int PUB_MEMFILE_CREATE_TO = 200U; constexpr unsigned int PUB_MEMFILE_OPEN_TO = 200U; +/* memory file access timeout */ +constexpr unsigned int EXP_MEMFILE_ACCESS_TIMEOUT = 100U; -/* timeout for memory read acknowledge signal from data reader in ms */ -constexpr unsigned int PUB_MEMFILE_ACK_TO = 0U; /* ms */ - -/* defines number of memory files handle by the publisher for a 1:n connection - a higher number will increase data throughput, but will also increase the size of used memory, number of semaphores - and number of memory file observer threads on subscription side, default = 1, double buffering = 2 - higher values than 3 are not recommended - values > 1 will break local IPC compatibility to eCAL 5.9 and older -*/ -constexpr unsigned int PUB_MEMFILE_BUF_COUNT = 1U; - -/* allow subscriber to access memory file without copying content in advance (zero copy) - this memory file is blocked for other readers wihle processed by the user callback function - this option is fully IPC compatible to all eCAL 5.x versions -*/ -constexpr bool PUB_MEMFILE_ZERO_COPY = false; - -/**********************************************************************************************/ -/* service settings */ -/**********************************************************************************************/ -/* support service protocol v0, eCAL 5.11 and older (0 = off, 1 = on) */ -constexpr bool SERVICE_PROTOCOL_V0 = true; - -/* support service protocol v1, eCAL 5.12 and newer (0 = off, 1 = on) */ -constexpr bool SERVICE_PROTOCOL_V1 = true; - -/**********************************************************************************************/ -/* time settings */ -/**********************************************************************************************/ -constexpr const char* TIME_SYNC_MOD_RT = ""; -constexpr const char* TIME_SYNC_MOD_REPLAY = ""; -constexpr const char* TIME_SYNC_MODULE = "ecaltime-localtime"; - -/**********************************************************************************************/ -/* process settings */ -/**********************************************************************************************/ -constexpr const char* PROCESS_TERMINAL_EMULATOR = ""; - -/**********************************************************************************************/ -/* ecal internal timings */ -/**********************************************************************************************/ -/* timeout for automatic removing registered topics and memory files in global database in ms */ -constexpr unsigned int CMN_REGISTRATION_TO = (60U*1000U); - -/* time for resend registration info from publisher/subscriber in ms */ -constexpr unsigned int CMN_REGISTRATION_REFRESH = 1000U; - -/* delta time to check timeout for data readers in ms */ -constexpr unsigned int CMN_DATAREADER_TIMEOUT_RESOLUTION_MS = 100U; - -/* cylce time udp receive threads in ms */ -constexpr unsigned int CMN_UDP_RECEIVE_THREAD_CYCLE_TIME_MS = 1000U; /**********************************************************************************************/ /* events */ @@ -184,21 +58,3 @@ constexpr unsigned int CMN_UDP_RECEIVE_THREAD_CYCLE_TIME_MS = 1000U; /* common stop event prefix to shut down a local user process */ constexpr const char* EVENT_SHUTDOWN_PROC = "ecal_shutdown_process"; -/**********************************************************************************************/ -/* experimental */ -/**********************************************************************************************/ -/* enable distribution of monitoring/registration information via shared memory */ -constexpr bool EXP_SHM_MONITORING_ENABLED = false; -/* enable distribution of monitoring/registration information via network (default) */ -constexpr bool EXP_NETWORK_MONITORING_ENABLED = true; -/* queue size of monitoring/registration events */ -constexpr unsigned int EXP_SHM_MONITORING_QUEUE_SIZE = 1024U; -/* domain name for shared memory based monitoring/registration */ -constexpr const char* EXP_SHM_MONITORING_DOMAIN = "ecal_monitoring"; -/* memory file access timeout */ -constexpr unsigned int EXP_MEMFILE_ACCESS_TIMEOUT = 100U; - -/* enable dropping of payload messages that arrive out of order */ -constexpr bool EXP_DROP_OUT_OF_ORDER_MESSAGES = false; - -constexpr eCAL::Monitoring::Types::Mode EXP_MONITORING_MODE = eCAL::Monitoring::Types::Mode::none; diff --git a/ecal/core/src/ecal_descgate.cpp b/ecal/core/src/ecal_descgate.cpp index ea80fd0d45..2c62df3113 100644 --- a/ecal/core/src/ecal_descgate.cpp +++ b/ecal/core/src/ecal_descgate.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,81 +27,125 @@ namespace { - eCAL::Util::DescQualityFlags GetDataTypeInfoQuality(const eCAL::SDataTypeInformation& data_type_info_, bool is_producer_) + eCAL::Registration::DescQualityFlags GetDataTypeInfoQuality(const eCAL::SDataTypeInformation& data_type_info_, bool is_producer_) { - eCAL::Util::DescQualityFlags quality = eCAL::Util::DescQualityFlags::NO_QUALITY; + eCAL::Registration::DescQualityFlags quality = eCAL::Registration::DescQualityFlags::NO_QUALITY; if (!data_type_info_.name.empty()) - quality |= eCAL::Util::DescQualityFlags::TYPENAME_AVAILABLE; + quality |= eCAL::Registration::DescQualityFlags::TYPENAME_AVAILABLE; if (!data_type_info_.encoding.empty()) - quality |= eCAL::Util::DescQualityFlags::ENCODING_AVAILABLE; + quality |= eCAL::Registration::DescQualityFlags::ENCODING_AVAILABLE; if (!data_type_info_.descriptor.empty()) - quality |= eCAL::Util::DescQualityFlags::DESCRIPTION_AVAILABLE; - if(is_producer_) quality |= eCAL::Util::DescQualityFlags::INFO_COMES_FROM_PRODUCER; + quality |= eCAL::Registration::DescQualityFlags::DESCRIPTION_AVAILABLE; + if(is_producer_) quality |= eCAL::Registration::DescQualityFlags::INFO_COMES_FROM_PRODUCER; return quality; } + + eCAL::Registration::SEntityId ConvertToEntityId(const eCAL::Registration::SampleIdentifier& sample_identifier) + { + eCAL::Registration::SEntityId id{ sample_identifier.entity_id, sample_identifier.process_id, sample_identifier.host_name}; + return id; + } + + } namespace eCAL { - CDescGate::CDescGate(const std::chrono::milliseconds& exp_timeout_) : - m_publisher_info_map (exp_timeout_), - m_subscriber_info_map (exp_timeout_), - m_service_info_map (exp_timeout_), - m_client_info_map (exp_timeout_) + CDescGate::CDescGate() = default; + CDescGate::~CDescGate() = default; + + std::set CDescGate::GetPublisherIDs() const { + return GetTopicIDs(m_publisher_info_map); + } + + bool CDescGate::GetPublisherInfo(const Registration::STopicId& id_, Registration::SQualityTopicInfo& topic_info_) const + { + return GetTopic(id_, m_publisher_info_map, topic_info_); } - CDescGate::~CDescGate() = default; - Util::QualityTopicInfoMultiMap CDescGate::GetPublishers() + std::set CDescGate::GetSubscriberIDs() const { - return GetTopics(m_publisher_info_map); + return GetTopicIDs(m_subscriber_info_map); } - Util::QualityTopicInfoMultiMap CDescGate::GetSubscribers() + bool CDescGate::GetSubscriberInfo(const Registration::STopicId& id_, Registration::SQualityTopicInfo& topic_info_) const { - return GetTopics(m_subscriber_info_map); + return GetTopic(id_, m_subscriber_info_map, topic_info_); } - Util::QualityServiceInfoMultimap CDescGate::GetServices() + std::set CDescGate::GetServiceIDs() const { - return GetServices(m_service_info_map); + return GetServiceIDs(m_service_info_map); } - Util::QualityServiceInfoMultimap CDescGate::GetClients() + bool CDescGate::GetServiceInfo(const Registration::SServiceId& id_, Registration::SQualityServiceInfo& service_info_) const { - return GetServices(m_client_info_map); + return GetService(id_, m_service_info_map, service_info_); } - Util::QualityTopicInfoMultiMap CDescGate::GetTopics(SQualityTopicIdMap& topic_info_map_) + std::set CDescGate::GetClientIDs() const { - Util::QualityTopicInfoMultiMap multi_map; + return GetServiceIDs(m_client_info_map); + } - const std::lock_guard lock(topic_info_map_.mtx); - topic_info_map_.map.remove_deprecated(); + bool CDescGate::GetClientInfo(const Registration::SServiceId& id_, Registration::SQualityServiceInfo& service_info_) const + { + return GetService(id_, m_client_info_map, service_info_); + } + + std::set CDescGate::GetTopicIDs(const SQualityTopicIdMap& topic_info_map_) + { + std::set topic_id_set; + const std::lock_guard lock(topic_info_map_.mtx); for (const auto& topic_map_it : topic_info_map_.map) { - multi_map.insert(std::pair(topic_map_it.first.topic_name, topic_map_it.second)); + topic_id_set.insert(topic_map_it.first); } + return topic_id_set; + } - return multi_map; + bool CDescGate::GetTopic(const Registration::STopicId& id_, const SQualityTopicIdMap& topic_info_map_, Registration::SQualityTopicInfo& topic_info_) + { + const std::lock_guard lock(topic_info_map_.mtx); + auto iter = topic_info_map_.map.find(id_); + if (iter == topic_info_map_.map.end()) + { + return false; + } + else + { + topic_info_ = iter->second; + return true; + } } - Util::QualityServiceInfoMultimap CDescGate::GetServices(SQualityServiceIdMap& service_method_info_map_) + std::set CDescGate::GetServiceIDs(const SQualityServiceIdMap& service_method_info_map_) { - Util::QualityServiceInfoMultimap multi_map; + std::set service_id_set; const std::lock_guard lock(service_method_info_map_.mtx); - service_method_info_map_.map.remove_deprecated(); - for (const auto& service_method_info_map_it : service_method_info_map_.map) { - Util::SServiceMethod key; - key.service_name = service_method_info_map_it.first.service_name; - key.method_name = service_method_info_map_it.first.method_name; - multi_map.insert(std::pair(key, service_method_info_map_it.second)); + service_id_set.insert(service_method_info_map_it.first); + } + return service_id_set; + } + + bool CDescGate::GetService(const Registration::SServiceId& id_, const SQualityServiceIdMap& service_method_info_map_, Registration::SQualityServiceInfo& service_method_info_) + { + const std::lock_guard lock(service_method_info_map_.mtx); + auto iter = service_method_info_map_.map.find(id_); + if (iter == service_method_info_map_.map.end()) + { + return false; + } + else + { + service_method_info_ = iter->second; + return true; } - return multi_map; } void CDescGate::ApplySample(const Registration::Sample& sample_, eTLayerType /*layer_*/) @@ -125,12 +169,12 @@ namespace eCAL response_type.name = method.resp_type; response_type.descriptor = method.resp_desc; - ApplyServiceDescription(m_service_info_map, sample_.service.sname, method.mname, std::stoull(sample_.service.sid), request_type, response_type, GetDataTypeInfoQuality(request_type, true), GetDataTypeInfoQuality(response_type, true)); + ApplyServiceDescription(m_service_info_map, sample_.identifier, sample_.service.sname, method.mname, request_type, response_type, GetDataTypeInfoQuality(request_type, true), GetDataTypeInfoQuality(response_type, true)); } } break; case bct_unreg_service: - RemServiceDescription(m_service_info_map, sample_.service.sname, std::stoull(sample_.service.sid)); + RemServiceDescription(m_service_info_map, sample_.identifier, sample_.service.sname); break; case bct_reg_client: for (const auto& method : sample_.client.methods) @@ -143,23 +187,23 @@ namespace eCAL response_type.name = method.resp_type; response_type.descriptor = method.resp_desc; - ApplyServiceDescription(m_client_info_map, sample_.client.sname, method.mname, std::stoull(sample_.client.sid), request_type, response_type, GetDataTypeInfoQuality(request_type, false), GetDataTypeInfoQuality(response_type, false)); + ApplyServiceDescription(m_client_info_map, sample_.identifier, sample_.client.sname, method.mname, request_type, response_type, GetDataTypeInfoQuality(request_type, false), GetDataTypeInfoQuality(response_type, false)); } break; case bct_unreg_client: - RemServiceDescription(m_client_info_map, sample_.client.sname, std::stoull(sample_.client.sid)); + RemServiceDescription(m_client_info_map, sample_.identifier, sample_.client.sname); break; case bct_reg_publisher: - ApplyTopicDescription(m_publisher_info_map, sample_.topic.tname, std::stoull(sample_.topic.tid), sample_.topic.tdatatype, GetDataTypeInfoQuality(sample_.topic.tdatatype, true)); + ApplyTopicDescription(m_publisher_info_map, sample_.identifier, sample_.topic.tname, sample_.topic.tdatatype, GetDataTypeInfoQuality(sample_.topic.tdatatype, true)); break; case bct_unreg_publisher: - RemTopicDescription(m_publisher_info_map, sample_.topic.tname, std::stoull(sample_.topic.tid)); + RemTopicDescription(m_publisher_info_map, sample_.identifier, sample_.topic.tname); break; case bct_reg_subscriber: - ApplyTopicDescription(m_subscriber_info_map, sample_.topic.tname, std::stoull(sample_.topic.tid), sample_.topic.tdatatype, GetDataTypeInfoQuality(sample_.topic.tdatatype, false)); + ApplyTopicDescription(m_subscriber_info_map, sample_.identifier, sample_.topic.tname, sample_.topic.tdatatype, GetDataTypeInfoQuality(sample_.topic.tdatatype, false)); break; case bct_unreg_subscriber: - RemTopicDescription(m_subscriber_info_map, sample_.topic.tname, std::stoull(sample_.topic.tid)); + RemTopicDescription(m_subscriber_info_map, sample_.identifier, sample_.topic.tname); break; default: { @@ -170,73 +214,71 @@ namespace eCAL } void CDescGate::ApplyTopicDescription(SQualityTopicIdMap& topic_info_map_, - const std::string& topic_name_, - const Util::TopicId& topic_id_, - const SDataTypeInformation& topic_info_, - const Util::DescQualityFlags topic_quality_) + const Registration::SampleIdentifier& topic_id_, + const std::string& topic_name_, + const SDataTypeInformation& topic_info_, + const Registration::DescQualityFlags topic_quality_) { - const auto topic_info_key = STopicIdKey{ topic_name_, topic_id_ }; + const auto topic_info_key = Registration::STopicId{ ConvertToEntityId(topic_id_), topic_name_ }; - Util::SQualityTopicInfo topic_quality_info; - topic_quality_info.id = topic_id_; + Registration::SQualityTopicInfo topic_quality_info; topic_quality_info.info = topic_info_; topic_quality_info.quality = topic_quality_; const std::unique_lock lock(topic_info_map_.mtx); - topic_info_map_.map.remove_deprecated(); topic_info_map_.map[topic_info_key] = topic_quality_info; } - void CDescGate::RemTopicDescription(SQualityTopicIdMap& topic_info_map_, const std::string& topic_name_, const Util::TopicId& topic_id_) + void CDescGate::RemTopicDescription(SQualityTopicIdMap& topic_info_map_, + const Registration::SampleIdentifier& topic_id_, + const std::string& topic_name_) { const std::unique_lock lock(topic_info_map_.mtx); - topic_info_map_.map.remove_deprecated(); - topic_info_map_.map.erase(STopicIdKey{ topic_name_, topic_id_ }); + topic_info_map_.map.erase(Registration::STopicId{ ConvertToEntityId(topic_id_) , topic_name_}); } void CDescGate::ApplyServiceDescription(SQualityServiceIdMap& service_method_info_map_, - const std::string& service_name_, - const std::string& method_name_, - const Util::ServiceId& service_id_, - const SDataTypeInformation& request_type_information_, - const SDataTypeInformation& response_type_information_, - const Util::DescQualityFlags request_type_quality_, - const Util::DescQualityFlags response_type_quality_) - { - const auto service_method_info_key = SServiceIdKey{ service_name_, method_name_, service_id_}; - - Util::SQualityServiceInfo service_quality_info; - service_quality_info.id = service_id_; + const Registration::SampleIdentifier& service_id_, + const std::string& service_name_, + const std::string& method_name_, + const SDataTypeInformation& request_type_information_, + const SDataTypeInformation& response_type_information_, + const Registration::DescQualityFlags request_type_quality_, + const Registration::DescQualityFlags response_type_quality_) + { + const auto service_method_info_key = Registration::SServiceId{ ConvertToEntityId(service_id_), service_name_, method_name_}; + + Registration::SQualityServiceInfo service_quality_info; service_quality_info.info.request_type = request_type_information_; service_quality_info.info.response_type = response_type_information_; service_quality_info.request_quality = request_type_quality_; service_quality_info.response_quality = response_type_quality_; const std::lock_guard lock(service_method_info_map_.mtx); - service_method_info_map_.map.remove_deprecated(); service_method_info_map_.map[service_method_info_key] = service_quality_info; } - void CDescGate::RemServiceDescription(SQualityServiceIdMap& service_method_info_map_, const std::string& service_name_, const Util::ServiceId& service_id_) + void CDescGate::RemServiceDescription(SQualityServiceIdMap& service_method_info_map_, + const Registration::SampleIdentifier& service_id_, + const std::string& service_name_) { - std::list service_method_infos_to_remove; + std::list service_method_info_keys_to_remove; const std::lock_guard lock(service_method_info_map_.mtx); - service_method_info_map_.map.remove_deprecated(); for (auto&& service_it : service_method_info_map_.map) { - const auto service_method_info = service_it.first; - if ((service_method_info.service_name == service_name_) - && (service_method_info.service_id == service_id_)) + const auto service_method_info_key = service_it.first; + if ((service_method_info_key.service_name == service_name_) + && (service_method_info_key.service_id == ConvertToEntityId(service_id_))) { - service_method_infos_to_remove.push_back(service_method_info); + service_method_info_keys_to_remove.push_back(service_method_info_key); } } - for (const auto& service_method_info : service_method_infos_to_remove) + for (const auto& service_method_info_key : service_method_info_keys_to_remove) { - service_method_info_map_.map.erase(service_method_info); + service_method_info_map_.map.erase(service_method_info_key); } } } diff --git a/ecal/core/src/ecal_descgate.h b/ecal/core/src/ecal_descgate.h index c91b854be3..6224e4940d 100644 --- a/ecal/core/src/ecal_descgate.h +++ b/ecal/core/src/ecal_descgate.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,11 +23,11 @@ #pragma once +#include #include #include #include "serialization/ecal_struct_sample_registration.h" -#include "util/ecal_expmap.h" #include #include @@ -37,48 +37,30 @@ namespace eCAL { - struct STopicIdKey - { - std::string topic_name; - Util::TopicId topic_id; - - bool operator<(const STopicIdKey& other) const - { - return std::tie(topic_name, topic_id) < std::tie(other.topic_name, other.topic_id); - } - }; - - struct SServiceIdKey - { - std::string service_name; - std::string method_name; - Util::ServiceId service_id; - - bool operator<(const SServiceIdKey& other) const - { - return std::tie(service_name, method_name, service_id) < std::tie(other.service_name, other.method_name, other.service_id); - } - }; - - using QualityTopicIdMap = std::map; - using QualityServiceIdMap = std::map; - class CDescGate { public: - CDescGate(const std::chrono::milliseconds& exp_timeout_); + CDescGate(); ~CDescGate(); // apply samples to description gate void ApplySample(const Registration::Sample& sample_, eTLayerType layer_); - // get publisher/subscriber maps - Util::QualityTopicInfoMultiMap GetPublishers(); - Util::QualityTopicInfoMultiMap GetSubscribers(); + // get publisher information + std::set GetPublisherIDs() const; + bool GetPublisherInfo(const Registration::STopicId& id_, Registration::SQualityTopicInfo& topic_info_) const; - // get service/clients maps - Util::QualityServiceInfoMultimap GetServices(); - Util::QualityServiceInfoMultimap GetClients(); + // get subscriber information + std::set GetSubscriberIDs() const; + bool GetSubscriberInfo(const Registration::STopicId& id_, Registration::SQualityTopicInfo& topic_info_) const; + + // get service information + std::set GetServiceIDs() const; + bool GetServiceInfo(const Registration::SServiceId& id_, Registration::SQualityServiceInfo& service_info_) const; + + // get client information + std::set GetClientIDs() const; + bool GetClientInfo(const Registration::SServiceId& id_, Registration::SQualityServiceInfo& service_info_) const; // delete copy constructor and copy assignment operator CDescGate(const CDescGate&) = delete; @@ -89,47 +71,48 @@ namespace eCAL CDescGate& operator=(CDescGate&&) = delete; protected: - using QualityTopicIdExpMap = eCAL::Util::CExpMap; + using QualityTopicIdMap = std::map; struct SQualityTopicIdMap { - explicit SQualityTopicIdMap(const std::chrono::milliseconds& timeout_) : map(timeout_) {}; - mutable std::mutex mtx; - QualityTopicIdExpMap map; + mutable std::mutex mtx; + QualityTopicIdMap map; }; - using QualityServiceIdExpMap = eCAL::Util::CExpMap; + using QualityServiceIdMap = std::map; struct SQualityServiceIdMap { - explicit SQualityServiceIdMap(const std::chrono::milliseconds& timeout_) : map(timeout_) {}; - mutable std::mutex mtx; - QualityServiceIdExpMap map; + mutable std::mutex mtx; + QualityServiceIdMap map; }; - static Util::QualityTopicInfoMultiMap GetTopics (SQualityTopicIdMap& topic_info_map_); - static Util::QualityServiceInfoMultimap GetServices(SQualityServiceIdMap& service_method_info_map_); + static std::set GetTopicIDs(const SQualityTopicIdMap& topic_info_map_); + static bool GetTopic (const Registration::STopicId& id_, const SQualityTopicIdMap& topic_info_map_, Registration::SQualityTopicInfo& topic_info_); + + static std::set GetServiceIDs(const SQualityServiceIdMap& service_method_info_map_); + static bool GetService (const Registration::SServiceId& id_, const SQualityServiceIdMap& service_method_info_map_, Registration::SQualityServiceInfo& service_method_info_); static void ApplyTopicDescription(SQualityTopicIdMap& topic_info_map_, + const Registration::SampleIdentifier& topic_id_, const std::string& topic_name_, - const Util::TopicId& topic_id_, const SDataTypeInformation& topic_info_, - Util::DescQualityFlags topic_quality_); + Registration::DescQualityFlags topic_quality_); static void RemTopicDescription(SQualityTopicIdMap& topic_info_map_, - const std::string& topic_name_, - const Util::TopicId& topic_id_); + const Registration::SampleIdentifier& topic_id_, + const std::string& topic_name_); static void ApplyServiceDescription(SQualityServiceIdMap& service_method_info_map_, + const Registration::SampleIdentifier& service_id_, const std::string& service_name_, const std::string& method_name_, - const Util::ServiceId& service_id_, const SDataTypeInformation& request_type_information_, const SDataTypeInformation& response_type_information_, - Util::DescQualityFlags request_type_quality_, - Util::DescQualityFlags response_type_quality_); + Registration::DescQualityFlags request_type_quality_, + Registration::DescQualityFlags response_type_quality_); static void RemServiceDescription(SQualityServiceIdMap& service_method_info_map_, - const std::string& service_name_, - const Util::ServiceId& service_id_); + const Registration::SampleIdentifier& service_id_, + const std::string& service_name_); // internal quality topic info publisher/subscriber maps SQualityTopicIdMap m_publisher_info_map; diff --git a/ecal/core/src/ecal_global_accessors.cpp b/ecal/core/src/ecal_global_accessors.cpp index eea548714e..85024b1eef 100644 --- a/ecal/core/src/ecal_global_accessors.cpp +++ b/ecal/core/src/ecal_global_accessors.cpp @@ -21,10 +21,12 @@ * @brief eCAL core functions **/ +#include "ecal/config/configuration.h" + #include "ecal_global_accessors.h" #include "ecal_def.h" #include "ecal_globals.h" -#include "ecal/config/configuration.h" + #include #include @@ -51,7 +53,8 @@ namespace eCAL void InitGlobals() { - g_globals_ctx = new CGlobals; + if (g_globals_ctx == nullptr) + g_globals_ctx = new CGlobals; } void SetGlobalUnitName(const char *unit_name_) @@ -83,7 +86,7 @@ namespace eCAL } #endif } - }; + } CGlobals* g_globals() { diff --git a/ecal/core/src/ecal_globals.cpp b/ecal/core/src/ecal_globals.cpp index 088bfc60ec..5db8f5cff4 100644 --- a/ecal/core/src/ecal_globals.cpp +++ b/ecal/core/src/ecal_globals.cpp @@ -23,8 +23,6 @@ #include "ecal_globals.h" -#include "config/ecal_config_reader.h" - #include #include #include @@ -35,6 +33,9 @@ #include "service/ecal_service_singleton_manager.h" #endif +#include "builder/registration_attribute_builder.h" +#include "builder/monitoring_attribute_builder.h" + namespace eCAL { CGlobals::CGlobals() : initialized(false), components(0) @@ -51,12 +52,13 @@ namespace eCAL bool new_initialization(false); #if ECAL_CORE_REGISTRATION + const Registration::SAttributes registration_attr = BuildRegistrationAttributes(GetConfiguration().registration, GetConfiguration().transport_layer.udp, eCAL::Process::GetProcessID()); ///////////////////// // REGISTRATION PROVIDER ///////////////////// if (registration_provider_instance == nullptr) { - registration_provider_instance = std::make_unique(); + registration_provider_instance = std::make_unique(registration_attr); new_initialization = true; } @@ -65,7 +67,7 @@ namespace eCAL ///////////////////// if(registration_receiver_instance == nullptr) { - registration_receiver_instance = std::make_unique(); + registration_receiver_instance = std::make_unique(registration_attr); new_initialization = true; } #endif // ECAL_CORE_REGISTRATION @@ -76,7 +78,7 @@ namespace eCAL if (descgate_instance == nullptr) { // create description gate with configured expiration timeout - descgate_instance = std::make_unique(std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())); + descgate_instance = std::make_unique(); new_initialization = true; } @@ -176,7 +178,7 @@ namespace eCAL { if (monitoring_instance == nullptr) { - monitoring_instance = std::make_unique(); + monitoring_instance = std::make_unique(eCAL::Monitoring::BuildMonitoringAttributes(GetConfiguration().monitoring)); new_initialization = true; } } @@ -206,8 +208,7 @@ namespace eCAL if (descgate_instance) { #if ECAL_CORE_REGISTRATION - // utilize registration provider and receiver to get descriptions - g_registration_provider()->SetCustomApplySampleCallback("descgate", [](const auto& sample_) {g_descgate()->ApplySample(sample_, tl_none); }); + // utilize registration receiver to get descriptions g_registration_receiver()->SetCustomApplySampleCallback("descgate", [](const auto& sample_) {g_descgate()->ApplySample(sample_, tl_none); }); #endif } @@ -305,8 +306,7 @@ namespace eCAL if (descgate_instance) { #if ECAL_CORE_REGISTRATION - // stop registration provider and receiver utilization to get descriptions - g_registration_provider()->RemCustomApplySampleCallback("descgate"); + // stop registration receiver utilization to get descriptions g_registration_receiver()->RemCustomApplySampleCallback("descgate"); #endif } diff --git a/ecal/core/src/ecal_process.cpp b/ecal/core/src/ecal_process.cpp index 20c26b45e5..13dcb2feb9 100644 --- a/ecal/core/src/ecal_process.cpp +++ b/ecal/core/src/ecal_process.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,6 @@ #include "ecal_utils/command_line.h" #include "ecal_utils/str_convert.h" -#include "config/ecal_config_reader_hlp.h" #include "io/udp/ecal_udp_configurations.h" #include @@ -630,7 +629,7 @@ namespace // Check whether we are able to use a terminal emulator. The requirements // are: // - the DISPLAY variable must be set - // - the terminal_emulator must be set in the ecal.ini + // - the terminal_emulator must be set in the ecal.yaml // - ecal_process_stub bust be available AND print the correct version // ------------------------ DISPLAY variable check ------------------------- @@ -651,11 +650,11 @@ namespace const std::string terminal_emulator_command = eCAL::Config::GetTerminalEmulatorCommand(); if (!terminal_emulator_command.empty()) { - STD_COUT_DEBUG("[PID " << getpid() << "]: " << "ecal.ini terminal emulator command is: " << terminal_emulator_command << std::endl); + STD_COUT_DEBUG("[PID " << getpid() << "]: " << "ecal.yaml terminal emulator command is: " << terminal_emulator_command << std::endl); } else { - STD_COUT_DEBUG("[PID " << getpid() << "]: " << "ecal.ini terminal emulator command is not set. Not using terminal emulator." << std::endl); + STD_COUT_DEBUG("[PID " << getpid() << "]: " << "ecal.yaml terminal emulator command is not set. Not using terminal emulator." << std::endl); return ""; } @@ -996,7 +995,7 @@ namespace eCAL // terminal emulator, when: // - The process_mode_ is not set to hidden // - the DISPLAY variable indicates that we have a display attached - // - the terminal_emulator is set in the ecal.ini + // - the terminal_emulator is set in the ecal.yaml // - ecal_process_stub is available AND prints the correct version std::string terminal_emulator_command; diff --git a/ecal/core/src/ecal_util.cpp b/ecal/core/src/ecal_util.cpp index 4df41fb12a..6e11e4ecbd 100644 --- a/ecal/core/src/ecal_util.cpp +++ b/ecal/core/src/ecal_util.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,133 +29,6 @@ #include #include -namespace -{ - /** - * @brief Extract a set of all SQualityTopicInfo matching the given topic name. - * - * @param topic_name_ The topic name. - * @param quality_data_type_info_multi_map_ MultiMap { TopicName -> SQualityTopicInfo }. - * - * @return Set of SQualityTopicInfo - **/ - std::set GetQualityTopicInfoSet(const std::string& topic_name_, const eCAL::Util::QualityTopicInfoMultiMap& quality_data_type_info_multi_map_) - { - std::set quality_topic_info_set; - - const auto topic_info_range = quality_data_type_info_multi_map_.equal_range(topic_name_); - for (auto topic_info_range_it = topic_info_range.first; topic_info_range_it != topic_info_range.second; ++topic_info_range_it) - { - quality_topic_info_set.insert(topic_info_range_it->second); - } - - return quality_topic_info_set; - } - - /** - * @brief Extract a set of all SQualityServiceInfo matching the given service name/method name. - * - * @param service_name_ The service name. - * @param method_name_ The method name. - * @param quality_service_info_multi_map_ MultiMap { -> SQualityServiceInfo }. - * - * @return Set of SQualityServiceInfo - **/ - std::set GetQualityServiceInfoSet(const std::string& service_name_, const std::string& method_name_, const eCAL::Util::QualityServiceInfoMultimap& quality_service_info_multi_map_) - { - std::set quality_service_info_set; - - eCAL::Util::SServiceMethod key; - key.service_name = service_name_; - key.method_name = method_name_; - const auto service_info_range = quality_service_info_multi_map_.equal_range(key); - for (auto service_info_range_it = service_info_range.first; service_info_range_it != service_info_range.second; ++service_info_range_it) - { - quality_service_info_set.insert(service_info_range_it->second); - } - - return quality_service_info_set; - } - - /** - * @brief Reducing std::map<(TopicName, TopicID), SQualityTopicInfo> to - * std::map based on the quality - * - * @param source_map_ std::map<(TopicName, TopicID), SQualityTopicInfo>. - * - * @return std::map - **/ - std::map ReduceQualityTopicIdMap(const eCAL::Util::QualityTopicInfoMultiMap& source_map_) - { - std::map target_map; - - for (const auto& source_pair : source_map_) - { - const auto& source_key = source_pair.first; - const auto& source_value = source_pair.second; - - auto target_it = target_map.find(source_key); - if (target_it != target_map.end()) - { - // key exists in target map - if (source_value.quality > target_it->second.quality) - { - // source quality is greater, overwrite - target_it->second = source_value; - } - } - else - { - // key does not exist in target map, insert source pair - target_map.insert(std::make_pair(source_key, source_value)); - } - } - - return target_map; - } - - /** - * @brief Reducing std::map<(ServiceName, ServiceId, MethodName), SQualityServiceInfo> to - * std::map, SQualityServiceInfo> based on the quality - * - * @param source_map_ std::map<(ServiceName, ServiceId, MethodName), SQualityServiceInfo>. - * - * @return std::map, SQualityServiceInfo> - **/ - std::map ReduceQualityServiceIdMap(const eCAL::Util::QualityServiceInfoMultimap& source_map_) - { - std::map target_map; - - for (const auto& source_pair : source_map_) - { - const auto& source_key = source_pair.first; - const auto& source_value = source_pair.second; - - eCAL::Util::SServiceMethod target_key; - target_key.service_name = source_key.service_name; - target_key.method_name = source_key.method_name; - auto target_it = target_map.find(target_key); - if (target_it != target_map.end()) - { - // key exists in target map - if ( (source_value.request_quality > target_it->second.request_quality) - || (source_value.response_quality > target_it->second.response_quality)) - { - // source quality is greater, overwrite - target_it->second = source_value; - } - } - else - { - // key does not exist in target map, insert source pair - target_map.insert(std::make_pair(target_key, source_pair.second)); - } - } - - return target_map; - } -} - namespace eCAL { namespace Util @@ -296,242 +169,6 @@ namespace eCAL } #endif // ECAL_CORE_MONITORING - QualityTopicInfoMultiMap GetPublishers() - { - if (g_descgate() == nullptr) return QualityTopicInfoMultiMap(); - return g_descgate()->GetPublishers(); - } - - QualityTopicInfoSet GetPublishers(const std::string& topic_name_) - { - return ::GetQualityTopicInfoSet(topic_name_, GetPublishers()); - } - - QualityTopicInfoMultiMap GetSubscribers() - { - if (g_descgate() == nullptr) return QualityTopicInfoMultiMap(); - return g_descgate()->GetSubscribers(); - } - - QualityTopicInfoSet GetSubscribers(const std::string& topic_name_) - { - return ::GetQualityTopicInfoSet(topic_name_, GetSubscribers()); - } - - SDataTypeInformation GetHighestQualityDataTypeInformation(const QualityTopicInfoSet& quality_topic_info_set_) - { - SQualityTopicInfo highest_quality_topic_info; - for (const auto& info : quality_topic_info_set_) - { - if (info.quality > highest_quality_topic_info.quality) - { - highest_quality_topic_info = info; - } - } - return highest_quality_topic_info.info; - } - - QualityServiceInfoMultimap GetServices() - { - if (g_descgate() == nullptr) return QualityServiceInfoMultimap(); - return g_descgate()->GetServices(); - } - - QualityServiceInfoMultimap GetClients() - { - if (g_descgate() == nullptr) return QualityServiceInfoMultimap(); - return g_descgate()->GetClients(); - } - - SServiceMethodInformation GetHighestQualityServiceMethodInformation(const SQualityServiceInfoSet& quality_service_info_set_) - { - SQualityServiceInfo highest_quality_service_info; - for (const auto& info : quality_service_info_set_) - { - if ( (info.request_quality > highest_quality_service_info.request_quality) - || (info.response_quality > highest_quality_service_info.response_quality)) - { - highest_quality_service_info = info; - } - } - return highest_quality_service_info.info; - } - - void GetTopics(std::map& data_type_info_map_) - { - data_type_info_map_.clear(); - - std::map quality_data_type_info_map; - GetTopics(quality_data_type_info_map); - - // transform into target map - for (const auto& quality_data_type_info : quality_data_type_info_map) - { - data_type_info_map_.insert(std::pair(quality_data_type_info.first, quality_data_type_info.second.info)); - } - } - - void GetTopics(std::map& quality_topic_info_map_) - { - quality_topic_info_map_.clear(); - if (g_descgate() == nullptr) return; - - QualityTopicInfoMultiMap pub_sub_map = g_descgate()->GetPublishers(); - QualityTopicInfoMultiMap sub_map = g_descgate()->GetSubscribers(); - pub_sub_map.insert(sub_map.begin(), sub_map.end()); - - // transform into a map with the highest quality data type information - quality_topic_info_map_ = ReduceQualityTopicIdMap(pub_sub_map); - } - - void GetTopicNames(std::set& topic_names_) - { - topic_names_.clear(); - - // get publisher & subscriber multi maps - auto pub_multi_map = GetPublishers(); - auto sub_multi_map = GetSubscribers(); - - // filter out unique topic names into a set - for (const auto& publisher : pub_multi_map) - { - topic_names_.insert(publisher.first); - } - for (const auto& subscriber : sub_multi_map) - { - topic_names_.insert(subscriber.first); - } - } - - bool GetTopicDataTypeInformation(const std::string& topic_name_, SDataTypeInformation& data_type_info_) - { - auto info_set = GetPublishers(topic_name_); - const auto sub_info_set = GetSubscribers(topic_name_); - - info_set.insert(sub_info_set.begin(), sub_info_set.end()); - data_type_info_ = GetHighestQualityDataTypeInformation(info_set); - - return !info_set.empty(); - } - - void GetServices(std::map& service_method_info_map_) - { - service_method_info_map_.clear(); - - std::map quality_service_method_info_map; - GetServices(quality_service_method_info_map); - - // transform into target map - for (const auto& quality_service_method_info : quality_service_method_info_map) - { - service_method_info_map_.insert(std::pair(quality_service_method_info.first, quality_service_method_info.second.info)); - } - } - - void GetServices(std::map& quality_service_info_map_) - { - quality_service_info_map_.clear(); - if (g_descgate() == nullptr) return; - - // transform into a map with the highest quality service method information - quality_service_info_map_ = ReduceQualityServiceIdMap(g_descgate()->GetServices()); - } - - void GetServiceMethodNames(std::set& service_method_names_) - { - service_method_names_.clear(); - - // get services multi map - auto multi_map = GetServices(); - - // filter out unique service names into a set - for (const auto& service : multi_map) - { - service_method_names_.insert(service.first); - } - } - - bool GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_) - { - const auto service_method_info_set = GetQualityServiceInfoSet(service_name_, method_name_, GetServices()); - - const SServiceMethodInformation service_method_info = GetHighestQualityServiceMethodInformation(service_method_info_set); - req_type_ = service_method_info.request_type.name; - resp_type_ = service_method_info.response_type.name; - - return !service_method_info_set.empty(); - } - - bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_) - { - const auto service_method_info_set = GetQualityServiceInfoSet(service_name_, method_name_, GetServices()); - - const SServiceMethodInformation service_method_info = GetHighestQualityServiceMethodInformation(service_method_info_set); - req_desc_ = service_method_info.request_type.descriptor; - resp_desc_ = service_method_info.response_type.descriptor; - - return !service_method_info_set.empty(); - } - - void GetClients(std::map& client_method_info_map_) - { - client_method_info_map_.clear(); - - std::map quality_client_method_info_map_; - GetClients(quality_client_method_info_map_); - - // transform into target map - for (const auto& quality_client_method_info : quality_client_method_info_map_) - { - client_method_info_map_.insert(std::pair(quality_client_method_info.first, quality_client_method_info.second.info)); - } - } - - void GetClients(std::map& quality_client_info_map_) - { - quality_client_info_map_.clear(); - if (g_descgate() == nullptr) return; - - // transform into a map with the highest quality service method information - quality_client_info_map_ = ReduceQualityServiceIdMap(g_descgate()->GetClients()); - } - - void GetClientMethodNames(std::set& client_method_names_) - { - client_method_names_.clear(); - - // get services multi map - auto multi_map = GetClients(); - - // filter out unique service names into a set - for (const auto& service : multi_map) - { - client_method_names_.insert(service.first); - } - } - - bool GetClientTypeNames(const std::string& client_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_) - { - const auto service_method_info_set = GetQualityServiceInfoSet(client_name_, method_name_, GetClients()); - - const SServiceMethodInformation service_method_info = GetHighestQualityServiceMethodInformation(service_method_info_set); - req_type_ = service_method_info.request_type.name; - resp_type_ = service_method_info.response_type.name; - - return !service_method_info_set.empty(); - } - - bool GetClientDescription(const std::string& client_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_) - { - const auto service_method_info_set = GetQualityServiceInfoSet(client_name_, method_name_, GetClients()); - - const SServiceMethodInformation service_method_info = GetHighestQualityServiceMethodInformation(service_method_info_set); - req_desc_ = service_method_info.request_type.descriptor; - resp_desc_ = service_method_info.response_type.descriptor; - - return !service_method_info_set.empty(); - } - std::pair SplitCombinedTopicType(const std::string& combined_topic_type_) { auto pos = combined_topic_type_.find(':'); diff --git a/ecal/core/src/io/shm/ecal_memfile_pool.cpp b/ecal/core/src/io/shm/ecal_memfile_pool.cpp index 3e3a778e11..e2a1ed816b 100644 --- a/ecal/core/src/io/shm/ecal_memfile_pool.cpp +++ b/ecal/core/src/io/shm/ecal_memfile_pool.cpp @@ -211,14 +211,26 @@ namespace eCAL // ------------------------------------------------------------------------- if (zero_copy_allowed) { - // acquire memory file payload pointer (no copying here) - const void* buf(nullptr); - if (m_memfile.GetReadAddress(buf, mfile_hdr.data_size) > 0) + if (m_data_callback) { - // calculate data buffer offset - const char* data_buf = static_cast(buf) + mfile_hdr.hdr_size; - // add sample to data reader (and call user callback function) - if (m_data_callback) m_data_callback(topic_name_, topic_id_, data_buf, mfile_hdr.data_size, (long long)mfile_hdr.id, (long long)mfile_hdr.clock, (long long)mfile_hdr.time, (size_t)mfile_hdr.hash); + const char* data_buf = nullptr; + if (mfile_hdr.data_size > 0) + { + // acquire memory file payload pointer (no copying here) + const void* buf(nullptr); + if (m_memfile.GetReadAddress(buf, mfile_hdr.data_size) > 0) + { + // calculate user payload address + data_buf = static_cast(buf) + mfile_hdr.hdr_size; + // call user callback function + m_data_callback(topic_name_, topic_id_, data_buf, mfile_hdr.data_size, (long long)mfile_hdr.id, (long long)mfile_hdr.clock, (long long)mfile_hdr.time, (size_t)mfile_hdr.hash); + } + } + else + { + // call user callback function + m_data_callback(topic_name_, topic_id_, data_buf, mfile_hdr.data_size, (long long)mfile_hdr.id, (long long)mfile_hdr.clock, (long long)mfile_hdr.time, (size_t)mfile_hdr.hash); + } } } // ------------------------------------------------------------------------- diff --git a/ecal/core/src/io/udp/ecal_udp_configurations.cpp b/ecal/core/src/io/udp/ecal_udp_configurations.cpp index d3e81cf46a..ac29ca5950 100644 --- a/ecal/core/src/io/udp/ecal_udp_configurations.cpp +++ b/ecal/core/src/io/udp/ecal_udp_configurations.cpp @@ -175,5 +175,15 @@ namespace eCAL // if network is enabled, return the configured UDP multicast TTL value return Config::GetUdpMulticastTtl(); } + + int GetReceiveBufferSize() + { + return Config::GetUdpMulticastRcvBufSizeBytes(); + } + + int GetSendBufferSize() + { + return Config::GetUdpMulticastSndBufSizeBytes(); + } } } diff --git a/ecal/core/src/io/udp/ecal_udp_configurations.h b/ecal/core/src/io/udp/ecal_udp_configurations.h index 62d919a547..988b168ecc 100644 --- a/ecal/core/src/io/udp/ecal_udp_configurations.h +++ b/ecal/core/src/io/udp/ecal_udp_configurations.h @@ -147,5 +147,20 @@ namespace eCAL * @return The TTL value for UDP multicast communication based on the network configuration. */ int GetMulticastTtl(); + + /** + * @brief GetReceiveBufferSize retrieves the receive buffer size for UDP multicast communication. + * + * @return The receive buffer size in bytes. + */ + int GetReceiveBufferSize(); + + /** + * @brief GetSendBufferSize retrieves the send buffer size for UDP multicast communication. + * + * @return The send buffer size in bytes. + */ + int GetSendBufferSize(); + } } diff --git a/ecal/core/src/logging/ecal_log_impl.cpp b/ecal/core/src/logging/ecal_log_impl.cpp index 3a8ee3064e..17da3e854e 100644 --- a/ecal/core/src/logging/ecal_log_impl.cpp +++ b/ecal/core/src/logging/ecal_log_impl.cpp @@ -145,7 +145,7 @@ namespace eCAL attr.ttl = UDP::GetMulticastTtl(); attr.broadcast = UDP::IsBroadcast(); attr.loopback = true; - attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); + attr.sndbuf = UDP::GetSendBufferSize(); // create udp logging sender m_udp_logging_sender = std::make_unique(attr); @@ -157,7 +157,7 @@ namespace eCAL attr.port = UDP::GetLoggingPort(); attr.broadcast = UDP::IsBroadcast(); attr.loopback = true; - attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); + attr.rcvbuf = UDP::GetReceiveBufferSize(); // start logging receiver m_log_receiver = std::make_shared(attr, std::bind(&CLog::HasSample, this, std::placeholders::_1), std::bind(&CLog::ApplySample, this, std::placeholders::_1, std::placeholders::_2)); diff --git a/ecal/core/src/pubsub/ecal_subscriber_config.cpp b/ecal/core/src/monitoring/attributes/monitoring_attributes.h similarity index 78% rename from ecal/core/src/pubsub/ecal_subscriber_config.cpp rename to ecal/core/src/monitoring/attributes/monitoring_attributes.h index 1ecdea261f..4f9d071c25 100644 --- a/ecal/core/src/pubsub/ecal_subscriber_config.cpp +++ b/ecal/core/src/monitoring/attributes/monitoring_attributes.h @@ -17,20 +17,20 @@ * ========================= eCAL LICENSE ================================= */ -/** - * @brief eCAL subscriber configuration -**/ +#pragma once -#include -#include +#include namespace eCAL { - namespace Subscriber + namespace Monitoring { - Configuration::Configuration() + struct SAttributes { - *this = GetConfiguration().subscriber; - } + unsigned int timeout; + std::string filter_excl; + std::string filter_incl; + }; + } -} +} \ No newline at end of file diff --git a/ecal/core/src/monitoring/ecal_monitoring_def.cpp b/ecal/core/src/monitoring/ecal_monitoring_def.cpp index e5f115fa4a..bd17ea16be 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_def.cpp +++ b/ecal/core/src/monitoring/ecal_monitoring_def.cpp @@ -29,9 +29,9 @@ namespace eCAL { - CMonitoring::CMonitoring() + CMonitoring::CMonitoring(const Monitoring::SAttributes& attr_) { - m_monitoring_impl = std::make_unique(); + m_monitoring_impl = std::make_unique(attr_); } CMonitoring::~CMonitoring() diff --git a/ecal/core/src/monitoring/ecal_monitoring_def.h b/ecal/core/src/monitoring/ecal_monitoring_def.h index ca567d0c1e..f903ff32fe 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_def.h +++ b/ecal/core/src/monitoring/ecal_monitoring_def.h @@ -25,6 +25,8 @@ #include +#include "attributes/monitoring_attributes.h" + #include #include @@ -37,7 +39,7 @@ namespace eCAL class CMonitoring { public: - CMonitoring(); + CMonitoring(const Monitoring::SAttributes& attr_); ~CMonitoring(); void Start(); diff --git a/ecal/core/src/monitoring/ecal_monitoring_impl.cpp b/ecal/core/src/monitoring/ecal_monitoring_impl.cpp index 73fac8cc29..f5b397de53 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_impl.cpp +++ b/ecal/core/src/monitoring/ecal_monitoring_impl.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,8 @@ #include #include "io/udp/ecal_udp_configurations.h" -#include "config/ecal_config_reader_hlp.h" #include "ecal_monitoring_impl.h" +#include "ecal_global_accessors.h" #include @@ -41,13 +41,9 @@ namespace eCAL //////////////////////////////////////// // Monitoring Implementation //////////////////////////////////////// - CMonitoringImpl::CMonitoringImpl() : + CMonitoringImpl::CMonitoringImpl(const Monitoring::SAttributes& attr_) : m_init(false), - m_process_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_publisher_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_subscriber_map(std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_server_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())), - m_clients_map (std::chrono::milliseconds(Config::GetMonitoringTimeoutMs())) + m_attributes(attr_) { } @@ -55,16 +51,11 @@ namespace eCAL { if (m_init) return; - // get name of this host - m_host_name = Process::GetHostName(); + // enable loopback to monitor process internal entities as well + eCAL::Util::EnableLoopback(true); - // utilize registration provider and receiver to enrich monitor information - g_registration_provider()->SetCustomApplySampleCallback("monitoring", [this](const auto& sample_) {this->ApplySample(sample_, tl_none); }); - g_registration_receiver()->SetCustomApplySampleCallback("monitoring", [this](const auto& sample_){this->ApplySample(sample_, tl_none);}); - - // setup blacklist and whitelist filter strings# - m_topic_filter_excl_s = Config::GetMonitoringFilterExcludeList(); - m_topic_filter_incl_s = Config::GetMonitoringFilterIncludeList(); + // utilize registration receiver to enrich monitor information + g_registration_receiver()->SetCustomApplySampleCallback("monitoring", [this](const auto& sample_){this->ApplySample(sample_, tl_none);}); // setup filtering on by default SetFilterState(true); @@ -74,20 +65,19 @@ namespace eCAL void CMonitoringImpl::Destroy() { - // stop registration provider and receiver utilization to enrich monitor information - g_registration_provider()->RemCustomApplySampleCallback("monitoring"); + // stop registration receiver utilization to enrich monitor information g_registration_receiver()->RemCustomApplySampleCallback("monitoring"); m_init = false; } void CMonitoringImpl::SetExclFilter(const std::string& filter_) { - m_topic_filter_excl_s = filter_; + m_attributes.filter_excl = filter_; } void CMonitoringImpl::SetInclFilter(const std::string& filter_) { - m_topic_filter_incl_s = filter_; + m_attributes.filter_incl = filter_; } void CMonitoringImpl::SetFilterState(bool state_) @@ -97,13 +87,13 @@ namespace eCAL // create excluding filter list { const std::lock_guard lock(m_topic_filter_excl_mtx); - Tokenize(m_topic_filter_excl_s, m_topic_filter_excl, ",;", true); + Tokenize(m_attributes.filter_excl, m_topic_filter_excl, ",;", true); } // create including filter list { const std::lock_guard lock(m_topic_filter_incl_mtx); - Tokenize(m_topic_filter_incl_s, m_topic_filter_incl, ",;", true); + Tokenize(m_attributes.filter_incl, m_topic_filter_incl, ",;", true); } } else @@ -199,7 +189,7 @@ namespace eCAL bool CMonitoringImpl::RegisterTopic(const Registration::Sample& sample_, enum ePubSub pubsub_type_) { const auto& sample_topic = sample_.topic; - const int process_id = sample_topic.pid; + const int process_id = sample_.identifier.process_id; const std::string& topic_name = sample_topic.tname; const int32_t topic_size = sample_topic.tsize; bool topic_tlayer_ecal_udp(false); @@ -207,9 +197,9 @@ namespace eCAL bool topic_tlayer_ecal_tcp(false); for (const auto& layer : sample_topic.tlayer) { - topic_tlayer_ecal_udp |= (layer.type == tl_ecal_udp) && layer.confirmed; - topic_tlayer_ecal_shm |= (layer.type == tl_ecal_shm) && layer.confirmed; - topic_tlayer_ecal_tcp |= (layer.type == tl_ecal_tcp) && layer.confirmed; + topic_tlayer_ecal_udp |= (layer.type == tl_ecal_udp) && layer.active; + topic_tlayer_ecal_shm |= (layer.type == tl_ecal_shm) && layer.active; + topic_tlayer_ecal_tcp |= (layer.type == tl_ecal_tcp) && layer.active; } const int32_t connections_loc = sample_topic.connections_loc; const int32_t connections_ext = sample_topic.connections_ext; @@ -255,11 +245,11 @@ namespace eCAL const std::lock_guard lock(pTopicMap->sync); // common infos - const std::string& host_name = sample_topic.hname; + const std::string& host_name = sample_.identifier.host_name; const std::string& host_group_name = sample_topic.hgname; const std::string& process_name = sample_topic.pname; const std::string& unit_name = sample_topic.uname; - const std::string& topic_id = sample_topic.tid; + const std::string& topic_id = sample_.identifier.entity_id; std::string direction; switch (pubsub_type_) { @@ -305,22 +295,22 @@ namespace eCAL // tlayer udp_mc { eCAL::Monitoring::TLayer tlayer; - tlayer.type = eCAL::Monitoring::tl_ecal_udp_mc; - tlayer.confirmed = topic_tlayer_ecal_udp; + tlayer.type = eCAL::Monitoring::tl_ecal_udp_mc; + tlayer.active = topic_tlayer_ecal_udp; TopicInfo.tlayer.push_back(tlayer); } // tlayer shm { eCAL::Monitoring::TLayer tlayer; - tlayer.type = eCAL::Monitoring::tl_ecal_shm; - tlayer.confirmed = topic_tlayer_ecal_shm; + tlayer.type = eCAL::Monitoring::tl_ecal_shm; + tlayer.active = topic_tlayer_ecal_shm; TopicInfo.tlayer.push_back(tlayer); } // tlayer tcp { eCAL::Monitoring::TLayer tlayer; - tlayer.type = eCAL::Monitoring::tl_ecal_tcp; - tlayer.confirmed = topic_tlayer_ecal_tcp; + tlayer.type = eCAL::Monitoring::tl_ecal_tcp; + tlayer.active = topic_tlayer_ecal_tcp; TopicInfo.tlayer.push_back(tlayer); } @@ -340,7 +330,7 @@ namespace eCAL { const auto& sample_topic = sample_.topic; const std::string& topic_name = sample_topic.tname; - const std::string& topic_id = sample_topic.tid; + const std::string& topic_id = sample_.identifier.entity_id; // unregister from topic map STopicMonMap* pTopicMap = GetMap(pubsub_type_); @@ -360,10 +350,10 @@ namespace eCAL bool CMonitoringImpl::RegisterProcess(const Registration::Sample& sample_) { const auto& sample_process = sample_.process; - const std::string& host_name = sample_process.hname; + const std::string& host_name = sample_.identifier.host_name; const std::string& host_group_name = sample_process.hgname; const std::string& process_name = sample_process.pname; - const int process_id = sample_process.pid; + const int process_id = sample_.identifier.process_id; const std::string& process_param = sample_process.pparam; const std::string& unit_name = sample_process.uname; const auto& sample_process_state = sample_process.state; @@ -411,7 +401,7 @@ namespace eCAL { const auto& sample_process = sample_.process; const std::string& process_name = sample_process.pname; - const int process_id = sample_process.pid; + const int process_id = sample_.identifier.process_id; // create map key const std::string process_name_id = process_name + std::to_string(process_id); @@ -427,13 +417,16 @@ namespace eCAL bool CMonitoringImpl::RegisterServer(const Registration::Sample& sample_) { + const auto& sample_identifier = sample_.identifier; + const std::string& service_id = sample_identifier.entity_id; + const int32_t process_id = sample_identifier.process_id; + const std::string& host_name = sample_identifier.host_name; + const auto& sample_service = sample_.service; - const std::string& host_name = sample_service.hname; const std::string& service_name = sample_service.sname; - const std::string& service_id = sample_service.sid; const std::string& process_name = sample_service.pname; const std::string& unit_name = sample_service.uname; - const int32_t process_id = sample_service.pid; + const uint32_t tcp_port_v0 = sample_service.tcp_port_v0; const uint32_t tcp_port_v1 = sample_service.tcp_port_v1; @@ -476,10 +469,12 @@ namespace eCAL bool CMonitoringImpl::UnregisterServer(const Registration::Sample& sample_) { - const auto& sample_service = sample_.service; + const auto& sample_service = sample_.service; + const auto& sample_identifier = sample_.identifier; + const std::string& service_name = sample_service.sname; - const std::string& service_id = sample_service.sid; - const int process_id = sample_service.pid; + const std::string& service_id = sample_identifier.entity_id; + const int process_id = sample_identifier.process_id; // create map key const std::string service_name_id = service_name + service_id + std::to_string(process_id); @@ -495,13 +490,15 @@ namespace eCAL bool CMonitoringImpl::RegisterClient(const Registration::Sample& sample_) { + const auto& sample_identifier = sample_.identifier; + const std::string& service_id = sample_identifier.entity_id; + const int32_t process_id = sample_identifier.process_id; + const std::string& host_name = sample_identifier.host_name; + const auto& sample_client = sample_.client; - const std::string& host_name = sample_client.hname; const std::string& service_name = sample_client.sname; - const std::string& service_id = sample_client.sid; const std::string& process_name = sample_client.pname; const std::string& unit_name = sample_client.uname; - const int process_id = sample_client.pid; // create map key const std::string service_name_id = service_name + service_id + std::to_string(process_id); @@ -540,10 +537,12 @@ namespace eCAL bool CMonitoringImpl::UnregisterClient(const Registration::Sample& sample_) { + const auto& sample_identifier = sample_.identifier; + const std::string& service_id = sample_identifier.entity_id; + const int32_t process_id = sample_identifier.process_id; + const auto& sample_client = sample_.client; const std::string& service_name = sample_client.sname; - const std::string& service_id = sample_client.sid; - const int process_id = sample_client.pid; // create map key const std::string service_name_id = service_name + service_id + std::to_string(process_id); @@ -619,7 +618,6 @@ namespace eCAL monitoring_.processes.reserve(m_process_map.map->size()); // iterate map - m_process_map.map->remove_deprecated(); for (const auto& process : (*m_process_map.map)) { monitoring_.processes.emplace_back(process.second); @@ -637,7 +635,6 @@ namespace eCAL monitoring_.publisher.reserve(m_publisher_map.map->size()); // iterate map - m_publisher_map.map->remove_deprecated(); for (const auto& publisher : (*m_publisher_map.map)) { monitoring_.publisher.emplace_back(publisher.second); @@ -655,7 +652,6 @@ namespace eCAL monitoring_.subscriber.reserve(m_subscriber_map.map->size()); // iterate map - m_subscriber_map.map->remove_deprecated(); for (const auto& subscriber : (*m_subscriber_map.map)) { monitoring_.subscriber.emplace_back(subscriber.second); @@ -673,7 +669,6 @@ namespace eCAL monitoring_.server.reserve(m_server_map.map->size()); // iterate map - m_server_map.map->remove_deprecated(); for (const auto& server : (*m_server_map.map)) { monitoring_.server.emplace_back(server.second); @@ -691,7 +686,6 @@ namespace eCAL monitoring_.clients.reserve(m_clients_map.map->size()); // iterate map - m_clients_map.map->remove_deprecated(); for (const auto& client : (*m_clients_map.map)) { monitoring_.clients.emplace_back(client.second); @@ -705,7 +699,6 @@ namespace eCAL const std::lock_guard lock(m_process_map.sync); // iterate map - m_process_map.map->remove_deprecated(); for (const auto& process : (*m_process_map.map)) { // add process @@ -719,7 +712,6 @@ namespace eCAL const std::lock_guard lock(m_server_map.sync); // iterate map - m_server_map.map->remove_deprecated(); for (const auto& server : (*m_server_map.map)) { // add service @@ -733,7 +725,6 @@ namespace eCAL const std::lock_guard lock(m_clients_map.sync); // iterate map - m_clients_map.map->remove_deprecated(); for (const auto& client : (*m_clients_map.map)) { // add client @@ -747,7 +738,6 @@ namespace eCAL const std::lock_guard lock(map_.sync); // iterate map - map_.map->remove_deprecated(); for (const auto& topic : (*map_.map)) { if (direction_ == "publisher") diff --git a/ecal/core/src/monitoring/ecal_monitoring_impl.h b/ecal/core/src/monitoring/ecal_monitoring_impl.h index bfce029004..5325d8f44d 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_impl.h +++ b/ecal/core/src/monitoring/ecal_monitoring_impl.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,12 @@ #include #include "ecal_def.h" -#include "util/ecal_expmap.h" +#include "attributes/monitoring_attributes.h" #include "serialization/ecal_serialize_sample_registration.h" #include +#include #include #include #include @@ -47,7 +48,7 @@ namespace eCAL class CMonitoringImpl { public: - CMonitoringImpl(); + CMonitoringImpl(const Monitoring::SAttributes& attr_); ~CMonitoringImpl() = default; void Create(); @@ -81,44 +82,44 @@ namespace eCAL bool RegisterTopic(const Registration::Sample& sample_, enum ePubSub pubsub_type_); bool UnregisterTopic(const Registration::Sample& sample_, enum ePubSub pubsub_type_); - using TopicMonMapT = Util::CExpMap; + using TopicMonMapT = std::map; struct STopicMonMap { - explicit STopicMonMap(const std::chrono::milliseconds& timeout_) : - map(std::make_unique(timeout_)) + explicit STopicMonMap() : + map(std::make_unique()) { }; std::mutex sync; std::unique_ptr map; }; - using ProcessMonMapT = Util::CExpMap; + using ProcessMonMapT = std::map; struct SProcessMonMap { - explicit SProcessMonMap(const std::chrono::milliseconds& timeout_) : - map(std::make_unique(timeout_)) + explicit SProcessMonMap() : + map(std::make_unique()) { }; std::mutex sync; std::unique_ptr map; }; - using ServerMonMapT = Util::CExpMap; + using ServerMonMapT = std::map; struct SServerMonMap { - explicit SServerMonMap(const std::chrono::milliseconds& timeout_) : - map(std::make_unique(timeout_)) + explicit SServerMonMap() : + map(std::make_unique()) { }; std::mutex sync; std::unique_ptr map; }; - using ClientMonMapT = Util::CExpMap; + using ClientMonMapT = std::map; struct SClientMonMap { - explicit SClientMonMap(const std::chrono::milliseconds& timeout_) : - map(std::make_unique(timeout_)) + explicit SClientMonMap() : + map(std::make_unique()) { }; std::mutex sync; @@ -149,14 +150,13 @@ namespace eCAL void Tokenize(const std::string& str, StrICaseSetT& tokens, const std::string& delimiters, bool trimEmpty); bool m_init; - std::string m_host_name; + + Monitoring::SAttributes m_attributes; std::mutex m_topic_filter_excl_mtx; - std::string m_topic_filter_excl_s; StrICaseSetT m_topic_filter_excl; std::mutex m_topic_filter_incl_mtx; - std::string m_topic_filter_incl_s; StrICaseSetT m_topic_filter_incl; // database diff --git a/ecal/core/src/pubsub/ecal_pubgate.cpp b/ecal/core/src/pubsub/ecal_pubgate.cpp index cd624626d6..671b559210 100644 --- a/ecal/core/src/pubsub/ecal_pubgate.cpp +++ b/ecal/core/src/pubsub/ecal_pubgate.cpp @@ -120,27 +120,27 @@ namespace eCAL // check topic name if (topic_name.empty()) return; - CDataWriter::SSubscriptionInfo subscription_info; - subscription_info.host_name = ecal_topic.hname; - subscription_info.topic_id = ecal_topic.tid; - subscription_info.process_id = ecal_topic.pid; + // TODO: Substitute ProducerInfo type + const auto& subscription_info = ecal_sample_.identifier; const SDataTypeInformation topic_information = ecal_topic.tdatatype; CDataWriter::SLayerStates layer_states; for (const auto& layer : ecal_topic.tlayer) { - if (layer.confirmed) + // transport layer versions 0 and 1 did not support dynamic layer enable feature + // so we set assume layer is enabled if we receive a registration in this case + if (layer.enabled || (layer.version < 2)) { switch (layer.type) { case TLayer::tlayer_udp_mc: - layer_states.udp = true; + layer_states.udp.read_enabled = true; break; case TLayer::tlayer_shm: - layer_states.shm = true; + layer_states.shm.read_enabled = true; break; case TLayer::tlayer_tcp: - layer_states.tcp = true; + layer_states.tcp.read_enabled = true; break; default: break; @@ -178,10 +178,7 @@ namespace eCAL // check topic name if (topic_name.empty()) return; - CDataWriter::SSubscriptionInfo subscription_info; - subscription_info.host_name = ecal_topic.hname; - subscription_info.topic_id = ecal_topic.tid; - subscription_info.process_id = ecal_topic.pid; + const auto& subscription_info = ecal_sample_.identifier; // unregister subscriber const std::shared_lock lock(m_topic_name_datawriter_sync); @@ -192,16 +189,15 @@ namespace eCAL } } - void CPubGate::RefreshRegistrations() + void CPubGate::GetRegistrations(Registration::SampleList& reg_sample_list_) { if (!m_created) return; - // refresh publisher registrations + // read reader registrations const std::shared_lock lock(m_topic_name_datawriter_sync); for (const auto& iter : m_topic_name_datawriter_map) { - // force data writer to (re)register itself on registration provider - iter.second->RefreshRegistration(); + reg_sample_list_.samples.emplace_back(iter.second->GetRegistration()); } } } diff --git a/ecal/core/src/pubsub/ecal_pubgate.h b/ecal/core/src/pubsub/ecal_pubgate.h index 2714bbeca0..0db416cc06 100644 --- a/ecal/core/src/pubsub/ecal_pubgate.h +++ b/ecal/core/src/pubsub/ecal_pubgate.h @@ -56,7 +56,7 @@ namespace eCAL void ApplySubRegistration(const Registration::Sample& ecal_sample_); void ApplySubUnregistration(const Registration::Sample& ecal_sample_); - void RefreshRegistrations(); + void GetRegistrations(Registration::SampleList& reg_sample_list_); protected: static std::atomic m_created; diff --git a/ecal/core/src/pubsub/ecal_subgate.cpp b/ecal/core/src/pubsub/ecal_subgate.cpp index 44261b5e63..314d4b005a 100644 --- a/ecal/core/src/pubsub/ecal_subgate.cpp +++ b/ecal/core/src/pubsub/ecal_subgate.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,10 +52,6 @@ namespace eCAL void CSubGate::Start() { if(m_created) return; - - // initialize data reader layers - CDataReader::InitializeLayers(); - m_created = true; } @@ -166,7 +162,7 @@ namespace eCAL const auto& ecal_sample_content = ecal_sample.content; for (const auto& reader : readers_to_apply) { - applied_size = reader->AddSample( + applied_size = reader->ApplySample( ecal_sample.topic.tid, payload_addr, payload_size, @@ -207,7 +203,7 @@ namespace eCAL for (const auto& reader : readers_to_apply) { - applied_size = reader->AddSample(topic_id_, buf_, len_, id_, clock_, time_, hash_, layer_); + applied_size = reader->ApplySample(topic_id_, buf_, len_, id_, clock_, time_, hash_, layer_); } return (applied_size > 0); @@ -223,27 +219,26 @@ namespace eCAL // check topic name if (topic_name.empty()) return; - CDataReader::SPublicationInfo publication_info; - publication_info.host_name = ecal_topic.hname; - publication_info.topic_id = ecal_topic.tid; - publication_info.process_id = ecal_topic.pid; + const auto& publication_info = ecal_sample_.identifier; const SDataTypeInformation topic_information = ecal_topic.tdatatype; CDataReader::SLayerStates layer_states; for (const auto& layer : ecal_topic.tlayer) { - if (layer.confirmed) + // transport layer versions 0 and 1 did not support dynamic layer enable feature + // so we set assume layer is enabled if we receive a registration in this case + if (layer.enabled || layer.version < 2) { switch (layer.type) { case TLayer::tlayer_udp_mc: - layer_states.udp = true; + layer_states.udp.write_enabled = true; break; case TLayer::tlayer_shm: - layer_states.shm = true; + layer_states.shm.write_enabled = true; break; case TLayer::tlayer_tcp: - layer_states.tcp = true; + layer_states.tcp.write_enabled = true; break; default: break; @@ -261,8 +256,16 @@ namespace eCAL { iter->second->ApplyLayerParameter(publication_info, tlayer.type, tlayer.par_layer); } - // inform for publisher connection - iter->second->ApplyPublication(publication_info, topic_information, layer_states); + // we only inform the subscriber when the publisher has already recognized at least one subscriber + // this should avoid to set the "IsPublished" state before the publisher is able to send data + const bool local_publication = publication_info.host_name == Process::GetHostName(); + const bool external_publication = !local_publication; + const bool local_confirmed = local_publication && (ecal_sample_.topic.connections_loc > 0); + const bool external_confirmed = external_publication && (ecal_sample_.topic.connections_ext > 0); + if(local_confirmed || external_confirmed) + { + iter->second->ApplyPublication(publication_info, topic_information, layer_states); + } } } @@ -276,10 +279,7 @@ namespace eCAL // check topic name if (topic_name.empty()) return; - CDataReader::SPublicationInfo publication_info; - publication_info.host_name = ecal_topic.hname; - publication_info.topic_id = ecal_topic.tid; - publication_info.process_id = ecal_topic.pid; + const auto& publication_info = ecal_sample_.identifier; // unregister publisher const std::shared_lock lock(m_topic_name_datareader_sync); @@ -290,16 +290,15 @@ namespace eCAL } } - void CSubGate::RefreshRegistrations() + void CSubGate::GetRegistrations(Registration::SampleList& reg_sample_list_) { if (!m_created) return; - // refresh reader registrations + // read reader registrations const std::shared_lock lock(m_topic_name_datareader_sync); for (const auto& iter : m_topic_name_datareader_map) { - // force data reader to (re)register itself on registration provider - iter.second->RefreshRegistration(); + reg_sample_list_.samples.emplace_back(iter.second->GetRegistration()); } } } diff --git a/ecal/core/src/pubsub/ecal_subgate.h b/ecal/core/src/pubsub/ecal_subgate.h index 1e78578a71..cc1b589c76 100644 --- a/ecal/core/src/pubsub/ecal_subgate.h +++ b/ecal/core/src/pubsub/ecal_subgate.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ namespace eCAL void ApplyPubRegistration(const Registration::Sample& ecal_sample_); void ApplyPubUnregistration(const Registration::Sample& ecal_sample_); - void RefreshRegistrations(); + void GetRegistrations(Registration::SampleList& reg_sample_list_); protected: static std::atomic m_created; diff --git a/ecal/core/src/pubsub/ecal_subscriber.cpp b/ecal/core/src/pubsub/ecal_subscriber.cpp index 0086f32afa..4e4366050b 100644 --- a/ecal/core/src/pubsub/ecal_subscriber.cpp +++ b/ecal/core/src/pubsub/ecal_subscriber.cpp @@ -81,7 +81,7 @@ namespace eCAL if (topic_name_.empty()) return(false); // create datareader - m_datareader = std::make_shared(topic_name_, data_type_info_); + m_datareader = std::make_shared(topic_name_, data_type_info_, config_); // register datareader g_subgate()->Register(topic_name_, m_datareader); @@ -143,7 +143,7 @@ namespace eCAL bool CSubscriber::ReceiveBuffer(std::string& buf_, long long* time_ /* = nullptr */, int rcv_timeout_ /* = 0 */) const { if (!m_created) return(false); - return(m_datareader->Receive(buf_, time_, rcv_timeout_)); + return(m_datareader->Read(buf_, time_, rcv_timeout_)); } bool CSubscriber::AddReceiveCallback(ReceiveCallbackT callback_) @@ -172,6 +172,12 @@ namespace eCAL return(m_datareader->RemEventCallback(type_)); } + bool CSubscriber::IsPublished() const + { + if (m_datareader == nullptr) return(false); + return(m_datareader->IsPublished()); + } + size_t CSubscriber::GetPublisherCount() const { if (m_datareader == nullptr) return(0); diff --git a/ecal/core/src/readwrite/ecal_reader.cpp b/ecal/core/src/readwrite/ecal_reader.cpp index 9fedd736f9..122db1b657 100644 --- a/ecal/core/src/readwrite/ecal_reader.cpp +++ b/ecal/core/src/readwrite/ecal_reader.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,20 +21,18 @@ * @brief common eCAL data reader **/ -#include -#include #include -#include -#include -#include -#include +#include +#include #if ECAL_CORE_REGISTRATION #include "registration/ecal_registration_provider.h" #endif + #include "ecal_reader.h" #include "ecal_global_accessors.h" #include "ecal_reader_layer.h" +#include "ecal_transport_layer.h" #if ECAL_CORE_TRANSPORT_UDP #include "udp/ecal_reader_udp.h" @@ -49,9 +47,14 @@ #endif #include +#include +#include #include +#include +#include +#include #include -#include +#include #include namespace eCAL @@ -59,7 +62,7 @@ namespace eCAL //////////////////////////////////////// // CDataReader //////////////////////////////////////// - CDataReader::CDataReader(const std::string& topic_name_, const SDataTypeInformation& topic_info_) : + CDataReader::CDataReader(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Subscriber::Configuration& config_) : m_host_name(Process::GetHostName()), m_host_group_name(Process::GetHostGroupName()), m_pid(Process::GetProcessID()), @@ -67,6 +70,7 @@ namespace eCAL m_topic_name(topic_name_), m_topic_info(topic_info_), m_topic_size(0), + m_config(config_), m_connected(false), m_receive_time(0), m_clock(0), @@ -85,18 +89,12 @@ namespace eCAL counter << std::chrono::steady_clock::now().time_since_epoch().count(); m_topic_id = counter.str(); - // set registration expiration - const std::chrono::milliseconds registration_timeout(Config::GetRegistrationTimeoutMs()); - m_pub_map.set_expiration(registration_timeout); - // start transport layers - SubscribeToLayers(); + InitializeLayers(); + StartTransportLayer(); // mark as created m_created = true; - - // register - Register(false); } CDataReader::~CDataReader() @@ -118,7 +116,7 @@ namespace eCAL #endif // stop transport layers - UnsubscribeFromLayers(); + StopTransportLayer(); // reset receive callback { @@ -141,181 +139,117 @@ namespace eCAL return true; } - void CDataReader::InitializeLayers() + bool CDataReader::Read(std::string& buf_, long long* time_ /* = nullptr */, int rcv_timeout_ms_ /* = 0 */) { - // initialize udp multicast layer -#if ECAL_CORE_TRANSPORT_UDP - if (Config::IsUdpMulticastRecEnabled()) - { - CUDPReaderLayer::Get()->Initialize(); - } -#endif + if (!m_created) return(false); - // initialize tcp layer -#if ECAL_CORE_TRANSPORT_TCP - if (Config::IsTcpRecEnabled()) - { - CTCPReaderLayer::Get()->Initialize(); - } -#endif - } + std::unique_lock read_buffer_lock(m_read_buf_mtx); - void CDataReader::SubscribeToLayers() - { - // subscribe topic to udp multicast layer -#if ECAL_CORE_TRANSPORT_UDP - if (Config::IsUdpMulticastRecEnabled()) + // No need to wait (for whatever time) if something has been received + if (!m_read_buf_received) { - CUDPReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id); + if (rcv_timeout_ms_ < 0) + { + m_read_buf_cv.wait(read_buffer_lock, [this]() { return this->m_read_buf_received; }); + } + else if (rcv_timeout_ms_ > 0) + { + m_read_buf_cv.wait_for(read_buffer_lock, std::chrono::milliseconds(rcv_timeout_ms_), [this]() { return this->m_read_buf_received; }); + } } -#endif - // subscribe topic to tcp layer -#if ECAL_CORE_TRANSPORT_TCP - if (Config::IsTcpRecEnabled()) - { - CTCPReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id); - } -#endif - } - - void CDataReader::UnsubscribeFromLayers() - { - // unsubscribe topic from udp multicast layer -#if ECAL_CORE_TRANSPORT_UDP - if (Config::IsUdpMulticastRecEnabled()) + // did we receive new samples ? + if (m_read_buf_received) { - CUDPReaderLayer::Get()->RemSubscription(m_host_name, m_topic_name, m_topic_id); - } +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_topic_name + "::CDataReader::Receive"); #endif + // copy content to target string + buf_.clear(); + buf_.swap(m_read_buf); + m_read_buf_received = false; - // unsubscribe topic from tcp multicast layer -#if ECAL_CORE_TRANSPORT_TCP - if (Config::IsTcpRecEnabled()) - { - CTCPReaderLayer::Get()->RemSubscription(m_host_name, m_topic_name, m_topic_id); + // apply time + if (time_ != nullptr) *time_ = m_read_time; + + // return success + return(true); } -#endif + + return(false); } - bool CDataReader::Register(const bool force_) + bool CDataReader::AddReceiveCallback(ReceiveCallbackT callback_) { -#if ECAL_CORE_REGISTRATION - if (!m_created) return(false); - if(m_topic_name.empty()) return(false); + if (!m_created) return(false); - // create command parameter - Registration::Sample ecal_reg_sample; - ecal_reg_sample.cmd_type = bct_reg_subscriber; - auto& ecal_reg_sample_topic = ecal_reg_sample.topic; - ecal_reg_sample_topic.hname = m_host_name; - ecal_reg_sample_topic.hgname = m_host_group_name; - ecal_reg_sample_topic.tname = m_topic_name; - ecal_reg_sample_topic.tid = m_topic_id; - // topic_information + // store receive callback { - auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; - if (m_share_ttype) - { - ecal_reg_sample_tdatatype.encoding = m_topic_info.encoding; - ecal_reg_sample_tdatatype.name = m_topic_info.name; - } - if (m_share_tdesc) - { - ecal_reg_sample_tdatatype.descriptor = m_topic_info.descriptor; - } + const std::lock_guard lock(m_receive_callback_mtx); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddReceiveCallback"); +#endif + m_receive_callback = std::move(callback_); } - ecal_reg_sample_topic.attr = m_attr; - ecal_reg_sample_topic.tsize = static_cast(m_topic_size); -#if ECAL_CORE_TRANSPORT_UDP - // udp multicast layer - { - Registration::TLayer udp_tlayer; - udp_tlayer.type = tl_ecal_udp; - udp_tlayer.version = 1; - udp_tlayer.confirmed = m_confirmed_layers.udp; - ecal_reg_sample_topic.tlayer.push_back(udp_tlayer); - } -#endif + return(true); + } -#if ECAL_CORE_TRANSPORT_SHM - // shm layer - { - Registration::TLayer shm_tlayer; - shm_tlayer.type = tl_ecal_shm; - shm_tlayer.version = 1; - shm_tlayer.confirmed = m_confirmed_layers.shm; - ecal_reg_sample_topic.tlayer.push_back(shm_tlayer); - } -#endif + bool CDataReader::RemReceiveCallback() + { + if (!m_created) return(false); -#if ECAL_CORE_TRANSPORT_TCP - // tcp layer + // reset receive callback { - Registration::TLayer tcp_tlayer; - tcp_tlayer.type = tl_ecal_tcp; - tcp_tlayer.version = 1; - tcp_tlayer.confirmed = m_confirmed_layers.tcp; - ecal_reg_sample_topic.tlayer.push_back(tcp_tlayer); - } + const std::lock_guard lock(m_receive_callback_mtx); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemReceiveCallback"); #endif + m_receive_callback = nullptr; + } - ecal_reg_sample_topic.pid = m_pid; - ecal_reg_sample_topic.pname = m_pname; - ecal_reg_sample_topic.uname = Process::GetUnitName(); - ecal_reg_sample_topic.dclock = m_clock; - ecal_reg_sample_topic.dfreq = GetFrequency(); - ecal_reg_sample_topic.message_drops = static_cast(m_message_drops); + return(true); + } - // we do not know the number of connections .. - ecal_reg_sample_topic.connections_loc = 0; - ecal_reg_sample_topic.connections_ext = 0; + bool CDataReader::AddEventCallback(eCAL_Subscriber_Event type_, SubEventCallbackT callback_) + { + if (!m_created) return(false); - // register subscriber - if(g_registration_provider() != nullptr) g_registration_provider()->ApplySample(ecal_reg_sample, force_); + // store event callback + { #ifndef NDEBUG - // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataReader::DoRegister"); + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddEventCallback"); #endif + const std::lock_guard lock(m_event_callback_map_mtx); + m_event_callback_map[type_] = std::move(callback_); + } -#endif // ECAL_CORE_REGISTRATION return(true); } - bool CDataReader::Unregister() + bool CDataReader::RemEventCallback(eCAL_Subscriber_Event type_) { -#if ECAL_CORE_REGISTRATION - if (m_topic_name.empty()) return(false); - - // create command parameter - Registration::Sample ecal_unreg_sample; - ecal_unreg_sample.cmd_type = bct_unreg_subscriber; - auto& ecal_reg_sample_topic = ecal_unreg_sample.topic; - ecal_reg_sample_topic.hname = m_host_name; - ecal_reg_sample_topic.hgname = m_host_group_name; - ecal_reg_sample_topic.pname = m_pname; - ecal_reg_sample_topic.pid = m_pid; - ecal_reg_sample_topic.tname = m_topic_name; - ecal_reg_sample_topic.tid = m_topic_id; - ecal_reg_sample_topic.uname = Process::GetUnitName(); + if (!m_created) return(false); - // unregister subscriber - if (g_registration_provider() != nullptr) g_registration_provider()->ApplySample(ecal_unreg_sample, false); + // reset event callback + { #ifndef NDEBUG - // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataReader::Unregister"); + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemEventCallback"); #endif + const std::lock_guard lock(m_event_callback_map_mtx); + m_event_callback_map[type_] = nullptr; + } -#endif // ECAL_CORE_REGISTRATION return(true); } bool CDataReader::SetAttribute(const std::string& attr_name_, const std::string& attr_value_) { - auto current_val = m_attr.find(attr_name_); - - const bool force = current_val == m_attr.end() || current_val->second != attr_value_; m_attr[attr_name_] = attr_value_; #ifndef NDEBUG @@ -323,16 +257,11 @@ namespace eCAL Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::SetAttribute"); #endif - // register it - Register(force); - return(true); } bool CDataReader::ClearAttribute(const std::string& attr_name_) { - auto force = m_attr.find(attr_name_) != m_attr.end(); - m_attr.erase(attr_name_); #ifndef NDEBUG @@ -340,63 +269,124 @@ namespace eCAL Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::ClearAttribute"); #endif - // register it - Register(force); - return(true); } - bool CDataReader::Receive(std::string& buf_, long long* time_ /* = nullptr */, int rcv_timeout_ms_ /* = 0 */) + void CDataReader::SetID(const std::set& id_set_) { - if (!m_created) return(false); + m_id_set = id_set_; + } - std::unique_lock read_buffer_lock(m_read_buf_mtx); + void CDataReader::ApplyPublication(const SPublicationInfo& publication_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_) + { + // flag write enabled from publisher side (information not used yet) +#if ECAL_CORE_TRANSPORT_UDP + m_layers.udp.write_enabled = layer_states_.udp.write_enabled; +#endif +#if ECAL_CORE_TRANSPORT_SHM + m_layers.shm.write_enabled = layer_states_.shm.write_enabled; +#endif +#if ECAL_CORE_TRANSPORT_TCP + m_layers.tcp.write_enabled = layer_states_.tcp.write_enabled; +#endif - // No need to wait (for whatever time) if something has been received - if (!m_read_buf_received) + FireConnectEvent(publication_info_.entity_id, data_type_info_); + + // add key to publisher map { - if (rcv_timeout_ms_ < 0) - { - m_read_buf_cv.wait(read_buffer_lock, [this]() { return this->m_read_buf_received; }); - } - else if (rcv_timeout_ms_ > 0) - { - m_read_buf_cv.wait_for(read_buffer_lock, std::chrono::milliseconds(rcv_timeout_ms_), [this]() { return this->m_read_buf_received; }); - } + const std::lock_guard lock(m_pub_map_mtx); + m_pub_map[publication_info_] = std::make_tuple(data_type_info_, layer_states_); } + } - // did we receive new samples ? - if (m_read_buf_received) + void CDataReader::RemovePublication(const SPublicationInfo& publication_info_) + { + // remove key from publisher map { -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataReader::Receive"); + const std::lock_guard lock(m_pub_map_mtx); + m_pub_map.erase(publication_info_); + } + } + + void CDataReader::ApplyLayerParameter(const SPublicationInfo& publication_info_, eTLayerType type_, const Registration::ConnectionPar& parameter_) + { + SReaderLayerPar par; + par.host_name = publication_info_.host_name; + par.process_id = publication_info_.process_id; + par.topic_name = m_topic_name; + par.topic_id = publication_info_.entity_id; + par.parameter = parameter_; + + switch (type_) + { + case tl_ecal_shm: +#if ECAL_CORE_TRANSPORT_SHM + CSHMReaderLayer::Get()->SetConnectionParameter(par); #endif - // copy content to target string - buf_.clear(); - buf_.swap(m_read_buf); - m_read_buf_received = false; + break; + case tl_ecal_tcp: +#if ECAL_CORE_TRANSPORT_TCP + CTCPReaderLayer::Get()->SetConnectionParameter(par); +#endif + break; + default: + break; + } + } - // apply time - if(time_ != nullptr) *time_ = m_read_time; + void CDataReader::InitializeLayers() + { + // initialize udp layer +#if ECAL_CORE_TRANSPORT_UDP + if (m_config.layer.udp.enable) + { + CUDPReaderLayer::Get()->Initialize(); + } +#endif - // return success - return(true); + // initialize shm layer +#if ECAL_CORE_TRANSPORT_SHM + if (m_config.layer.shm.enable) + { + CSHMReaderLayer::Get()->Initialize(); } +#endif - return(false); + // initialize tcp layer +#if ECAL_CORE_TRANSPORT_TCP + if (m_config.layer.tcp.enable) + { + CTCPReaderLayer::Get()->Initialize(); + } +#endif } - size_t CDataReader::AddSample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_) + size_t CDataReader::ApplySample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_) { // ensure thread safety const std::lock_guard lock(m_receive_callback_mtx); if (!m_created) return(0); + // check receive layer configuration + switch (layer_) + { + case tl_ecal_udp: + if (!m_config.layer.udp.enable) return 0; + break; + case tl_ecal_shm: + if (!m_config.layer.shm.enable) return 0; + break; + case tl_ecal_tcp: + if (!m_config.layer.tcp.enable) return 0; + break; + default: + break; + } + // store receive layer - m_confirmed_layers.udp |= layer_ == tl_ecal_udp; - m_confirmed_layers.shm |= layer_ == tl_ecal_shm; - m_confirmed_layers.tcp |= layer_ == tl_ecal_tcp; + m_layers.udp.active |= layer_ == tl_ecal_udp; + m_layers.shm.active |= layer_ == tl_ecal_shm; + m_layers.tcp.active |= layer_ == tl_ecal_tcp; // number of hash values to track for duplicates constexpr int hash_queue_size(64); @@ -480,7 +470,7 @@ namespace eCAL } // if not consumed by user receive call - if(!processed) + if (!processed) { // push sample into read buffer const std::lock_guard read_buffer_lock(m_read_buf_mtx); @@ -500,126 +490,247 @@ namespace eCAL return(size_); } - bool CDataReader::AddReceiveCallback(ReceiveCallbackT callback_) + std::string CDataReader::Dump(const std::string& indent_ /* = "" */) { - if (!m_created) return(false); + std::stringstream out; - // store receive callback - { - const std::lock_guard lock(m_receive_callback_mtx); -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddReceiveCallback"); -#endif - m_receive_callback = std::move(callback_); - } + out << '\n'; + out << indent_ << "------------------------------------" << '\n'; + out << indent_ << " class CDataReader " << '\n'; + out << indent_ << "------------------------------------" << '\n'; + out << indent_ << "m_host_name: " << m_host_name << '\n'; + out << indent_ << "m_host_group_name: " << m_host_group_name << '\n'; + out << indent_ << "m_topic_name: " << m_topic_name << '\n'; + out << indent_ << "m_topic_id: " << m_topic_id << '\n'; + out << indent_ << "m_topic_info.encoding: " << m_topic_info.encoding << '\n'; + out << indent_ << "m_topic_info.name: " << m_topic_info.name << '\n'; + out << indent_ << "m_topic_info.desc: " << m_topic_info.descriptor << '\n'; + out << indent_ << "m_topic_size: " << m_topic_size << '\n'; + out << indent_ << "m_read_buf.size(): " << m_read_buf.size() << '\n'; + out << indent_ << "m_read_time: " << m_read_time << '\n'; + out << indent_ << "m_clock: " << m_clock << '\n'; + out << indent_ << "frequency [mHz]: " << GetFrequency() << '\n'; + out << indent_ << "m_created: " << m_created << '\n'; + out << '\n'; - return(true); + return(out.str()); } - bool CDataReader::RemReceiveCallback() + void CDataReader::Register() { - if (!m_created) return(false); +#if ECAL_CORE_REGISTRATION + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); - // reset receive callback - { - const std::lock_guard lock(m_receive_callback_mtx); #ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemReceiveCallback"); + // log it + Logging::Log(log_level_debug4, m_topic_name + "::CDataReader::Register"); #endif - m_receive_callback = nullptr; - } - - return(true); +#endif // ECAL_CORE_REGISTRATION } - bool CDataReader::AddEventCallback(eCAL_Subscriber_Event type_, SubEventCallbackT callback_) + void CDataReader::Unregister() { - if (!m_created) return(false); +#if ECAL_CORE_REGISTRATION + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); - // store event callback - { #ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddEventCallback"); + // log it + Logging::Log(log_level_debug4, m_topic_name + "::CDataReader::Unregister"); #endif - const std::lock_guard lock(m_event_callback_map_mtx); - m_event_callback_map[type_] = std::move(callback_); - } - - return(true); +#endif // ECAL_CORE_REGISTRATION } - bool CDataReader::RemEventCallback(eCAL_Subscriber_Event type_) + void CDataReader::CheckConnections() { - if (!m_created) return(false); + const std::lock_guard lock(m_pub_map_mtx); - // reset event callback + if (m_pub_map.empty()) { -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemEventCallback"); -#endif - const std::lock_guard lock(m_event_callback_map_mtx); - m_event_callback_map[type_] = nullptr; + FireDisconnectEvent(); } - - return(true); } - void CDataReader::SetID(const std::set& id_set_) + Registration::Sample CDataReader::GetRegistration() { - m_id_set = id_set_; - } + // check connection timeouts + CheckConnections(); - void CDataReader::ApplyPublication(const SPublicationInfo& publication_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_) + // return registration + return GetRegistrationSample(); + } + + Registration::Sample CDataReader::GetRegistrationSample() { - Connect(publication_info_.topic_id, data_type_info_); + // create registration sample + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_reg_subscriber; - // add key to publisher map + auto& ecal_reg_sample_identifier = ecal_reg_sample.identifier; + ecal_reg_sample_identifier.process_id = m_pid; + ecal_reg_sample_identifier.entity_id = m_topic_id; + ecal_reg_sample_identifier.host_name = m_host_name; + + auto& ecal_reg_sample_topic = ecal_reg_sample.topic; + ecal_reg_sample_topic.hgname = m_host_group_name; + ecal_reg_sample_topic.tname = m_topic_name; + // topic_information { - const std::lock_guard lock(m_pub_map_mtx); - m_pub_map[publication_info_] = std::make_tuple(data_type_info_, layer_states_); + auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; + if (m_share_ttype) + { + ecal_reg_sample_tdatatype.encoding = m_topic_info.encoding; + ecal_reg_sample_tdatatype.name = m_topic_info.name; + } + if (m_share_tdesc) + { + ecal_reg_sample_tdatatype.descriptor = m_topic_info.descriptor; + } } - } + ecal_reg_sample_topic.attr = m_attr; + ecal_reg_sample_topic.tsize = static_cast(m_topic_size); - void CDataReader::RemovePublication(const SPublicationInfo& publication_info_) - { - // remove key from publisher map +#if ECAL_CORE_TRANSPORT_UDP + // udp multicast layer { - const std::lock_guard lock(m_pub_map_mtx); - m_pub_map.erase(publication_info_); + Registration::TLayer udp_tlayer; + udp_tlayer.type = tl_ecal_udp; + udp_tlayer.version = ecal_transport_layer_version; + udp_tlayer.enabled = m_layers.udp.read_enabled; + udp_tlayer.active = m_layers.udp.active; + ecal_reg_sample_topic.tlayer.push_back(udp_tlayer); + } +#endif + +#if ECAL_CORE_TRANSPORT_SHM + // shm layer + { + Registration::TLayer shm_tlayer; + shm_tlayer.type = tl_ecal_shm; + shm_tlayer.version = ecal_transport_layer_version; + shm_tlayer.enabled = m_layers.shm.read_enabled; + shm_tlayer.active = m_layers.shm.active; + ecal_reg_sample_topic.tlayer.push_back(shm_tlayer); } +#endif + +#if ECAL_CORE_TRANSPORT_TCP + // tcp layer + { + Registration::TLayer tcp_tlayer; + tcp_tlayer.type = tl_ecal_tcp; + tcp_tlayer.version = ecal_transport_layer_version; + tcp_tlayer.enabled = m_layers.tcp.read_enabled; + tcp_tlayer.active = m_layers.tcp.active; + ecal_reg_sample_topic.tlayer.push_back(tcp_tlayer); + } +#endif + + ecal_reg_sample_topic.pname = m_pname; + ecal_reg_sample_topic.uname = Process::GetUnitName(); + ecal_reg_sample_topic.dclock = m_clock; + ecal_reg_sample_topic.dfreq = GetFrequency(); + ecal_reg_sample_topic.message_drops = static_cast(m_message_drops); + + // we do not know the number of connections .. + ecal_reg_sample_topic.connections_loc = 0; + ecal_reg_sample_topic.connections_ext = 0; + + return ecal_reg_sample; } - void CDataReader::ApplyLayerParameter(const SPublicationInfo& publication_info_, eTLayerType type_, const Registration::ConnectionPar& parameter_) + Registration::Sample CDataReader::GetUnregistrationSample() { - SReaderLayerPar par; - par.host_name = publication_info_.host_name; - par.process_id = publication_info_.process_id; - par.topic_name = m_topic_name; - par.topic_id = publication_info_.topic_id; - par.parameter = parameter_; + // create unregistration sample + Registration::Sample ecal_unreg_sample; + ecal_unreg_sample.cmd_type = bct_unreg_subscriber; - switch (type_) + auto& ecal_reg_sample_identifier = ecal_unreg_sample.identifier; + ecal_reg_sample_identifier.process_id = m_pid; + ecal_reg_sample_identifier.entity_id = m_topic_id; + ecal_reg_sample_identifier.host_name = m_host_name; + + auto& ecal_reg_sample_topic = ecal_unreg_sample.topic; + ecal_reg_sample_topic.hgname = m_host_group_name; + ecal_reg_sample_topic.pname = m_pname; + ecal_reg_sample_topic.tname = m_topic_name; + ecal_reg_sample_topic.uname = Process::GetUnitName(); + + return ecal_unreg_sample; + } + + void CDataReader::StartTransportLayer() + { +#if ECAL_CORE_TRANSPORT_UDP + if (m_config.layer.udp.enable) { - case tl_ecal_shm: + // flag enabled + m_layers.udp.read_enabled = true; + + // subscribe to layer (if supported) + CUDPReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id); + } +#endif + #if ECAL_CORE_TRANSPORT_SHM - CSHMReaderLayer::Get()->SetConnectionParameter(par); + if (m_config.layer.shm.enable) + { + // flag enabled + m_layers.shm.read_enabled = true; + + // subscribe to layer (if supported) + CSHMReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id); + } #endif - break; - case tl_ecal_tcp: + #if ECAL_CORE_TRANSPORT_TCP - CTCPReaderLayer::Get()->SetConnectionParameter(par); + if (m_config.layer.tcp.enable) + { + // flag enabled + m_layers.tcp.read_enabled = true; + + // subscribe to layer (if supported) + CTCPReaderLayer::Get()->AddSubscription(m_host_name, m_topic_name, m_topic_id); + } #endif - break; - default: - break; + } + + void CDataReader::StopTransportLayer() + { +#if ECAL_CORE_TRANSPORT_UDP + if (m_config.layer.udp.enable) + { + // flag disabled + m_layers.udp.read_enabled = false; + + // unsubscribe from layer (if supported) + CUDPReaderLayer::Get()->RemSubscription(m_host_name, m_topic_name, m_topic_id); } +#endif + +#if ECAL_CORE_TRANSPORT_SHM + if (m_config.layer.shm.enable) + { + // flag disabled + m_layers.shm.read_enabled = false; + + // unsubscribe from layer (if supported) + CSHMReaderLayer::Get()->RemSubscription(m_host_name, m_topic_name, m_topic_id); + } +#endif + +#if ECAL_CORE_TRANSPORT_TCP + if (m_config.layer.tcp.enable) + { + // flag disabled + m_layers.tcp.read_enabled = false; + + // unsubscribe from layer (if supported) + CTCPReaderLayer::Get()->RemSubscription(m_host_name, m_topic_name, m_topic_id); + } +#endif } - void CDataReader::Connect(const std::string& tid_, const SDataTypeInformation& tinfo_) + void CDataReader::FireConnectEvent(const std::string& tid_, const SDataTypeInformation& tinfo_) { SSubEventCallbackData data; data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); @@ -657,7 +768,7 @@ namespace eCAL } } - void CDataReader::Disconnect() + void CDataReader::FireDisconnectEvent() { if (m_connected) { @@ -804,53 +915,4 @@ namespace eCAL const std::lock_guard lock(m_frequency_calculator_mtx); return static_cast(m_frequency_calculator.getFrequency(frequency_time) * 1000); } - - void CDataReader::RefreshRegistration() - { - if(!m_created) return; - - // ensure that registration is not called within zero nanoseconds - // normally it will be called from registration logic every second - - // register without send - Register(false); - - // check connection timeouts - { - const std::lock_guard lock(m_pub_map_mtx); - m_pub_map.remove_deprecated(); - - if (m_pub_map.empty()) - { - Disconnect(); - } - } - } - - std::string CDataReader::Dump(const std::string& indent_ /* = "" */) - { - std::stringstream out; - - - out << '\n'; - out << indent_ << "------------------------------------" << '\n'; - out << indent_ << " class CDataReader " << '\n'; - out << indent_ << "------------------------------------" << '\n'; - out << indent_ << "m_host_name: " << m_host_name << '\n'; - out << indent_ << "m_host_group_name: " << m_host_group_name << '\n'; - out << indent_ << "m_topic_name: " << m_topic_name << '\n'; - out << indent_ << "m_topic_id: " << m_topic_id << '\n'; - out << indent_ << "m_topic_info.encoding: " << m_topic_info.encoding << '\n'; - out << indent_ << "m_topic_info.name: " << m_topic_info.name << '\n'; - out << indent_ << "m_topic_info.desc: " << m_topic_info.descriptor << '\n'; - out << indent_ << "m_topic_size: " << m_topic_size << '\n'; - out << indent_ << "m_read_buf.size(): " << m_read_buf.size() << '\n'; - out << indent_ << "m_read_time: " << m_read_time << '\n'; - out << indent_ << "m_clock: " << m_clock << '\n'; - out << indent_ << "frequency [mHz]: " << GetFrequency() << '\n'; - out << indent_ << "m_created: " << m_created << '\n'; - out << '\n'; - - return(out.str()); - } } diff --git a/ecal/core/src/readwrite/ecal_reader.h b/ecal/core/src/readwrite/ecal_reader.h index 5d48a3c1f0..9b1ba93208 100644 --- a/ecal/core/src/readwrite/ecal_reader.h +++ b/ecal/core/src/readwrite/ecal_reader.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,13 +23,12 @@ #pragma once -#include #include #include +#include #include "serialization/ecal_serialize_sample_payload.h" #include "serialization/ecal_serialize_sample_registration.h" -#include "util/ecal_expmap.h" #include "util/frequency_calculator.h" #include @@ -39,10 +38,10 @@ #include #include #include +#include #include #include #include -#include #include namespace eCAL @@ -50,34 +49,27 @@ namespace eCAL class CDataReader { public: - struct SLayerStates + struct SLayerState { - bool udp = false; - bool shm = false; - bool tcp = false; + bool write_enabled = false; // is publisher enabled to write data on this layer? + bool read_enabled = false; // is this subscriber configured to read data from this layer? + bool active = false; // data has been received on this layer }; - - struct SPublicationInfo + + struct SLayerStates { - std::string host_name; - int32_t process_id = 0; - std::string topic_id; - - friend bool operator<(const SPublicationInfo& l, const SPublicationInfo& r) - { - return std::tie(l.host_name, l.process_id, l.topic_id) - < std::tie(r.host_name, r.process_id, r.topic_id); - } + SLayerState udp; + SLayerState shm; + SLayerState tcp; }; - CDataReader(const std::string& topic_name_, const SDataTypeInformation& topic_info_); + using SPublicationInfo = Registration::SampleIdentifier; + CDataReader(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Subscriber::Configuration& config_); ~CDataReader(); bool Stop(); - static void InitializeLayers(); - - bool Receive(std::string& buf_, long long* time_ = nullptr, int rcv_timeout_ms_ = 0); + bool Read(std::string& buf_, long long* time_ = nullptr, int rcv_timeout_ms_ = 0); bool AddReceiveCallback(ReceiveCallbackT callback_); bool RemReceiveCallback(); @@ -95,8 +87,7 @@ namespace eCAL void ApplyLayerParameter(const SPublicationInfo& publication_info_, eTLayerType type_, const Registration::ConnectionPar& parameter_); - std::string Dump(const std::string& indent_ = ""); - + Registration::Sample GetRegistration(); bool IsCreated() const { return(m_created); } bool IsPublished() const @@ -115,19 +106,26 @@ namespace eCAL std::string GetTopicID() const { return(m_topic_id); } SDataTypeInformation GetDataTypeInformation() const { return(m_topic_info); } - void RefreshRegistration(); + void InitializeLayers(); + size_t ApplySample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_); - size_t AddSample(const std::string& tid_, const char* payload_, size_t size_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_); + std::string Dump(const std::string& indent_ = ""); protected: - void SubscribeToLayers(); - void UnsubscribeFromLayers(); + void Register(); + void Unregister(); + + void CheckConnections(); + + Registration::Sample GetRegistrationSample(); + Registration::Sample GetUnregistrationSample(); + + void StartTransportLayer(); + void StopTransportLayer(); - bool Register(bool force_); - bool Unregister(); + void FireConnectEvent(const std::string& tid_, const SDataTypeInformation& tinfo_); + void FireDisconnectEvent(); - void Connect(const std::string& tid_, const SDataTypeInformation& tinfo_); - void Disconnect(); bool CheckMessageClock(const std::string& tid_, long long current_clock_); int32_t GetFrequency(); @@ -141,9 +139,10 @@ namespace eCAL SDataTypeInformation m_topic_info; std::map m_attr; std::atomic m_topic_size; + Subscriber::Configuration m_config; std::atomic m_connected; - using PublicationMapT = Util::CExpMap>; + using PublicationMapT = std::map>; mutable std::mutex m_pub_map_mtx; PublicationMapT m_pub_map; @@ -177,7 +176,7 @@ namespace eCAL bool m_share_ttype = false; bool m_share_tdesc = false; - SLayerStates m_confirmed_layers; + SLayerStates m_layers; std::atomic m_created; }; } diff --git a/ecal/core/src/readwrite/ecal_transport_layer.h b/ecal/core/src/readwrite/ecal_transport_layer.h new file mode 100644 index 0000000000..0e5cc61283 --- /dev/null +++ b/ecal/core/src/readwrite/ecal_transport_layer.h @@ -0,0 +1,29 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief transport layer settings +**/ + +#pragma once + +namespace eCAL +{ + constexpr int ecal_transport_layer_version = 2; +} diff --git a/ecal/core/src/readwrite/ecal_writer.cpp b/ecal/core/src/readwrite/ecal_writer.cpp index 6f4548e526..a4f93d8867 100644 --- a/ecal/core/src/readwrite/ecal_writer.cpp +++ b/ecal/core/src/readwrite/ecal_writer.cpp @@ -21,15 +21,10 @@ * @brief common eCAL data writer **/ -#include #include +#include #include -#include -#include -#include -#include - -#include "config/ecal_config_reader_hlp.h" +#include #if ECAL_CORE_REGISTRATION #include "registration/ecal_registration_provider.h" @@ -38,11 +33,16 @@ #include "ecal_writer.h" #include "ecal_writer_base.h" #include "ecal_writer_buffer_payload.h" +#include "ecal_global_accessors.h" +#include "ecal_transport_layer.h" -#include "pubsub/ecal_pubgate.h" - -#include +#include #include +#include +#include +#include +#include +#include struct SSndHash { @@ -65,6 +65,32 @@ namespace std }; } +namespace +{ +#ifndef NDEBUG + // function to convert boolean to string + std::string boolToString(bool value) + { + return value ? "true" : "false"; + } + + // function to log the states of SLayerState + void logLayerState(const std::string& layerName, const eCAL::CDataWriter::SLayerState& state) { + std::cout << layerName << " - Read Enabled: " << boolToString(state.read_enabled) + << ", Write Enabled: " << boolToString(state.write_enabled) + << ", Write Active : " << boolToString(state.active) << std::endl; + } + + // function to log the states of SLayerStates + void logLayerStates(const eCAL::CDataWriter::SLayerStates& states) { + std::cout << "Logging Layer States:" << std::endl; + logLayerState("UDP", states.udp); + logLayerState("SHM", states.shm); + logLayerState("TCP", states.tcp); + } +#endif +} + namespace eCAL { CDataWriter::CDataWriter(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Publisher::Configuration& config_) : @@ -89,18 +115,8 @@ namespace eCAL counter << std::chrono::steady_clock::now().time_since_epoch().count(); m_topic_id = counter.str(); - // set registration expiration - const std::chrono::milliseconds registration_timeout(Config::GetRegistrationTimeoutMs()); - m_sub_map.set_expiration(registration_timeout); - // mark as created m_created = true; - - // register - Register(false); - - // start udp, shm, tcp layer - StartTransportLayer(); } CDataWriter::~CDataWriter() @@ -121,8 +137,8 @@ namespace eCAL Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Stop"); #endif - // stop udp, shm, tcp layer - StopTransportLayer(); + // stop all transport layer + StopAllLayer(); // clear subscriber maps { @@ -145,92 +161,6 @@ namespace eCAL return true; } - bool CDataWriter::SetDataTypeInformation(const SDataTypeInformation& topic_info_) - { - // Does it even make sense to register if the info is the same??? - const bool force = m_topic_info != topic_info_; - m_topic_info = topic_info_; - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetDescription"); -#endif - - // register it - Register(force); - - return(true); - } - - bool CDataWriter::SetAttribute(const std::string& attr_name_, const std::string& attr_value_) - { - auto current_val = m_attr.find(attr_name_); - - const bool force = current_val == m_attr.end() || current_val->second != attr_value_; - m_attr[attr_name_] = attr_value_; - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetAttribute"); -#endif - - // register it - Register(force); - - return(true); - } - - bool CDataWriter::ClearAttribute(const std::string& attr_name_) - { - auto force = m_attr.find(attr_name_) != m_attr.end(); - - m_attr.erase(attr_name_); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ClearAttribute"); -#endif - - // register it - Register(force); - - return(true); - } - - bool CDataWriter::AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_) - { - if (!m_created) return(false); - - // store event callback - { -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::AddEventCallback"); -#endif - const std::lock_guard lock(m_event_callback_map_mtx); - m_event_callback_map[type_] = std::move(callback_); - } - - return(true); - } - - bool CDataWriter::RemEventCallback(eCAL_Publisher_Event type_) - { - if (!m_created) return(false); - - // reset event callback - { -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::RemEventCallback"); -#endif - const std::lock_guard lock(m_event_callback_map_mtx); - m_event_callback_map[type_] = nullptr; - } - - return(true); - } - size_t CDataWriter::Write(CPayloadWriter& payload_, long long time_, long long id_) { // get payload buffer size (one time, to avoid multiple computations) @@ -239,7 +169,7 @@ namespace eCAL // are we allowed to perform zero copy writing? bool allow_zero_copy(false); #if ECAL_CORE_TRANSPORT_SHM - allow_zero_copy = m_config.shm.zero_copy_mode; // zero copy mode activated by user + allow_zero_copy = m_config.layer.shm.zero_copy_mode; // zero copy mode activated by user #endif #if ECAL_CORE_TRANSPORT_UDP // udp is active -> no zero copy @@ -284,14 +214,14 @@ namespace eCAL wattr.clock = m_clock; wattr.hash = snd_hash; wattr.time = time_; - wattr.zero_copy = m_config.shm.zero_copy_mode; - wattr.acknowledge_timeout_ms = m_config.shm.acknowledge_timeout_ms; + wattr.zero_copy = m_config.layer.shm.zero_copy_mode; + wattr.acknowledge_timeout_ms = m_config.layer.shm.acknowledge_timeout_ms; // prepare send if (m_writer_shm->PrepareWrite(wattr)) { // register new to update listening subscribers and rematch - Register(true); + Register(); Process::SleepMS(5); } @@ -310,7 +240,7 @@ namespace eCAL shm_sent = m_writer_shm->Write(payload_buf, wattr); } - m_confirmed_layers.shm = true; + m_layers.shm.active = true; } written |= shm_sent; @@ -349,19 +279,19 @@ namespace eCAL wattr.clock = m_clock; wattr.hash = snd_hash; wattr.time = time_; - wattr.loopback = m_config.udp.loopback; + wattr.loopback = eCAL::GetConfiguration().registration.loopback; // prepare send if (m_writer_udp->PrepareWrite(wattr)) { // register new to update listening subscribers and rematch - Register(true); + Register(); Process::SleepMS(5); } // write to udp multicast layer udp_sent = m_writer_udp->Write(m_payload_buffer.data(), wattr); - m_confirmed_layers.udp = true; + m_layers.udp.active = true; } written |= udp_sent; @@ -403,7 +333,7 @@ namespace eCAL // write to tcp layer tcp_sent = m_writer_tcp->Write(m_payload_buffer.data(), wattr); - m_confirmed_layers.tcp = true; + m_layers.tcp.active = true; } written |= tcp_sent; @@ -426,25 +356,141 @@ namespace eCAL else return 0; } - void CDataWriter::ApplySubscription(const SSubscriptionInfo& subscription_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_, const std::string& reader_par_) + bool CDataWriter::SetDataTypeInformation(const SDataTypeInformation& topic_info_) + { + m_topic_info = topic_info_; + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetDescription"); +#endif + + return(true); + } + + bool CDataWriter::SetAttribute(const std::string& attr_name_, const std::string& attr_value_) + { + m_attr[attr_name_] = attr_value_; + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetAttribute"); +#endif + + return(true); + } + + bool CDataWriter::ClearAttribute(const std::string& attr_name_) + { + m_attr.erase(attr_name_); + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ClearAttribute"); +#endif + + return(true); + } + + bool CDataWriter::AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_) + { + if (!m_created) return(false); + + // store event callback + { +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::AddEventCallback"); +#endif + const std::lock_guard lock(m_event_callback_map_mtx); + m_event_callback_map[type_] = std::move(callback_); + } + + return(true); + } + + bool CDataWriter::RemEventCallback(eCAL_Publisher_Event type_) + { + if (!m_created) return(false); + + // reset event callback + { +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::RemEventCallback"); +#endif + const std::lock_guard lock(m_event_callback_map_mtx); + m_event_callback_map[type_] = nullptr; + } + + return(true); + } + + void CDataWriter::ApplySubscription(const SSubscriptionInfo& subscription_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& sub_layer_states_, const std::string& reader_par_) { - Connect(subscription_info_.topic_id, data_type_info_); + FireConnectEvent(subscription_info_.entity_id, data_type_info_); + + // collect layer states + std::vector pub_layers; + std::vector sub_layers; +#if ECAL_CORE_TRANSPORT_UDP + if (m_config.layer.udp.enable) pub_layers.push_back(tl_ecal_udp); + if (sub_layer_states_.udp.read_enabled) sub_layers.push_back(tl_ecal_udp); + + m_layers.udp.read_enabled = sub_layer_states_.udp.read_enabled; // just for debugging/logging +#endif +#if ECAL_CORE_TRANSPORT_SHM + if (m_config.layer.shm.enable) pub_layers.push_back(tl_ecal_shm); + if (sub_layer_states_.shm.read_enabled) sub_layers.push_back(tl_ecal_shm); + + m_layers.shm.read_enabled = sub_layer_states_.shm.read_enabled; // just for debugging/logging +#endif +#if ECAL_CORE_TRANSPORT_TCP + if (m_config.layer.tcp.enable) pub_layers.push_back(tl_ecal_tcp); + if (sub_layer_states_.tcp.read_enabled) sub_layers.push_back(tl_ecal_tcp); + + m_layers.tcp.read_enabled = sub_layer_states_.tcp.read_enabled; // just for debugging/logging +#endif + + // determine if we need to start a transport layer + // if a new layer gets activated, we reregister for SHM and TCP to force the exchange of connection parameter + // without this forced registration we would need one additional registration loop for these two layers to establish the connection + const TLayer::eTransportLayer layer2activate = DetermineTransportLayer2Start(pub_layers, sub_layers, m_host_name == subscription_info_.host_name); + switch (layer2activate) + { + case tl_ecal_udp: + StartUdpLayer(); + break; + case tl_ecal_shm: + StartShmLayer(); + break; + case tl_ecal_tcp: + StartTcpLayer(); + break; + default: + break; + } + +#ifndef NDEBUG + // log it + //logLayerStates(m_layers); +#endif // add key to subscriber map { const std::lock_guard lock(m_sub_map_mtx); - m_sub_map[subscription_info_] = std::make_tuple(data_type_info_, layer_states_); + m_sub_map[subscription_info_] = std::make_tuple(data_type_info_, sub_layer_states_); } // add a new subscription #if ECAL_CORE_TRANSPORT_UDP - if (m_writer_udp) m_writer_udp->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id, reader_par_); + if (m_writer_udp) m_writer_udp->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.entity_id, reader_par_); #endif #if ECAL_CORE_TRANSPORT_SHM - if (m_writer_shm) m_writer_shm->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id, reader_par_); + if (m_writer_shm) m_writer_shm->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.entity_id, reader_par_); #endif #if ECAL_CORE_TRANSPORT_TCP - if (m_writer_tcp) m_writer_tcp->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id, reader_par_); + if (m_writer_tcp) m_writer_tcp->ApplySubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.entity_id, reader_par_); #endif #ifndef NDEBUG @@ -463,13 +509,13 @@ namespace eCAL // remove subscription #if ECAL_CORE_TRANSPORT_UDP - if (m_writer_udp) m_writer_udp->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id); + if (m_writer_udp) m_writer_udp->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.entity_id); #endif #if ECAL_CORE_TRANSPORT_SHM - if (m_writer_shm) m_writer_shm->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id); + if (m_writer_shm) m_writer_shm->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.entity_id); #endif #if ECAL_CORE_TRANSPORT_TCP - if (m_writer_tcp) m_writer_tcp->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.topic_id); + if (m_writer_tcp) m_writer_tcp->RemoveSubscription(subscription_info_.host_name, subscription_info_.process_id, subscription_info_.entity_id); #endif #ifndef NDEBUG @@ -478,25 +524,6 @@ namespace eCAL #endif } - void CDataWriter::RefreshRegistration() - { - if (!m_created) return; - - // register without send - Register(false); - - // check connection timeouts - { - const std::lock_guard lock(m_sub_map_mtx); - m_sub_map.remove_deprecated(); - - if (m_sub_map.empty()) - { - Disconnect(); - } - } - } - void CDataWriter::RefreshSendCounter() { // increase write clock @@ -536,21 +563,63 @@ namespace eCAL return(out.str()); } - bool CDataWriter::Register(bool force_) + void CDataWriter::Register() + { +#if ECAL_CORE_REGISTRATION + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Register"); +#endif +#endif // ECAL_CORE_REGISTRATION + } + + void CDataWriter::Unregister() { #if ECAL_CORE_REGISTRATION - if (!m_created) return(false); - if (m_topic_name.empty()) return(false); + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); - // create command parameter +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Unregister"); +#endif +#endif // ECAL_CORE_REGISTRATION + } + + void CDataWriter::CheckConnections() + { + const std::lock_guard lock(m_sub_map_mtx); + + if (m_sub_map.empty()) + { + FireDisconnectEvent(); + } + } + + Registration::Sample CDataWriter::GetRegistration() + { + // check connection timeouts + CheckConnections(); + + return GetRegistrationSample(); + } + + Registration::Sample CDataWriter::GetRegistrationSample() + { + // create registration sample Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_reg_publisher; + auto& ecal_reg_sample_identifier = ecal_reg_sample.identifier; + ecal_reg_sample_identifier.process_id = m_pid; + ecal_reg_sample_identifier.entity_id = m_topic_id; + ecal_reg_sample_identifier.host_name = m_host_name; + auto& ecal_reg_sample_topic = ecal_reg_sample.topic; - ecal_reg_sample_topic.hname = m_host_name; ecal_reg_sample_topic.hgname = m_host_group_name; ecal_reg_sample_topic.tname = m_topic_name; - ecal_reg_sample_topic.tid = m_topic_id; + // topic_information { auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; @@ -573,8 +642,9 @@ namespace eCAL { eCAL::Registration::TLayer udp_tlayer; udp_tlayer.type = tl_ecal_udp; - udp_tlayer.version = 1; - udp_tlayer.confirmed = m_confirmed_layers.udp; + udp_tlayer.version = ecal_transport_layer_version; + udp_tlayer.enabled = m_layers.udp.write_enabled; + udp_tlayer.active = m_layers.udp.active; udp_tlayer.par_layer.layer_par_udpmc = m_writer_udp->GetConnectionParameter().layer_par_udpmc; ecal_reg_sample_topic.tlayer.push_back(udp_tlayer); } @@ -586,8 +656,9 @@ namespace eCAL { eCAL::Registration::TLayer shm_tlayer; shm_tlayer.type = tl_ecal_shm; - shm_tlayer.version = 1; - shm_tlayer.confirmed = m_confirmed_layers.shm; + shm_tlayer.version = ecal_transport_layer_version; + shm_tlayer.enabled = m_layers.shm.write_enabled; + shm_tlayer.active = m_layers.shm.active; shm_tlayer.par_layer.layer_par_shm = m_writer_shm->GetConnectionParameter().layer_par_shm; ecal_reg_sample_topic.tlayer.push_back(shm_tlayer); } @@ -599,14 +670,14 @@ namespace eCAL { eCAL::Registration::TLayer tcp_tlayer; tcp_tlayer.type = tl_ecal_tcp; - tcp_tlayer.version = 1; - tcp_tlayer.confirmed = m_confirmed_layers.tcp; + tcp_tlayer.version = ecal_transport_layer_version; + tcp_tlayer.enabled = m_layers.tcp.write_enabled; + tcp_tlayer.active = m_layers.tcp.active; tcp_tlayer.par_layer.layer_par_tcp = m_writer_tcp->GetConnectionParameter().layer_par_tcp; ecal_reg_sample_topic.tlayer.push_back(tcp_tlayer); } #endif - ecal_reg_sample_topic.pid = m_pid; ecal_reg_sample_topic.pname = m_pname; ecal_reg_sample_topic.uname = Process::GetUnitName(); ecal_reg_sample_topic.did = m_id; @@ -629,49 +700,30 @@ namespace eCAL ecal_reg_sample_topic.connections_loc = static_cast(loc_connections); ecal_reg_sample_topic.connections_ext = static_cast(ext_connections); - // register publisher - if (g_registration_provider() != nullptr) g_registration_provider()->ApplySample(ecal_reg_sample, force_); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Register"); -#endif - -#endif // ECAL_CORE_REGISTRATION - return(true); + return ecal_reg_sample; } - bool CDataWriter::Unregister() + Registration::Sample CDataWriter::GetUnregistrationSample() { -#if ECAL_CORE_REGISTRATION - if (m_topic_name.empty()) return(false); - - // create command parameter + // create unregistration sample Registration::Sample ecal_unreg_sample; ecal_unreg_sample.cmd_type = bct_unreg_publisher; + auto& ecal_reg_sample_identifier = ecal_unreg_sample.identifier; + ecal_reg_sample_identifier.process_id = m_pid; + ecal_reg_sample_identifier.entity_id = m_topic_id; + ecal_reg_sample_identifier.host_name = m_host_name; + auto& ecal_reg_sample_topic = ecal_unreg_sample.topic; - ecal_reg_sample_topic.hname = m_host_name; ecal_reg_sample_topic.hgname = m_host_group_name; ecal_reg_sample_topic.pname = m_pname; - ecal_reg_sample_topic.pid = m_pid; ecal_reg_sample_topic.tname = m_topic_name; - ecal_reg_sample_topic.tid = m_topic_id; ecal_reg_sample_topic.uname = Process::GetUnitName(); - // unregister publisher - if (g_registration_provider() != nullptr) g_registration_provider()->ApplySample(ecal_unreg_sample, false); - -#ifndef NDEBUG - // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::UnRegister"); -#endif - -#endif // ECAL_CORE_REGISTRATION - return(true); + return ecal_unreg_sample; } - void CDataWriter::Connect(const std::string& tid_, const SDataTypeInformation& tinfo_) + void CDataWriter::FireConnectEvent(const std::string& tid_, const SDataTypeInformation& tinfo_) { SPubEventCallbackData data; data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); @@ -709,7 +761,7 @@ namespace eCAL } } - void CDataWriter::Disconnect() + void CDataWriter::FireDisconnectEvent() { if (m_connected) { @@ -731,91 +783,111 @@ namespace eCAL } } - void CDataWriter::StartTransportLayer() + bool CDataWriter::StartUdpLayer() { #if ECAL_CORE_TRANSPORT_UDP - if (m_config.udp.enable) - { - ActivateUdpLayer(); - } -#endif -#if ECAL_CORE_TRANSPORT_SHM - if (m_config.shm.enable) - { - ActivateShmLayer(); - } -#endif -#if ECAL_CORE_TRANSPORT_TCP - if (m_config.tcp.enable) - { - ActivateTcpLayer(); - } -#endif - } + if (m_layers.udp.write_enabled) return false; - void CDataWriter::StopTransportLayer() - { - // destroy udp writer -#if ECAL_CORE_TRANSPORT_UDP - m_writer_udp.reset(); -#endif - - // destroy shm writer -#if ECAL_CORE_TRANSPORT_SHM - m_writer_shm.reset(); -#endif + // flag enabled + m_layers.udp.write_enabled = true; - // destroy tcp writer -#if ECAL_CORE_TRANSPORT_TCP - m_writer_tcp.reset(); -#endif - } - - void CDataWriter::ActivateUdpLayer() - { -#if ECAL_CORE_TRANSPORT_UDP // log state - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateUdpLayer::ACTIVATED"); + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateUdpLayer::ACTIVATED"); // create writer - m_writer_udp = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.udp); + m_writer_udp = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.layer.udp); + + // register activated layer + Register(); #ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateUdpLayer::WRITER_CREATED"); + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateUdpLayer::WRITER_CREATED"); #endif + return true; +#else // ECAL_CORE_TRANSPORT_UDP + return false; #endif // ECAL_CORE_TRANSPORT_UDP } - void CDataWriter::ActivateShmLayer() + bool CDataWriter::StartShmLayer() { #if ECAL_CORE_TRANSPORT_SHM + if (m_layers.shm.write_enabled) return false; + + // flag enabled + m_layers.shm.write_enabled = true; + // log state - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateShmLayer::ACTIVATED"); + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateShmLayer::ACTIVATED"); // create writer - m_writer_shm = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.shm); + m_writer_shm = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.layer.shm); + + // register activated layer + Register(); #ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateShmLayer::WRITER_CREATED"); + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateShmLayer::WRITER_CREATED"); #endif + return true; +#else // ECAL_CORE_TRANSPORT_SHM + return false; #endif // ECAL_CORE_TRANSPORT_SHM } - void CDataWriter::ActivateTcpLayer() + bool CDataWriter::StartTcpLayer() { #if ECAL_CORE_TRANSPORT_TCP + if (m_layers.tcp.write_enabled) return false; + + // flag enabled + m_layers.tcp.write_enabled = true; + // log state - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateTcpLayer::ACTIVATED"); + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateTcpLayer::ACTIVATED"); // create writer - m_writer_tcp = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.tcp); + m_writer_tcp = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.layer.tcp); + + // register activated layer + Register(); #ifndef NDEBUG - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::ActivateTcpLayer::WRITER_CREATED"); + Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateTcpLayer::WRITER_CREATED"); #endif + return true; +#else // ECAL_CORE_TRANSPORT_TCP + return false; #endif // ECAL_CORE_TRANSPORT_TCP } + void CDataWriter::StopAllLayer() + { +#if ECAL_CORE_TRANSPORT_UDP + // flag disabled + m_layers.udp.write_enabled = false; + + // destroy writer + m_writer_udp.reset(); +#endif + +#if ECAL_CORE_TRANSPORT_SHM + // flag disabled + m_layers.shm.write_enabled = false; + + // destroy writer + m_writer_shm.reset(); +#endif + +#if ECAL_CORE_TRANSPORT_TCP + // flag disabled + m_layers.tcp.write_enabled = false; + + // destroy writer + m_writer_tcp.reset(); +#endif + } + size_t CDataWriter::PrepareWrite(long long id_, size_t len_) { // store id @@ -851,6 +923,26 @@ namespace eCAL return is_internal_only; } + TLayer::eTransportLayer CDataWriter::DetermineTransportLayer2Start(const std::vector& enabled_pub_layer_, const std::vector& enabled_sub_layer_, bool same_host_) + { + // determine the priority list to use + const Publisher::Configuration::LayerPriorityVector& layer_priority_vector = same_host_ ? m_config.layer_priority_local : m_config.layer_priority_remote; + + // find the highest priority transport layer that is available in both publisher and subscriber options + // TODO: we need to fusion the two layer enum types (eTransportLayer) in ecal_tlayer.h and ecal_struct_sample_common.hf + for (const TLayer::eTransportLayer layer : layer_priority_vector) + { + if (std::find(enabled_pub_layer_.begin(), enabled_pub_layer_.end(), layer) != enabled_pub_layer_.end() + && std::find(enabled_sub_layer_.begin(), enabled_sub_layer_.end(), layer) != enabled_sub_layer_.end()) + { + return layer; + } + } + + // return tl_none if no common transport layer is found + return TLayer::eTransportLayer::tlayer_none; + } + int32_t CDataWriter::GetFrequency() { const auto frequency_time = std::chrono::steady_clock::now(); diff --git a/ecal/core/src/readwrite/ecal_writer.h b/ecal/core/src/readwrite/ecal_writer.h index bc6919c08f..1dd6214bad 100644 --- a/ecal/core/src/readwrite/ecal_writer.h +++ b/ecal/core/src/readwrite/ecal_writer.h @@ -25,12 +25,12 @@ #include #include -#include #include #include +#include -#include "util/ecal_expmap.h" -#include +#include "serialization/ecal_serialize_sample_registration.h" +#include "util/frequency_calculator.h" #if ECAL_CORE_TRANSPORT_UDP #include "udp/ecal_writer_udp.h" @@ -59,49 +59,43 @@ namespace eCAL class CDataWriter { public: - struct SLayerStates + struct SLayerState { - bool udp = false; - bool shm = false; - bool tcp = false; + bool read_enabled = false; // is subscriber enabled to read data on this layer? + bool write_enabled = false; // is this publisher configured to write data from this layer? + bool active = false; // data has been sent on this layer }; - - struct SSubscriptionInfo + + struct SLayerStates { - std::string host_name; - int32_t process_id = 0; - std::string topic_id; - - friend bool operator<(const SSubscriptionInfo& l, const SSubscriptionInfo& r) - { - return std::tie(l.host_name, l.process_id, l.topic_id) - < std::tie(r.host_name, r.process_id, r.topic_id); - } + SLayerState udp; + SLayerState shm; + SLayerState tcp; }; - CDataWriter(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Publisher::Configuration& config_ = {}); + using SSubscriptionInfo = Registration::SampleIdentifier; + + CDataWriter(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Publisher::Configuration& config_); ~CDataWriter(); bool Stop(); - bool SetDataTypeInformation(const SDataTypeInformation& topic_info_); + size_t Write(CPayloadWriter& payload_, long long time_, long long id_); - bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); - bool ClearAttribute(const std::string& attr_name_); + bool SetDataTypeInformation(const SDataTypeInformation& topic_info_); bool AddEventCallback(eCAL_Publisher_Event type_, PubEventCallbackT callback_); bool RemEventCallback(eCAL_Publisher_Event type_); - size_t Write(CPayloadWriter& payload_, long long time_, long long id_); + bool SetAttribute(const std::string& attr_name_, const std::string& attr_value_); + bool ClearAttribute(const std::string& attr_name_); - void ApplySubscription(const SSubscriptionInfo& subscription_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_, const std::string& reader_par_); + void ApplySubscription(const SSubscriptionInfo& subscription_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& sub_layer_states_, const std::string& reader_par_); void RemoveSubscription(const SSubscriptionInfo& subscription_info_); - void RefreshRegistration(); + Registration::Sample GetRegistration(); void RefreshSendCounter(); - std::string Dump(const std::string& indent_ = ""); - bool IsCreated() const { return(m_created); } bool IsSubscribed() const @@ -116,26 +110,34 @@ namespace eCAL return(m_sub_map.size()); } - const std::string& GetTopicName() const { return(m_topic_name); } + const std::string& GetTopicName() const { return(m_topic_name); } const SDataTypeInformation& GetDataTypeInformation() const { return m_topic_info; } + std::string Dump(const std::string& indent_ = ""); + protected: - bool Register(bool force_); - bool Unregister(); + void Register(); + void Unregister(); + + void CheckConnections(); + + Registration::Sample GetRegistrationSample(); + Registration::Sample GetUnregistrationSample(); - void Connect(const std::string& tid_, const SDataTypeInformation& tinfo_); - void Disconnect(); + bool StartUdpLayer(); + bool StartShmLayer(); + bool StartTcpLayer(); - void StartTransportLayer(); - void StopTransportLayer(); + void StopAllLayer(); - void ActivateUdpLayer(); - void ActivateShmLayer(); - void ActivateTcpLayer(); + void FireConnectEvent(const std::string& tid_, const SDataTypeInformation& tinfo_); + void FireDisconnectEvent(); size_t PrepareWrite(long long id_, size_t len_); - bool IsInternalSubscribedOnly(); + bool IsInternalSubscribedOnly(); + TLayer::eTransportLayer DetermineTransportLayer2Start(const std::vector& enabled_pub_layer_, const std::vector& enabled_sub_layer_, bool same_host_); + int32_t GetFrequency(); std::string m_host_name; @@ -153,7 +155,7 @@ namespace eCAL std::atomic m_connected; - using SSubscriptionMapT = Util::CExpMap>; + using SSubscriptionMapT = std::map>; mutable std::mutex m_sub_map_mtx; SSubscriptionMapT m_sub_map; @@ -177,7 +179,7 @@ namespace eCAL std::unique_ptr m_writer_tcp; #endif - SLayerStates m_confirmed_layers; + SLayerStates m_layers; std::atomic m_created; }; } diff --git a/ecal/core/src/readwrite/shm/ecal_reader_shm.cpp b/ecal/core/src/readwrite/shm/ecal_reader_shm.cpp index 244460f1eb..a20c0db7c5 100644 --- a/ecal/core/src/readwrite/shm/ecal_reader_shm.cpp +++ b/ecal/core/src/readwrite/shm/ecal_reader_shm.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,15 +21,18 @@ * @brief shared memory layer **/ -#include -#include -#include +#include +#include #include "ecal_global_accessors.h" -#include "pubsub/ecal_subgate.h" -#include "io/shm/ecal_memfile_pool.h" #include "ecal_reader_shm.h" +#include "io/shm/ecal_memfile_pool.h" +#include "pubsub/ecal_subgate.h" + +#include +#include + namespace eCAL { //////////////// diff --git a/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp b/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp index 43d1b56fe7..a53d329b29 100644 --- a/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp +++ b/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,24 +21,26 @@ * @brief memory file data writer **/ -#include #include -#include #include "ecal_def.h" #include "ecal_writer_shm.h" +#include "ecal/ecal_config.h" + +#include namespace eCAL { const std::string CDataWriterSHM::m_memfile_base_name = "ecal_"; - CDataWriterSHM::CDataWriterSHM(const std::string& host_name_, const std::string& topic_name_, const std::string& /*topic_id_*/, const Publisher::SHM::Configuration& shm_config_) : + CDataWriterSHM::CDataWriterSHM(const std::string& host_name_, const std::string& topic_name_, const std::string& /*topic_id_*/, const Publisher::Layer::SHM::Configuration& shm_config_) : m_config(shm_config_) { m_host_name = host_name_; m_topic_name = topic_name_; // initialize memory file buffer + if (m_config.memfile_buffer_count < 1) m_config.memfile_buffer_count = 1; SetBufferCount(m_config.memfile_buffer_count); } @@ -123,8 +125,8 @@ namespace eCAL // prepare memfile attributes SSyncMemoryFileAttr memory_file_attr = {}; - memory_file_attr.min_size = m_config.memfile_min_size_bytes; - memory_file_attr.reserve = m_config.memfile_reserve_percent; + memory_file_attr.min_size = GetConfiguration().transport_layer.shm.memfile_min_size_bytes; + memory_file_attr.reserve = GetConfiguration().transport_layer.shm.memfile_reserve_percent; memory_file_attr.timeout_open_ms = PUB_MEMFILE_OPEN_TO; memory_file_attr.timeout_ack_ms = m_config.acknowledge_timeout_ms; diff --git a/ecal/core/src/readwrite/shm/ecal_writer_shm.h b/ecal/core/src/readwrite/shm/ecal_writer_shm.h index fd6dff0d93..8126f4992c 100644 --- a/ecal/core/src/readwrite/shm/ecal_writer_shm.h +++ b/ecal/core/src/readwrite/shm/ecal_writer_shm.h @@ -25,8 +25,8 @@ #include -#include "readwrite/ecal_writer_base.h" #include "io/shm/ecal_memfile_sync.h" +#include "readwrite/ecal_writer_base.h" #include #include @@ -38,7 +38,7 @@ namespace eCAL class CDataWriterSHM : public CDataWriterBase { public: - CDataWriterSHM(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::SHM::Configuration& shm_config_); + CDataWriterSHM(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::Layer::SHM::Configuration& shm_config_); SWriterInfo GetInfo() override; @@ -53,7 +53,7 @@ namespace eCAL protected: bool SetBufferCount(size_t buffer_count_); - Publisher::SHM::Configuration m_config; + Publisher::Layer::SHM::Configuration m_config; size_t m_write_idx = 0; std::vector> m_memory_file_vec; diff --git a/ecal/core/src/readwrite/tcp/ecal_reader_tcp.cpp b/ecal/core/src/readwrite/tcp/ecal_reader_tcp.cpp index ffbfbae0af..e916fcdc11 100644 --- a/ecal/core/src/readwrite/tcp/ecal_reader_tcp.cpp +++ b/ecal/core/src/readwrite/tcp/ecal_reader_tcp.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,15 +21,14 @@ * @brief tcp reader and layer **/ -#include "ecal_global_accessors.h" - #include -#include "pubsub/ecal_subgate.h" - +#include "ecal_global_accessors.h" #include "ecal_reader_tcp.h" #include "ecal_tcp_pubsub_logger.h" +#include "pubsub/ecal_subgate.h" + #include "ecal_utils/portable_endian.h" namespace eCAL @@ -126,10 +125,13 @@ namespace eCAL //////////////// // LAYER //////////////// - CTCPReaderLayer::CTCPReaderLayer() = default; + CTCPReaderLayer::CTCPReaderLayer() : m_initialized(false) {} void CTCPReaderLayer::Initialize() { + if (m_initialized) return; + m_initialized = true; + const tcp_pubsub::logger::logger_t tcp_pubsub_logger = std::bind(TcpPubsubLogger, std::placeholders::_1, std::placeholders::_2); m_executor = std::make_shared(Config::GetTcpPubsubReaderThreadpoolSize(), tcp_pubsub_logger); } diff --git a/ecal/core/src/readwrite/tcp/ecal_reader_tcp.h b/ecal/core/src/readwrite/tcp/ecal_reader_tcp.h index cc1a74c548..2eae0b2a5e 100644 --- a/ecal/core/src/readwrite/tcp/ecal_reader_tcp.h +++ b/ecal/core/src/readwrite/tcp/ecal_reader_tcp.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,10 @@ #include "serialization/ecal_struct_sample_payload.h" +#include +#include +#include + namespace eCAL { //////////////// @@ -70,6 +74,7 @@ namespace eCAL void SetConnectionParameter(SReaderLayerPar& /*par_*/) override; private: + std::atomic m_initialized; std::shared_ptr m_executor; using DataReaderTCPMapT = std::unordered_map>; diff --git a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp index 6334a40008..c9f98bf125 100644 --- a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp +++ b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp @@ -23,7 +23,6 @@ #include -#include "config/ecal_config_reader_hlp.h" #include "serialization/ecal_serialize_sample_payload.h" #include "ecal_writer_tcp.h" @@ -38,7 +37,7 @@ namespace eCAL std::mutex CDataWriterTCP::g_tcp_writer_executor_mtx; std::shared_ptr CDataWriterTCP::g_tcp_writer_executor; - CDataWriterTCP::CDataWriterTCP(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::TCP::Configuration& tcp_config_) : + CDataWriterTCP::CDataWriterTCP(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::Layer::TCP::Configuration& tcp_config_) : m_config(tcp_config_) { { diff --git a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h index 94a3e96fe7..2aeedf01db 100644 --- a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h +++ b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h @@ -40,7 +40,7 @@ namespace eCAL class CDataWriterTCP : public CDataWriterBase { public: - CDataWriterTCP(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::TCP::Configuration& tcp_config_); + CDataWriterTCP(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::Layer::TCP::Configuration& tcp_config_); SWriterInfo GetInfo() override; @@ -49,7 +49,7 @@ namespace eCAL Registration::ConnectionPar GetConnectionParameter() override; private: - Publisher::TCP::Configuration m_config; + Publisher::Layer::TCP::Configuration m_config; std::vector m_header_buffer; diff --git a/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp b/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp index 1bdcdf443e..2036f6fe01 100644 --- a/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp +++ b/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,14 @@ * @brief udp multicast reader and layer **/ -#include "ecal_reader_udp.h" +#include +#include "ecal_reader_udp.h" #include "ecal_global_accessors.h" -#include "pubsub/ecal_subgate.h" #include "io/udp/ecal_udp_configurations.h" +#include "pubsub/ecal_subgate.h" + #include #include #include @@ -61,7 +63,7 @@ namespace eCAL attr.port = UDP::GetPayloadPort(); attr.broadcast = UDP::IsBroadcast(); attr.loopback = true; - attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); + attr.rcvbuf = UDP::GetReceiveBufferSize(); // start payload sample receiver m_payload_receiver = std::make_shared(attr, std::bind(&CUDPReaderLayer::HasSample, this, std::placeholders::_1), std::bind(&CUDPReaderLayer::ApplySample, this, std::placeholders::_1, std::placeholders::_2)); diff --git a/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp b/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp index 8466a38f5d..efae120896 100644 --- a/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp +++ b/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp @@ -26,12 +26,13 @@ #include "ecal_writer_udp.h" #include "io/udp/ecal_udp_configurations.h" #include "serialization/ecal_serialize_sample_payload.h" +#include "ecal/ecal_config.h" #include namespace eCAL { - CDataWriterUdpMC::CDataWriterUdpMC(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::UDP::Configuration& udp_config_) : + CDataWriterUdpMC::CDataWriterUdpMC(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::Layer::UDP::Configuration& udp_config_) : m_config(udp_config_) { m_host_name = host_name_; @@ -44,7 +45,7 @@ namespace eCAL attr.port = UDP::GetPayloadPort(); attr.ttl = UDP::GetMulticastTtl(); attr.broadcast = UDP::IsBroadcast(); - attr.sndbuf = m_config.sndbuf_size_bytes; + attr.sndbuf = UDP::GetSendBufferSize(); // create udp/sample sender with activated loop-back attr.loopback = true; diff --git a/ecal/core/src/readwrite/udp/ecal_writer_udp.h b/ecal/core/src/readwrite/udp/ecal_writer_udp.h index 32a7ef78b1..8a76cc0489 100644 --- a/ecal/core/src/readwrite/udp/ecal_writer_udp.h +++ b/ecal/core/src/readwrite/udp/ecal_writer_udp.h @@ -37,14 +37,14 @@ namespace eCAL class CDataWriterUdpMC : public CDataWriterBase { public: - CDataWriterUdpMC(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::UDP::Configuration& udp_config_); + CDataWriterUdpMC(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_, const Publisher::Layer::UDP::Configuration& udp_config_); SWriterInfo GetInfo() override; bool Write(const void* buf_, const SWriterAttr& attr_) override; protected: - Publisher::UDP::Configuration m_config; + Publisher::Layer::UDP::Configuration m_config; std::vector m_sample_buffer; std::shared_ptr m_sample_sender_loopback; diff --git a/ecal/core/src/registration/attributes/registration_attributes.h b/ecal/core/src/registration/attributes/registration_attributes.h new file mode 100644 index 0000000000..f080bbc0c3 --- /dev/null +++ b/ecal/core/src/registration/attributes/registration_attributes.h @@ -0,0 +1,67 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include +#include +#include + +namespace eCAL +{ + namespace Registration + { + struct SUDPModeAttrributes + { + std::string group; + int ttl; + }; + + struct SUDPAttributes + { + Types::UDPMode mode; + int port; + int sendbuffer; + int receivebuffer; + SUDPModeAttrributes network; + SUDPModeAttrributes local; + }; + + struct SSHMAttributes + { + std::string domain; + size_t queue_size; + }; + + struct SAttributes + { + std::chrono::milliseconds timeout; + bool network_enabled; + bool loopback; + bool shm_enabled; + bool udp_enabled; + unsigned int refresh; + std::string host_group_name; + int process_id; + + SUDPAttributes udp; + SSHMAttributes shm; + }; + } +} diff --git a/ecal/core/src/registration/attributes/sample_applier_attributes.h b/ecal/core/src/registration/attributes/sample_applier_attributes.h new file mode 100644 index 0000000000..efec45de90 --- /dev/null +++ b/ecal/core/src/registration/attributes/sample_applier_attributes.h @@ -0,0 +1,39 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace Registration + { + namespace SampleApplier + { + struct SAttributes + { + bool network_enabled; + bool loopback; + std::string host_group_name; + int process_id; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/builder/sample_applier_attribute_builder.cpp b/ecal/core/src/registration/builder/sample_applier_attribute_builder.cpp new file mode 100644 index 0000000000..cab74eef50 --- /dev/null +++ b/ecal/core/src/registration/builder/sample_applier_attribute_builder.cpp @@ -0,0 +1,41 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "sample_applier_attribute_builder.h" + +namespace eCAL +{ + namespace Registration + { + namespace SampleApplier + { + SAttributes BuildSampleApplierAttributes(const Registration::SAttributes& attr_) + { + SAttributes sample_applier_attr; + + sample_applier_attr.network_enabled = attr_.network_enabled; + sample_applier_attr.loopback = attr_.loopback; + sample_applier_attr.host_group_name = attr_.host_group_name; + sample_applier_attr.process_id = attr_.process_id; + + return sample_applier_attr; + } + } + } +} diff --git a/ecal/core/src/registration/builder/sample_applier_attribute_builder.h b/ecal/core/src/registration/builder/sample_applier_attribute_builder.h new file mode 100644 index 0000000000..e9d4ce337b --- /dev/null +++ b/ecal/core/src/registration/builder/sample_applier_attribute_builder.h @@ -0,0 +1,34 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include "registration/attributes/registration_attributes.h" +#include "registration/attributes/sample_applier_attributes.h" + +namespace eCAL +{ + namespace Registration + { + namespace SampleApplier + { + SAttributes BuildSampleApplierAttributes(const Registration::SAttributes& attr_); + } + } +} diff --git a/ecal/core/src/registration/builder/udp_shm_attribute_builder.cpp b/ecal/core/src/registration/builder/udp_shm_attribute_builder.cpp new file mode 100644 index 0000000000..29965d9c8c --- /dev/null +++ b/ecal/core/src/registration/builder/udp_shm_attribute_builder.cpp @@ -0,0 +1,84 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "udp_shm_attribute_builder.h" + +namespace eCAL +{ + namespace Registration + { + UDP::SSenderAttributes BuildUDPSenderAttributes(const SAttributes& provider_attr_) + { + UDP::SSenderAttributes sender_attr; + sender_attr.broadcast = !provider_attr_.network_enabled; + sender_attr.loopback = provider_attr_.loopback; + + sender_attr.sndbuf = provider_attr_.udp.sendbuffer; + sender_attr.port = provider_attr_.udp.port; + + switch (provider_attr_.udp.mode) + { + case Types::UDPMode::NETWORK: + sender_attr.address = provider_attr_.udp.network.group; + sender_attr.ttl = provider_attr_.udp.network.ttl; + break; + case Types::UDPMode::LOCAL: + sender_attr.address = provider_attr_.udp.local.group; + sender_attr.ttl = provider_attr_.udp.local.ttl; + break; + default: + break; + } + + return sender_attr; + } + + UDP::SReceiverAttributes BuildUDPReceiverAttributes(const SAttributes& provider_attr_) + { + UDP::SReceiverAttributes receiver_attr; + receiver_attr.broadcast = !provider_attr_.network_enabled; + receiver_attr.loopback = true; + + receiver_attr.rcvbuf = provider_attr_.udp.receivebuffer; + receiver_attr.port = provider_attr_.udp.port; + + switch (provider_attr_.udp.mode) + { + case Types::UDPMode::NETWORK: + receiver_attr.address = provider_attr_.udp.network.group; + break; + case Types::UDPMode::LOCAL: + receiver_attr.address = provider_attr_.udp.local.group; + break; + default: + break; + } + + return receiver_attr; + } + + SHM::SAttributes BuildSHMAttributes(const SAttributes& provider_attr_) + { + SHM::SAttributes sender_attr; + sender_attr.domain = provider_attr_.shm.domain; + sender_attr.queue_size = provider_attr_.shm.queue_size; + return sender_attr; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/builder/udp_shm_attribute_builder.h b/ecal/core/src/registration/builder/udp_shm_attribute_builder.h new file mode 100644 index 0000000000..1fefc8e02b --- /dev/null +++ b/ecal/core/src/registration/builder/udp_shm_attribute_builder.h @@ -0,0 +1,36 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include "registration/udp/attributes/registration_sender_udp_attributes.h" +#include "registration/udp/attributes/registration_receiver_udp_attributes.h" +#include "registration/shm/attributes/registration_shm_attributes.h" +#include "registration/attributes/registration_attributes.h" + + +namespace eCAL +{ + namespace Registration + { + UDP::SSenderAttributes BuildUDPSenderAttributes (const SAttributes& provider_attr_); + UDP::SReceiverAttributes BuildUDPReceiverAttributes (const SAttributes& provider_attr_); + SHM::SAttributes BuildSHMAttributes (const SAttributes& provider_attr_); + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/ecal_process_registration.cpp b/ecal/core/src/registration/ecal_process_registration.cpp new file mode 100644 index 0000000000..34b6104a89 --- /dev/null +++ b/ecal/core/src/registration/ecal_process_registration.cpp @@ -0,0 +1,110 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief Functions to generate Process Registration / Unregistration samples. + * +**/ + +#include "ecal_process_registration.h" + +#include +#include +#include "ecal_global_accessors.h" +#include "ecal_globals.h" +#include "time/ecal_timegate.h" + +eCAL::Registration::Sample eCAL::Registration::GetProcessRegisterSample() +{ + Registration::Sample process_sample; + process_sample.cmd_type = bct_reg_process; + + auto& process_sample_identifier = process_sample.identifier; + process_sample_identifier.host_name = eCAL::Process::GetHostName(); + process_sample_identifier.process_id = eCAL::Process::GetProcessID(); + + auto& process_sample_process = process_sample.process; + process_sample_process.hgname = eCAL::Process::GetHostGroupName(); + process_sample_process.pname = eCAL::Process::GetProcessName(); + process_sample_process.uname = eCAL::Process::GetUnitName(); + process_sample_process.pparam = eCAL::Process::GetProcessParameter(); + process_sample_process.state.severity = static_cast(g_process_severity); + process_sample_process.state.severity_level = static_cast(g_process_severity_level); + process_sample_process.state.info = g_process_info; +#if ECAL_CORE_TIMEPLUGIN + if (g_timegate() == nullptr) + { + process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; + } + else + { + if (!g_timegate()->IsSynchronized()) + { + process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; + } + else + { + switch (g_timegate()->GetSyncMode()) + { + case CTimeGate::eTimeSyncMode::realtime: + process_sample_process.tsync_state = Registration::eTSyncState::tsync_realtime; + break; + case CTimeGate::eTimeSyncMode::replay: + process_sample_process.tsync_state = Registration::eTSyncState::tsync_replay; + break; + default: + process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; + break; + } + } + process_sample_process.tsync_mod_name = g_timegate()->GetName(); + } +#endif + + // eCAL initialization state + const unsigned int comp_state(g_globals()->GetComponents()); + process_sample_process.component_init_state = static_cast(comp_state); + std::string component_info; + if ((comp_state & eCAL::Init::Publisher) != 0u) component_info += "|pub"; + if ((comp_state & eCAL::Init::Subscriber) != 0u) component_info += "|sub"; + if ((comp_state & eCAL::Init::Logging) != 0u) component_info += "|log"; + if ((comp_state & eCAL::Init::TimeSync) != 0u) component_info += "|time"; + if (!component_info.empty()) component_info = component_info.substr(1); + process_sample_process.component_init_info = component_info; + + process_sample_process.ecal_runtime_version = eCAL::GetVersionString(); + + return process_sample; +} + +eCAL::Registration::Sample eCAL::Registration::GetProcessUnregisterSample() +{ + Registration::Sample process_sample; + process_sample.cmd_type = bct_unreg_process; + + auto& process_sample_identifier = process_sample.identifier; + process_sample_identifier.host_name = eCAL::Process::GetHostName(); + process_sample_identifier.process_id = eCAL::Process::GetProcessID(); + + auto& process_sample_process = process_sample.process; + process_sample_process.pname = eCAL::Process::GetProcessName(); + process_sample_process.uname = eCAL::Process::GetUnitName(); + + return process_sample; +} diff --git a/ecal/core/src/registration/ecal_process_registration.h b/ecal/core/src/registration/ecal_process_registration.h new file mode 100644 index 0000000000..e5961f5f82 --- /dev/null +++ b/ecal/core/src/registration/ecal_process_registration.h @@ -0,0 +1,37 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief Functions to generate Process Registration / Unregistration samples. + * +**/ + +#pragma once + +#include "serialization/ecal_struct_sample_registration.h" + +namespace eCAL +{ + namespace Registration + { + Sample GetProcessRegisterSample(); + + Sample GetProcessUnregisterSample(); + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/ecal_registration.cpp b/ecal/core/src/registration/ecal_registration.cpp new file mode 100644 index 0000000000..21b7bc4ed2 --- /dev/null +++ b/ecal/core/src/registration/ecal_registration.cpp @@ -0,0 +1,485 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief Registration public API. + * +**/ + +#include + +#include "ecal_def.h" +#include "ecal_globals.h" +#include "ecal_event.h" +#include "registration/ecal_registration_receiver.h" +#include "pubsub/ecal_pubgate.h" + +namespace +{ + /** + * @brief Extract a set of all SQualityTopicInfo matching the given topic name. + * + * @param topic_name_ The topic name. + * @param quality_data_type_info_multi_map_ MultiMap { TopicName -> SQualityTopicInfo }. + * + * @return Set of SQualityTopicInfo + **/ + std::set GetQualityTopicInfoSet(const std::string& topic_name_, const eCAL::Registration::QualityTopicInfoMultiMap& quality_data_type_info_multi_map_) + { + std::set quality_topic_info_set; + + const auto topic_info_range = quality_data_type_info_multi_map_.equal_range(topic_name_); + for (auto topic_info_range_it = topic_info_range.first; topic_info_range_it != topic_info_range.second; ++topic_info_range_it) + { + quality_topic_info_set.insert(topic_info_range_it->second); + } + + return quality_topic_info_set; + } + + /** + * @brief Extract a set of all SQualityServiceInfo matching the given service name/method name. + * + * @param service_name_ The service name. + * @param method_name_ The method name. + * @param quality_service_info_multi_map_ MultiMap { -> SQualityServiceInfo }. + * + * @return Set of SQualityServiceInfo + **/ + std::set GetQualityServiceInfoSet(const std::string& service_name_, const std::string& method_name_, const eCAL::Registration::QualityServiceInfoMultimap& quality_service_info_multi_map_) + { + std::set quality_service_info_set; + + eCAL::Registration::SServiceMethod key; + key.service_name = service_name_; + key.method_name = method_name_; + const auto service_info_range = quality_service_info_multi_map_.equal_range(key); + for (auto service_info_range_it = service_info_range.first; service_info_range_it != service_info_range.second; ++service_info_range_it) + { + quality_service_info_set.insert(service_info_range_it->second); + } + + return quality_service_info_set; + } + + /** + * @brief Reducing std::map<(TopicName, TopicID), SQualityTopicInfo> to + * std::map based on the quality + * + * @param source_map_ std::map<(TopicName, TopicID), SQualityTopicInfo>. + * + * @return std::map + **/ + std::map ReduceQualityTopicIdMap(const eCAL::Registration::QualityTopicInfoMultiMap& source_map_) + { + std::map target_map; + + for (const auto& source_pair : source_map_) + { + const auto& source_key = source_pair.first; + const auto& source_value = source_pair.second; + + auto target_it = target_map.find(source_key); + if (target_it != target_map.end()) + { + // key exists in target map + if (source_value.quality > target_it->second.quality) + { + // source quality is greater, overwrite + target_it->second = source_value; + } + } + else + { + // key does not exist in target map, insert source pair + target_map.insert(std::make_pair(source_key, source_value)); + } + } + + return target_map; + } + + /** + * @brief Reducing std::map<(ServiceName, ServiceId, MethodName), SQualityServiceInfo> to + * std::map, SQualityServiceInfo> based on the quality + * + * @param source_map_ std::map<(ServiceName, ServiceId, MethodName), SQualityServiceInfo>. + * + * @return std::map, SQualityServiceInfo> + **/ + std::map ReduceQualityServiceIdMap(const eCAL::Registration::QualityServiceInfoMultimap& source_map_) + { + std::map target_map; + + for (const auto& source_pair : source_map_) + { + const auto& source_key = source_pair.first; + const auto& source_value = source_pair.second; + + eCAL::Registration::SServiceMethod target_key; + target_key.service_name = source_key.service_name; + target_key.method_name = source_key.method_name; + auto target_it = target_map.find(target_key); + if (target_it != target_map.end()) + { + // key exists in target map + if ( (source_value.request_quality > target_it->second.request_quality) + || (source_value.response_quality > target_it->second.response_quality)) + { + // source quality is greater, overwrite + target_it->second = source_value; + } + } + else + { + // key does not exist in target map, insert source pair + target_map.insert(std::make_pair(target_key, source_pair.second)); + } + } + + return target_map; + } +} + +namespace eCAL +{ + namespace Registration + { + std::set GetPublisherIDs() + { + if (g_descgate() == nullptr) return std::set(); + return g_descgate()->GetPublisherIDs(); + } + + bool GetPublisherInfo(const STopicId& id_, SQualityTopicInfo& topic_info_) + { + if (g_descgate() == nullptr) return false; + return g_descgate()->GetPublisherInfo(id_, topic_info_); + } + + std::set GetSubscriberIDs() + { + if (g_descgate() == nullptr) return std::set(); + return g_descgate()->GetSubscriberIDs(); + } + + bool GetSubscriberInfo(const STopicId& id_, SQualityTopicInfo& topic_info_) + { + if (g_descgate() == nullptr) return false; + return g_descgate()->GetSubscriberInfo(id_, topic_info_); + } + + std::set GetServiceIDs() + { + if (g_descgate() == nullptr) return std::set(); + return g_descgate()->GetServiceIDs(); + } + + bool GetServiceInfo(const SServiceId& id_, SQualityServiceInfo& service_info_) + { + if (g_descgate() == nullptr) return false; + return g_descgate()->GetServiceInfo(id_, service_info_); + } + + std::set GetClientIDs() + { + if (g_descgate() == nullptr) return std::set(); + return g_descgate()->GetClientIDs(); + } + + bool GetClientInfo(const SServiceId& id_, SQualityServiceInfo& service_info_) + { + if (g_descgate() == nullptr) return false; + return g_descgate()->GetClientInfo(id_, service_info_); + } + + QualityTopicInfoMultiMap GetPublishers() + { + const std::set id_set = GetPublisherIDs(); + + Registration::QualityTopicInfoMultiMap multi_map; + for (const auto& id : id_set) + { + SQualityTopicInfo quality_info; + if (GetPublisherInfo(id, quality_info)) + { + multi_map.insert(std::pair(id.topic_name, quality_info)); + } + } + return multi_map; + } + + QualityTopicInfoSet GetPublishers(const std::string& topic_name_) + { + return ::GetQualityTopicInfoSet(topic_name_, GetPublishers()); + } + + QualityTopicInfoMultiMap GetSubscribers() + { + const std::set id_set = GetSubscriberIDs(); + + Registration::QualityTopicInfoMultiMap multi_map; + for (const auto& id : id_set) + { + SQualityTopicInfo quality_info; + if (GetSubscriberInfo(id, quality_info)) + { + multi_map.insert(std::pair(id.topic_name, quality_info)); + } + } + return multi_map; + } + + QualityTopicInfoSet GetSubscribers(const std::string& topic_name_) + { + return ::GetQualityTopicInfoSet(topic_name_, GetSubscribers()); + } + + SDataTypeInformation GetHighestQualityDataTypeInformation(const QualityTopicInfoSet& quality_topic_info_set_) + { + SQualityTopicInfo highest_quality_topic_info; + for (const auto& info : quality_topic_info_set_) + { + if (info.quality > highest_quality_topic_info.quality) + { + highest_quality_topic_info = info; + } + } + return highest_quality_topic_info.info; + } + + QualityServiceInfoMultimap GetServices() + { + const std::set id_set = GetServiceIDs(); + + Registration::QualityServiceInfoMultimap multi_map; + for (const auto& id : id_set) + { + SQualityServiceInfo quality_info; + if (GetServiceInfo(id, quality_info)) + { + multi_map.insert(std::pair(SServiceMethod{ id.service_name, id.method_name }, quality_info)); + } + } + return multi_map; + } + + QualityServiceInfoMultimap GetClients() + { + const std::set id_set = GetClientIDs(); + + Registration::QualityServiceInfoMultimap multi_map; + for (const auto& id : id_set) + { + SQualityServiceInfo quality_info; + if (GetClientInfo(id, quality_info)) + { + multi_map.insert(std::pair(SServiceMethod{ id.service_name, id.method_name }, quality_info)); + } + } + return multi_map; + } + + SServiceMethodInformation GetHighestQualityServiceMethodInformation(const SQualityServiceInfoSet& quality_service_info_set_) + { + SQualityServiceInfo highest_quality_service_info; + for (const auto& info : quality_service_info_set_) + { + if ( (info.request_quality > highest_quality_service_info.request_quality) + || (info.response_quality > highest_quality_service_info.response_quality)) + { + highest_quality_service_info = info; + } + } + return highest_quality_service_info.info; + } + + void GetTopics(std::map& data_type_info_map_) + { + data_type_info_map_.clear(); + + std::map quality_data_type_info_map; + GetTopics(quality_data_type_info_map); + + // transform into target map + for (const auto& quality_data_type_info : quality_data_type_info_map) + { + data_type_info_map_.insert(std::pair(quality_data_type_info.first, quality_data_type_info.second.info)); + } + } + + void GetTopics(std::map& quality_topic_info_map_) + { + quality_topic_info_map_.clear(); + + QualityTopicInfoMultiMap pub_sub_map = GetPublishers(); + QualityTopicInfoMultiMap sub_map = GetSubscribers(); + pub_sub_map.insert(sub_map.begin(), sub_map.end()); + + // transform into a map with the highest quality data type information + quality_topic_info_map_ = ReduceQualityTopicIdMap(pub_sub_map); + } + + void GetTopicNames(std::set& topic_names_) + { + topic_names_.clear(); + + // get publisher & subscriber multi maps + auto pub_multi_map = GetPublishers(); + auto sub_multi_map = GetSubscribers(); + + // filter out unique topic names into a set + for (const auto& publisher : pub_multi_map) + { + topic_names_.insert(publisher.first); + } + for (const auto& subscriber : sub_multi_map) + { + topic_names_.insert(subscriber.first); + } + } + + bool GetTopicDataTypeInformation(const std::string& topic_name_, SDataTypeInformation& data_type_info_) + { + auto info_set = GetPublishers(topic_name_); + const auto sub_info_set = GetSubscribers(topic_name_); + + info_set.insert(sub_info_set.begin(), sub_info_set.end()); + data_type_info_ = GetHighestQualityDataTypeInformation(info_set); + + return !info_set.empty(); + } + + void GetServices(std::map& service_method_info_map_) + { + service_method_info_map_.clear(); + + std::map quality_service_method_info_map; + GetServices(quality_service_method_info_map); + + // transform into target map + for (const auto& quality_service_method_info : quality_service_method_info_map) + { + service_method_info_map_.insert(std::pair(quality_service_method_info.first, quality_service_method_info.second.info)); + } + } + + void GetServices(std::map& quality_service_info_map_) + { + quality_service_info_map_.clear(); + + // transform into a map with the highest quality service method information + quality_service_info_map_ = ReduceQualityServiceIdMap(GetServices()); + } + + void GetServiceMethodNames(std::set& service_method_names_) + { + service_method_names_.clear(); + + // get services multi map + auto multi_map = GetServices(); + + // filter out unique service names into a set + for (const auto& service : multi_map) + { + service_method_names_.insert(service.first); + } + } + + bool GetServiceTypeNames(const std::string& service_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_) + { + const auto service_method_info_set = GetQualityServiceInfoSet(service_name_, method_name_, GetServices()); + + const SServiceMethodInformation service_method_info = GetHighestQualityServiceMethodInformation(service_method_info_set); + req_type_ = service_method_info.request_type.name; + resp_type_ = service_method_info.response_type.name; + + return !service_method_info_set.empty(); + } + + bool GetServiceDescription(const std::string& service_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_) + { + const auto service_method_info_set = GetQualityServiceInfoSet(service_name_, method_name_, GetServices()); + + const SServiceMethodInformation service_method_info = GetHighestQualityServiceMethodInformation(service_method_info_set); + req_desc_ = service_method_info.request_type.descriptor; + resp_desc_ = service_method_info.response_type.descriptor; + + return !service_method_info_set.empty(); + } + + void GetClients(std::map& client_method_info_map_) + { + client_method_info_map_.clear(); + + std::map quality_client_method_info_map_; + GetClients(quality_client_method_info_map_); + + // transform into target map + for (const auto& quality_client_method_info : quality_client_method_info_map_) + { + client_method_info_map_.insert(std::pair(quality_client_method_info.first, quality_client_method_info.second.info)); + } + } + + void GetClients(std::map& quality_client_info_map_) + { + quality_client_info_map_.clear(); + + // transform into a map with the highest quality service method information + quality_client_info_map_ = ReduceQualityServiceIdMap(GetClients()); + } + + void GetClientMethodNames(std::set& client_method_names_) + { + client_method_names_.clear(); + + // get services multi map + auto multi_map = GetClients(); + + // filter out unique service names into a set + for (const auto& service : multi_map) + { + client_method_names_.insert(service.first); + } + } + + bool GetClientTypeNames(const std::string& client_name_, const std::string& method_name_, std::string& req_type_, std::string& resp_type_) + { + const auto service_method_info_set = GetQualityServiceInfoSet(client_name_, method_name_, GetClients()); + + const SServiceMethodInformation service_method_info = GetHighestQualityServiceMethodInformation(service_method_info_set); + req_type_ = service_method_info.request_type.name; + resp_type_ = service_method_info.response_type.name; + + return !service_method_info_set.empty(); + } + + bool GetClientDescription(const std::string& client_name_, const std::string& method_name_, std::string& req_desc_, std::string& resp_desc_) + { + const auto service_method_info_set = GetQualityServiceInfoSet(client_name_, method_name_, GetClients()); + + const SServiceMethodInformation service_method_info = GetHighestQualityServiceMethodInformation(service_method_info_set); + req_desc_ = service_method_info.request_type.descriptor; + resp_desc_ = service_method_info.response_type.descriptor; + + return !service_method_info_set.empty(); + } + } +} diff --git a/ecal/core/src/registration/ecal_registration_provider.cpp b/ecal/core/src/registration/ecal_registration_provider.cpp index 90cf714803..c953023a40 100644 --- a/ecal/core/src/registration/ecal_registration_provider.cpp +++ b/ecal/core/src/registration/ecal_registration_provider.cpp @@ -22,34 +22,37 @@ * * All process internal publisher/subscriber, server/clients register here with all their attributes. * - * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. + * These information will be send cyclic (registration refresh) via UDP or SHM to external eCAL processes. * **/ - -#include -#include - -#include "ecal_def.h" -#include "ecal_globals.h" #include "ecal_registration_provider.h" -#include "io/udp/ecal_udp_configurations.h" -#include "io/udp/ecal_udp_sample_sender.h" - +#include #include #include -#include #include #include #include +#include +#include +#include "ecal_def.h" + +#include +#include +#if ECAL_CORE_REGISTRATION_SHM +#include +#endif + +#include "builder/udp_shm_attribute_builder.h" + + namespace eCAL { std::atomic CRegistrationProvider::m_created; - CRegistrationProvider::CRegistrationProvider() : - m_use_registration_udp(false), - m_use_registration_shm(false) + CRegistrationProvider::CRegistrationProvider(const Registration::SAttributes& attr_) : + m_attributes(attr_) { } @@ -62,37 +65,26 @@ namespace eCAL { if(m_created) return; - // send registration to shared memory and to udp - m_use_registration_udp = !Config::Experimental::IsNetworkMonitoringDisabled(); - m_use_registration_shm = Config::Experimental::IsShmMonitoringEnabled(); - - if (m_use_registration_udp) + // TODO Create the registration sender +#if ECAL_CORE_REGISTRATION_SHM + if (m_attributes.shm_enabled) { - // set network attributes - eCAL::UDP::SSenderAttr attr; - attr.address = UDP::GetRegistrationAddress(); - attr.port = UDP::GetRegistrationPort(); - attr.ttl = UDP::GetMulticastTtl(); - attr.broadcast = UDP::IsBroadcast(); - attr.loopback = true; - attr.sndbuf = Config::GetUdpMulticastSndBufSizeBytes(); - - // create udp registration sender - m_reg_sample_snd = std::make_shared(attr); + m_reg_sender = std::make_unique(Registration::BuildSHMAttributes(m_attributes)); + } else +#endif + if (m_attributes.udp_enabled) + { + m_reg_sender = std::make_unique(Registration::BuildUDPSenderAttributes(m_attributes)); } - -#if ECAL_CORE_REGISTRATION_SHM - if (m_use_registration_shm) + else { - std::cout << "Shared memory monitoring is enabled (domain: " << Config::Experimental::GetShmMonitoringDomain() << " - queue size: " << Config::Experimental::GetShmMonitoringQueueSize() << ")" << '\n'; - m_memfile_broadcast.Create(Config::Experimental::GetShmMonitoringDomain(), Config::Experimental::GetShmMonitoringQueueSize()); - m_memfile_broadcast_writer.Bind(&m_memfile_broadcast); + eCAL::Logging::Log(log_level_warning, "[CRegistrationProvider] No registration layer enabled."); + return; } -#endif // start cyclic registration thread m_reg_sample_snd_thread = std::make_shared(std::bind(&CRegistrationProvider::RegisterSendThread, this)); - m_reg_sample_snd_thread->start(std::chrono::milliseconds(Config::GetRegistrationRefreshMs())); + m_reg_sample_snd_thread->start(std::chrono::milliseconds(m_attributes.refresh)); m_created = true; } @@ -101,268 +93,89 @@ namespace eCAL { if(!m_created) return; - // stop cyclic registration thread - m_reg_sample_snd_thread->stop(); - - // add process unregistration sample - AddSample2SampleList(GetProcessUnregisterSample()); + // add unregistration sample to registration loop + AddSingleSample(Registration::GetProcessUnregisterSample()); - if (m_use_registration_udp) - { - // send process unregistration sample over udp - SendSampleList2UDP(); + // wake up registration thread the last time + m_reg_sample_snd_thread->trigger(); - // destroy udp registration sample sender - m_reg_sample_snd.reset(); - } + // stop cyclic registration thread + m_reg_sample_snd_thread->stop(); -#if ECAL_CORE_REGISTRATION_SHM - if (m_use_registration_shm) - { - // broadcast process unregistration sample over shm - SendSampleList2SHM(); - - // destroy shm registration sample writer - m_memfile_broadcast_writer.Unbind(); - m_memfile_broadcast.Destroy(); - } -#endif + // delete registration sender + m_reg_sender.reset(); m_created = false; } - bool CRegistrationProvider::ApplySample(const Registration::Sample& sample_, const bool force_) + // (re)register single sample + bool CRegistrationProvider::RegisterSample(const Registration::Sample& sample_) { if (!m_created) return(false); - // forward all registration samples to outside "customer" (e.g. monitoring, descgate) - { - const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); - for (const auto& iter : m_callback_custom_apply_sample_map) - { - iter.second(sample_); - } - } - - // update sample list - AddSample2SampleList(sample_); + // add registration sample to registration loop + AddSingleSample(sample_); - // if registration is forced - if (force_) - { - // send single registration sample over udp - SendSample2UDP(sample_); - -#if ECAL_CORE_REGISTRATION_SHM - // broadcast (updated) sample list over shm - SendSampleList2SHM(); -#endif - } + // wake up registration thread + m_reg_sample_snd_thread->trigger(); return(true); } - void CRegistrationProvider::SetCustomApplySampleCallback(const std::string& customer_, const ApplySampleCallbackT& callback_) - { - const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); - m_callback_custom_apply_sample_map[customer_] = callback_; - } - - void CRegistrationProvider::RemCustomApplySampleCallback(const std::string& customer_) - { - const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); - auto iter = m_callback_custom_apply_sample_map.find(customer_); - if (iter != m_callback_custom_apply_sample_map.end()) - { - m_callback_custom_apply_sample_map.erase(iter); - } - } - - void CRegistrationProvider::AddSample2SampleList(const Registration::Sample& sample_) - { - const std::lock_guard lock(m_sample_list_mtx); - m_sample_list.samples.push_back(sample_); - } - - bool CRegistrationProvider::SendSample2UDP(const Registration::Sample& sample_) + // unregister single sample + bool CRegistrationProvider::UnregisterSample(const Registration::Sample& sample_) { if (!m_created) return(false); - if (m_use_registration_udp && m_reg_sample_snd) - { - // lock sample buffer - const std::lock_guard lock(m_sample_buffer_mtx); + // add registration sample to registration loop, no need to force registration thread to send + AddSingleSample(sample_); - // serialize single sample - if (SerializeToBuffer(sample_, m_sample_buffer)) - { - // send single sample over udp - return m_reg_sample_snd->Send("reg_sample", m_sample_buffer) != 0; - } - } - return(false); + return(true); } - bool CRegistrationProvider::SendSampleList2UDP() + void CRegistrationProvider::AddSingleSample(const Registration::Sample& sample_) { - if (!m_created) return(false); - bool return_value{ true }; - - // lock sample list - const std::lock_guard lock(m_sample_list_mtx); - - // send all (single) samples over udp - if (m_use_registration_udp && m_reg_sample_snd) - { - for (const auto& sample : m_sample_list.samples) - { - return_value &= SendSample2UDP(sample); - } - } - - return return_value; + const std::lock_guard lock(m_applied_sample_list_mtx); + m_applied_sample_list.samples.push_back(sample_); } -#if ECAL_CORE_REGISTRATION_SHM - bool CRegistrationProvider::SendSampleList2SHM() + void CRegistrationProvider::RegisterSendThread() { - if (!m_created) return(false); - - bool return_value{ true }; - - // send sample list over shm - if (m_use_registration_shm) + // collect all registrations and send them out cyclic { - // lock sample list - const std::lock_guard lock(m_sample_list_mtx); - - // serialize whole sample list - if (SerializeToBuffer(m_sample_list, m_sample_list_buffer)) - { - if (!m_sample_list_buffer.empty()) - { - // broadcast sample list over shm - return_value &= m_memfile_broadcast_writer.Write(m_sample_list_buffer.data(), m_sample_list_buffer.size()); - } - } - } - return return_value; - } -#endif + // create sample list + Registration::SampleList sample_list; - void CRegistrationProvider::ClearSampleList() - { - // lock sample list - const std::lock_guard lock(m_sample_list_mtx); - // clear sample list - m_sample_list.samples.clear(); - } + // and add process registration sample + sample_list.samples.push_back(Registration::GetProcessRegisterSample()); - void CRegistrationProvider::RegisterSendThread() - { #if ECAL_CORE_SUBSCRIBER - // refresh subscriber registration - if (g_subgate() != nullptr) g_subgate()->RefreshRegistrations(); + // add subscriber registrations + if (g_subgate() != nullptr) g_subgate()->GetRegistrations(sample_list); #endif #if ECAL_CORE_PUBLISHER - // refresh publisher registration - if (g_pubgate() != nullptr) g_pubgate()->RefreshRegistrations(); + // add publisher registrations + if (g_pubgate() != nullptr) g_pubgate()->GetRegistrations(sample_list); #endif #if ECAL_CORE_SERVICE - // refresh server registration - if (g_servicegate() != nullptr) g_servicegate()->RefreshRegistrations(); + // add server registrations + if (g_servicegate() != nullptr) g_servicegate()->GetRegistrations(sample_list); - // refresh client registration - if (g_clientgate() != nullptr) g_clientgate()->RefreshRegistrations(); + // add client registrations + if (g_clientgate() != nullptr) g_clientgate()->GetRegistrations(sample_list); #endif - // send out sample list over udp - SendSampleList2UDP(); + // send collected registration sample list + m_reg_sender->SendSampleList(sample_list); -#if ECAL_CORE_REGISTRATION_SHM - // broadcast sample list over shm - SendSampleList2SHM(); -#endif - - // clear registration sample list - ClearSampleList(); - - // add process registration sample to internal sample list as first sample (for next registration loop) - AddSample2SampleList(GetProcessRegisterSample()); - } - - Registration::Sample CRegistrationProvider::GetProcessRegisterSample() - { - Registration::Sample process_sample; - process_sample.cmd_type = bct_reg_process; - auto& process_sample_process = process_sample.process; - process_sample_process.hname = Process::GetHostName(); - process_sample_process.hgname = Process::GetHostGroupName(); - process_sample_process.pid = Process::GetProcessID(); - process_sample_process.pname = Process::GetProcessName(); - process_sample_process.uname = Process::GetUnitName(); - process_sample_process.pparam = Process::GetProcessParameter(); - process_sample_process.state.severity = static_cast(g_process_severity); - process_sample_process.state.severity_level = static_cast(g_process_severity_level); - process_sample_process.state.info = g_process_info; -#if ECAL_CORE_TIMEPLUGIN - if (g_timegate() == nullptr) - { - process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; - } - else - { - if (!g_timegate()->IsSynchronized()) + // send asynchronously applied samples at the end of the registration loop { - process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; + const std::lock_guard lock(m_applied_sample_list_mtx); + m_reg_sender->SendSampleList(m_applied_sample_list); + m_applied_sample_list.samples.clear(); } - else - { - switch (g_timegate()->GetSyncMode()) - { - case CTimeGate::eTimeSyncMode::realtime: - process_sample_process.tsync_state = Registration::eTSyncState::tsync_realtime; - break; - case CTimeGate::eTimeSyncMode::replay: - process_sample_process.tsync_state = Registration::eTSyncState::tsync_replay; - break; - default: - process_sample_process.tsync_state = Registration::eTSyncState::tsync_none; - break; - } - } - process_sample_process.tsync_mod_name = g_timegate()->GetName(); } -#endif - - // eCAL initialization state - const unsigned int comp_state(g_globals()->GetComponents()); - process_sample_process.component_init_state = static_cast(comp_state); - std::string component_info; - if ((comp_state & Init::Publisher) != 0u) component_info += "|pub"; - if ((comp_state & Init::Subscriber) != 0u) component_info += "|sub"; - if ((comp_state & Init::Logging) != 0u) component_info += "|log"; - if ((comp_state & Init::TimeSync) != 0u) component_info += "|time"; - if (!component_info.empty()) component_info = component_info.substr(1); - process_sample_process.component_init_info = component_info; - - process_sample_process.ecal_runtime_version = GetVersionString(); - - return process_sample; - } - - Registration::Sample CRegistrationProvider::GetProcessUnregisterSample() - { - Registration::Sample process_sample; - process_sample.cmd_type = bct_unreg_process; - auto& process_sample_process = process_sample.process; - process_sample_process.hname = Process::GetHostName(); - process_sample_process.pid = Process::GetProcessID(); - process_sample_process.pname = Process::GetProcessName(); - process_sample_process.uname = Process::GetUnitName(); - - return process_sample; } } diff --git a/ecal/core/src/registration/ecal_registration_provider.h b/ecal/core/src/registration/ecal_registration_provider.h index 098a9ef2da..421d46a0e5 100644 --- a/ecal/core/src/registration/ecal_registration_provider.h +++ b/ecal/core/src/registration/ecal_registration_provider.h @@ -28,76 +28,43 @@ #pragma once -#include "io/udp/ecal_udp_sample_sender.h" +#include "registration/ecal_registration_sender.h" #include "util/ecal_thread.h" - -#if ECAL_CORE_REGISTRATION_SHM -#include "shm/ecal_memfile_broadcast.h" -#include "shm/ecal_memfile_broadcast_writer.h" -#endif - -#include "serialization/ecal_serialize_sample_registration.h" +#include "attributes/registration_attributes.h" #include #include #include -#include -#include + +#include "util/ecal_thread.h" namespace eCAL { class CRegistrationProvider { public: - CRegistrationProvider(); + CRegistrationProvider(const Registration::SAttributes& attr_); ~CRegistrationProvider(); void Start(); void Stop(); - bool ApplySample(const Registration::Sample& sample_, bool force_); - - using ApplySampleCallbackT = std::function; - void SetCustomApplySampleCallback(const std::string& customer_, const ApplySampleCallbackT& callback_); - void RemCustomApplySampleCallback(const std::string& customer_); + bool RegisterSample(const Registration::Sample& sample_); + bool UnregisterSample(const Registration::Sample& sample_); protected: - void AddSample2SampleList(const Registration::Sample& sample_); - bool SendSample2UDP(const Registration::Sample& sample_); - - bool SendSampleList2UDP(); -#if ECAL_CORE_REGISTRATION_SHM - bool SendSampleList2SHM(); -#endif - void ClearSampleList(); - + void AddSingleSample(const Registration::Sample& sample_); void RegisterSendThread(); - Registration::Sample GetProcessRegisterSample(); - Registration::Sample GetProcessUnregisterSample(); - - static std::atomic m_created; - - std::shared_ptr m_reg_sample_snd; - std::shared_ptr m_reg_sample_snd_thread; - - std::mutex m_sample_buffer_mtx; - std::vector m_sample_buffer; - - std::mutex m_sample_list_mtx; - Registration::SampleList m_sample_list; + static std::atomic m_created; -#if ECAL_CORE_REGISTRATION_SHM - std::vector m_sample_list_buffer; - CMemoryFileBroadcast m_memfile_broadcast; - CMemoryFileBroadcastWriter m_memfile_broadcast_writer; -#endif + std::unique_ptr m_reg_sender; + std::shared_ptr m_reg_sample_snd_thread; - bool m_use_registration_udp; - bool m_use_registration_shm; + std::mutex m_applied_sample_list_mtx; + Registration::SampleList m_applied_sample_list; - std::mutex m_callback_custom_apply_sample_map_mtx; - std::map m_callback_custom_apply_sample_map; + Registration::SAttributes m_attributes; }; } diff --git a/ecal/core/src/registration/ecal_registration_receiver.cpp b/ecal/core/src/registration/ecal_registration_receiver.cpp index 1ef8bfbf08..23a60a8002 100644 --- a/ecal/core/src/registration/ecal_registration_receiver.cpp +++ b/ecal/core/src/registration/ecal_registration_receiver.cpp @@ -25,20 +25,26 @@ * **/ -#include "ecal_registration_receiver.h" -#include "ecal_global_accessors.h" +#include "registration/ecal_registration_receiver.h" -#include "pubsub/ecal_subgate.h" -#include "pubsub/ecal_pubgate.h" -#include "service/ecal_clientgate.h" +#include "registration/ecal_registration_timeout_provider.h" +#include "util/ecal_thread.h" +#include "registration/udp/ecal_registration_receiver_udp.h" +#if ECAL_CORE_REGISTRATION_SHM +#include "registration/shm/ecal_registration_receiver_shm.h" +#endif #include "io/udp/ecal_udp_configurations.h" +#include #include #include #include #include #include +#include "builder/udp_shm_attribute_builder.h" +#include "builder/sample_applier_attribute_builder.h" + namespace eCAL { ////////////////////////////////////////////////////////////////// @@ -46,55 +52,62 @@ namespace eCAL ////////////////////////////////////////////////////////////////// std::atomic CRegistrationReceiver::m_created; - CRegistrationReceiver::CRegistrationReceiver() : - m_network(Config::IsNetworkEnabled()), - m_loopback(false), - m_callback_pub(nullptr), - m_callback_sub(nullptr), - m_callback_service(nullptr), - m_callback_client(nullptr), - m_callback_process(nullptr), - m_use_registration_udp(false), - m_use_registration_shm(false), - m_host_group_name(Process::GetHostGroupName()) + CRegistrationReceiver::CRegistrationReceiver(const Registration::SAttributes& attr_) + : m_timeout_provider(nullptr) + , m_timeout_provider_thread(nullptr) + , m_registration_receiver_udp(nullptr) + , m_registration_receiver_shm(nullptr) + , m_attributes(attr_) + , m_sample_applier(Registration::SampleApplier::BuildSampleApplierAttributes(attr_)) { + // Connect User registration callback and gates callback with the sample applier + m_sample_applier.SetCustomApplySampleCallback("gates", [](const eCAL::Registration::Sample& sample_) + { + Registration::CSampleApplierGates::ApplySample(sample_); + }); + m_sample_applier.SetCustomApplySampleCallback("custom_registration", [this](const eCAL::Registration::Sample& sample_) + { + m_user_applier.ApplySample(sample_); + }); + } CRegistrationReceiver::~CRegistrationReceiver() { Stop(); + + m_sample_applier.RemCustomApplySampleCallback("custom_registration"); + m_sample_applier.RemCustomApplySampleCallback("gates"); } void CRegistrationReceiver::Start() { if(m_created) return; - // receive registration from shared memory and or udp - m_use_registration_udp = !Config::Experimental::IsNetworkMonitoringDisabled(); - m_use_registration_shm = Config::Experimental::IsShmMonitoringEnabled(); + m_timeout_provider = std::make_unique>( + m_attributes.timeout, + [this](const Registration::Sample& sample_) + { + return m_sample_applier.ApplySample(sample_); + } + ); + m_sample_applier.SetCustomApplySampleCallback("timeout", [this](const eCAL::Registration::Sample& sample_) + { + m_timeout_provider->ApplySample(sample_); + }); + m_timeout_provider_thread = std::make_unique([this]() {m_timeout_provider->CheckForTimeouts(); }); + m_timeout_provider_thread->start(std::chrono::milliseconds(100)); - if (m_use_registration_udp) + // Why do we have here different behaviour than in the registration provider? + if (m_attributes.udp_enabled) { - // set network attributes - eCAL::UDP::SReceiverAttr attr; - attr.address = UDP::GetRegistrationAddress(); - attr.port = UDP::GetRegistrationPort(); - attr.broadcast = UDP::IsBroadcast(); - attr.loopback = true; - attr.rcvbuf = Config::GetUdpMulticastRcvBufSizeBytes(); - - // start registration sample receiver - m_registration_receiver = std::make_shared(attr, std::bind(&CRegistrationReceiver::HasSample, this, std::placeholders::_1), std::bind(&CRegistrationReceiver::ApplySerializedSample, this, std::placeholders::_1, std::placeholders::_2)); + m_registration_receiver_udp = std::make_unique([this](const Registration::Sample& sample_) {return m_sample_applier.ApplySample(sample_);}, Registration::BuildUDPReceiverAttributes(m_attributes)); } #if ECAL_CORE_REGISTRATION_SHM - if (m_use_registration_shm) + if (m_attributes.shm_enabled) { - m_memfile_broadcast.Create(Config::Experimental::GetShmMonitoringDomain(), Config::Experimental::GetShmMonitoringQueueSize()); - m_memfile_broadcast.FlushLocalEventQueue(); - m_memfile_broadcast_reader.Bind(&m_memfile_broadcast); - - m_memfile_reg_rcv.Create(&m_memfile_broadcast_reader); + m_registration_receiver_shm = std::make_unique([this](const Registration::Sample& sample_) {return m_sample_applier.ApplySample(sample_); }, Registration::BuildSHMAttributes(m_attributes)); } #endif @@ -106,281 +119,48 @@ namespace eCAL if(!m_created) return; // stop network registration receive thread - m_registration_receiver = nullptr; - - // stop network registration receive thread - if (m_use_registration_udp) + if (m_attributes.udp_enabled) { - m_registration_receiver = nullptr; + m_registration_receiver_udp = nullptr; } #if ECAL_CORE_REGISTRATION_SHM - if (m_use_registration_shm) + if (m_attributes.shm_enabled) { - // stop memfile registration receive thread and unbind reader - m_memfile_broadcast_reader.Unbind(); - m_memfile_broadcast.Destroy(); + m_registration_receiver_shm = nullptr; } #endif - // reset callbacks - m_callback_pub = nullptr; - m_callback_sub = nullptr; - m_callback_service = nullptr; - m_callback_client = nullptr; - m_callback_process = nullptr; + m_timeout_provider_thread = nullptr; + m_sample_applier.RemCustomApplySampleCallback("timeout"); + m_timeout_provider = nullptr; - // finished m_created = false; } void CRegistrationReceiver::EnableLoopback(bool state_) { - m_loopback = state_; - } - - bool CRegistrationReceiver::ApplySerializedSample(const char* serialized_sample_data_, size_t serialized_sample_size_) - { - if(!m_created) return false; - - Registration::Sample sample; - if (!DeserializeFromBuffer(serialized_sample_data_, serialized_sample_size_, sample)) return false; - - return ApplySample(sample); - } - - bool CRegistrationReceiver::ApplySample(const Registration::Sample& sample_) - { - if (!m_created) return false; - - // forward all registration samples to outside "customer" (e.g. monitoring, descgate) - { - const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); - for (const auto& iter : m_callback_custom_apply_sample_map) - { - iter.second(sample_); - } - } - - std::string reg_sample; - if (m_callback_pub - || m_callback_sub - || m_callback_service - || m_callback_client - || m_callback_process - ) - { - SerializeToBuffer(sample_, reg_sample); - } - - switch (sample_.cmd_type) - { - case bct_none: - case bct_set_sample: - break; - case bct_reg_process: - case bct_unreg_process: - // unregistration event not implemented currently - if (m_callback_process) m_callback_process(reg_sample.c_str(), static_cast(reg_sample.size())); - break; -#if ECAL_CORE_SERVICE - case bct_reg_service: - if (g_clientgate() != nullptr) g_clientgate()->ApplyServiceRegistration(sample_); - if (m_callback_service) m_callback_service(reg_sample.c_str(), static_cast(reg_sample.size())); - break; - case bct_unreg_service: - // current client implementation doesn't need that information - if (m_callback_service) m_callback_service(reg_sample.c_str(), static_cast(reg_sample.size())); - break; -#endif - case bct_reg_client: - case bct_unreg_client: - // current service implementation doesn't need that information - if (m_callback_client) m_callback_client(reg_sample.c_str(), static_cast(reg_sample.size())); - break; - case bct_reg_subscriber: - case bct_unreg_subscriber: - ApplySubscriberRegistration(sample_); - if (m_callback_sub) m_callback_sub(reg_sample.c_str(), static_cast(reg_sample.size())); - break; - case bct_reg_publisher: - case bct_unreg_publisher: - ApplyPublisherRegistration(sample_); - if (m_callback_pub) m_callback_pub(reg_sample.c_str(), static_cast(reg_sample.size())); - break; - default: - Logging::Log(log_level_debug1, "CRegistrationReceiver::ApplySample : unknown sample type"); - break; - } - - return true; + m_sample_applier.EnableLoopback(state_); } bool CRegistrationReceiver::AddRegistrationCallback(enum eCAL_Registration_Event event_, const RegistrationCallbackT& callback_) { - if (!m_created) return false; - switch (event_) - { - case reg_event_publisher: - m_callback_pub = callback_; - return true; - case reg_event_subscriber: - m_callback_sub = callback_; - return true; - case reg_event_service: - m_callback_service = callback_; - return true; - case reg_event_client: - m_callback_client = callback_; - return true; - case reg_event_process: - m_callback_process = callback_; - return true; - default: - return false; - } + return m_user_applier.AddRegistrationCallback(event_, callback_); } bool CRegistrationReceiver::RemRegistrationCallback(enum eCAL_Registration_Event event_) { - if (!m_created) return false; - switch (event_) - { - case reg_event_publisher: - m_callback_pub = nullptr; - return true; - case reg_event_subscriber: - m_callback_sub = nullptr; - return true; - case reg_event_service: - m_callback_service = nullptr; - return true; - case reg_event_client: - m_callback_client = nullptr; - return true; - case reg_event_process: - m_callback_process = nullptr; - return true; - default: - return false; - } - } - - void CRegistrationReceiver::ApplySubscriberRegistration(const Registration::Sample& sample_) - { -#if ECAL_CORE_PUBLISHER - if (g_pubgate() == nullptr) return; - - // process registrations from same host group - if (IsHostGroupMember(sample_)) - { - // do not register local entities, only if loop back flag is set true - if (m_loopback || (sample_.topic.pid != Process::GetProcessID())) - { - switch (sample_.cmd_type) - { - case bct_reg_subscriber: - g_pubgate()->ApplySubRegistration(sample_); - break; - case bct_unreg_subscriber: - g_pubgate()->ApplySubUnregistration(sample_); - break; - default: - break; - } - } - } - // process external registrations - else - { - if (m_network) - { - switch (sample_.cmd_type) - { - case bct_reg_subscriber: - g_pubgate()->ApplySubRegistration(sample_); - break; - case bct_unreg_subscriber: - g_pubgate()->ApplySubUnregistration(sample_); - break; - default: - break; - } - } - } -#endif - } - - void CRegistrationReceiver::ApplyPublisherRegistration(const Registration::Sample& sample_) - { -#if ECAL_CORE_SUBSCRIBER - if (g_subgate() == nullptr) return; - - // process registrations from same host group - if (IsHostGroupMember(sample_)) - { - // do not register local entities, only if loop back flag is set true - if (m_loopback || (sample_.topic.pid != Process::GetProcessID())) - { - switch (sample_.cmd_type) - { - case bct_reg_publisher: - g_subgate()->ApplyPubRegistration(sample_); - break; - case bct_unreg_publisher: - g_subgate()->ApplyPubUnregistration(sample_); - break; - default: - break; - } - } - } - // process external registrations - else - { - if (m_network) - { - switch (sample_.cmd_type) - { - case bct_reg_publisher: - g_subgate()->ApplyPubRegistration(sample_); - break; - case bct_unreg_publisher: - g_subgate()->ApplyPubUnregistration(sample_); - break; - default: - break; - } - } - } -#endif - } - - bool CRegistrationReceiver::IsHostGroupMember(const Registration::Sample& sample_) - { - const std::string& sample_host_group_name = sample_.topic.hgname.empty() ? sample_.topic.hname : sample_.topic.hgname; - - if (sample_host_group_name.empty() || m_host_group_name.empty()) - return false; - if (sample_host_group_name != m_host_group_name) - return false; - - return true; + return m_user_applier.RemRegistrationCallback(event_); } void CRegistrationReceiver::SetCustomApplySampleCallback(const std::string& customer_, const ApplySampleCallbackT& callback_) { - const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); - m_callback_custom_apply_sample_map[customer_] = callback_; + m_sample_applier.SetCustomApplySampleCallback(customer_, callback_); } void CRegistrationReceiver::RemCustomApplySampleCallback(const std::string& customer_) { - const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); - auto iter = m_callback_custom_apply_sample_map.find(customer_); - if(iter != m_callback_custom_apply_sample_map.end()) - { - m_callback_custom_apply_sample_map.erase(iter); - } + m_sample_applier.RemCustomApplySampleCallback(customer_); } + } diff --git a/ecal/core/src/registration/ecal_registration_receiver.h b/ecal/core/src/registration/ecal_registration_receiver.h index 2c58749cfe..6803225866 100644 --- a/ecal/core/src/registration/ecal_registration_receiver.h +++ b/ecal/core/src/registration/ecal_registration_receiver.h @@ -30,12 +30,11 @@ #include #include -#include "io/udp/ecal_udp_sample_receiver.h" #include "serialization/ecal_struct_sample_registration.h" - -#if ECAL_CORE_REGISTRATION_SHM -#include "ecal_registration_receiver_shm.h" -#endif +#include "registration/ecal_registration_sample_applier.h" +#include "registration/ecal_registration_sample_applier_gates.h" +#include "registration/ecal_registration_sample_applier_user.h" +#include "attributes/registration_attributes.h" #include #include @@ -47,20 +46,29 @@ namespace eCAL { + class CRegistrationReceiverUDP; + class CRegistrationReceiverSHM; + + namespace Registration + { + template + class CTimeoutProvider; + } + class CCallbackThread; + class CRegistrationReceiver { public: - CRegistrationReceiver(); + CRegistrationReceiver(const Registration::SAttributes& attr_); ~CRegistrationReceiver(); + //what about the rest of the rule of 5? + void Start(); void Stop(); void EnableLoopback(bool state_); - bool HasSample(const std::string& /*sample_name_*/) { return(true); }; - bool ApplySample(const Registration::Sample& sample_); - bool AddRegistrationCallback(enum eCAL_Registration_Event event_, const RegistrationCallbackT& callback_); bool RemRegistrationCallback(enum eCAL_Registration_Event event_); @@ -68,39 +76,27 @@ namespace eCAL void SetCustomApplySampleCallback(const std::string& customer_, const ApplySampleCallbackT& callback_); void RemCustomApplySampleCallback(const std::string& customer_); - protected: - bool ApplySerializedSample(const char* serialized_sample_data_, size_t serialized_sample_size_); - - void ApplySubscriberRegistration(const eCAL::Registration::Sample& sample_); - void ApplyPublisherRegistration(const eCAL::Registration::Sample& sample_); - - bool IsHostGroupMember(const eCAL::Registration::Sample& sample_); - + private: + // why is this a static variable? can someone explain? static std::atomic m_created; - bool m_network; - bool m_loopback; - - RegistrationCallbackT m_callback_pub; - RegistrationCallbackT m_callback_sub; - RegistrationCallbackT m_callback_service; - RegistrationCallbackT m_callback_client; - RegistrationCallbackT m_callback_process; - - std::shared_ptr m_registration_receiver; -#if ECAL_CORE_REGISTRATION_SHM - CMemoryFileBroadcast m_memfile_broadcast; - CMemoryFileBroadcastReader m_memfile_broadcast_reader; + // this class gets samples and tracks them for timouts + std::unique_ptr> m_timeout_provider; + std::unique_ptr m_timeout_provider_thread; - CMemfileRegistrationReceiver m_memfile_reg_rcv; + std::unique_ptr m_registration_receiver_udp; +#if ECAL_CORE_REGISTRATION_SHM + std::unique_ptr m_registration_receiver_shm; #endif - bool m_use_registration_udp; - bool m_use_registration_shm; + // This class distributes samples to all everyone who is interested in being notified about samples + Registration::CSampleApplier m_sample_applier; - std::mutex m_callback_custom_apply_sample_map_mtx; - std::map m_callback_custom_apply_sample_map; + // These classes are interested in being notified about samples + // Possibly remove these from this class + // The custom user callbacks (who receive serialized samples), e.g. registration events. + Registration::CSampleApplierUser m_user_applier; - std::string m_host_group_name; + Registration::SAttributes m_attributes; }; } diff --git a/ecal/core/src/registration/ecal_registration_sample_applier.cpp b/ecal/core/src/registration/ecal_registration_sample_applier.cpp new file mode 100644 index 0000000000..0db73c2030 --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_sample_applier.cpp @@ -0,0 +1,131 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "registration/ecal_registration_sample_applier.h" + +namespace eCAL +{ + namespace Registration + { + ////////////////////////////////////////////////////////////////// + // CSampleApplier + ////////////////////////////////////////////////////////////////// + CSampleApplier::CSampleApplier(const SampleApplier::SAttributes& attr_) + : m_attributes(attr_) + { + } + + void CSampleApplier::EnableLoopback(bool state_) + { + m_attributes.loopback = state_; + } + + bool CSampleApplier::ApplySample(const Registration::Sample& sample_) + { + if (!AcceptRegistrationSample(sample_)) + { + Logging::Log(log_level_debug1, "CSampleApplier::ApplySample : Incoming sample discarded"); + return false; + } + + // forward all registration samples to outside "customer" (e.g. monitoring, descgate, pub/subgate/client/service gates) + { + const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); + for (const auto& iter : m_callback_custom_apply_sample_map) + { + iter.second(sample_); + } + } + return true; + } + + bool CSampleApplier::IsHostGroupMember(const Registration::Sample& sample_) const + { + std::string host_group_name; + const std::string host_name = sample_.identifier.host_name; + switch (sample_.cmd_type) + { + case bct_reg_publisher: + case bct_unreg_publisher: + case bct_reg_subscriber: + case bct_unreg_subscriber: + host_group_name = sample_.topic.hgname; + break; + case bct_reg_service: + case bct_unreg_service: + //host_group_name = sample_.service.hgname; // TODO: we need to add hgname attribute to services + break; + case bct_reg_client: + case bct_unreg_client: + //host_group_name = sample_.client.hgname; // TODO: we need to add hgname attribute to clients + break; + default: + break; + } + + const std::string& sample_host_group_name = host_group_name.empty() ? host_name : host_group_name; + + if (sample_host_group_name.empty() || m_attributes.host_group_name.empty()) + return false; + if (sample_host_group_name != m_attributes.host_group_name) + return false; + + return true; + } + + bool CSampleApplier::IsSameProcess(const Registration::Sample& sample_) const + { + // is this actually sufficient? should we also check host_name? + const int32_t pid = sample_.identifier.process_id; + return pid == m_attributes.process_id; + } + + bool CSampleApplier::AcceptRegistrationSample(const Registration::Sample& sample_) + { + // check if the sample is from the same host group + if (IsHostGroupMember(sample_)) + { + // register if the sample is from another process + // or if loopback mode is enabled + return !IsSameProcess(sample_) || m_attributes.loopback; + } + else + { + // if the sample is from an external host, register only if network mode is enabled + return m_attributes.network_enabled; + } + } + + void CSampleApplier::SetCustomApplySampleCallback(const std::string& customer_, const ApplySampleCallbackT& callback_) + { + const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); + m_callback_custom_apply_sample_map[customer_] = callback_; + } + + void CSampleApplier::RemCustomApplySampleCallback(const std::string& customer_) + { + const std::lock_guard lock(m_callback_custom_apply_sample_map_mtx); + auto iter = m_callback_custom_apply_sample_map.find(customer_); + if (iter != m_callback_custom_apply_sample_map.end()) + { + m_callback_custom_apply_sample_map.erase(iter); + } + } + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/ecal_registration_sample_applier.h b/ecal/core/src/registration/ecal_registration_sample_applier.h new file mode 100644 index 0000000000..5007f2e9d8 --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_sample_applier.h @@ -0,0 +1,69 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL Sample Applier + * + * This class applies incoming samples to everyone who is interested. +**/ + +#pragma once + +#include +#include + +#include "serialization/ecal_struct_sample_registration.h" +#include "attributes/sample_applier_attributes.h" + +#include +#include +#include +#include + +namespace eCAL +{ + namespace Registration + { + class CSampleApplier + { + public: + CSampleApplier(const SampleApplier::SAttributes& attr_); + + // to be removed for eCAL 6, but keep until eCAL 5.14 + void EnableLoopback(bool state_); + bool ApplySample(const Registration::Sample& sample_); + + using ApplySampleCallbackT = std::function; + void SetCustomApplySampleCallback(const std::string& customer_, const ApplySampleCallbackT& callback_); + void RemCustomApplySampleCallback(const std::string& customer_); + + private: + bool IsSameProcess(const Registration::Sample& sample_) const; + bool IsHostGroupMember(const eCAL::Registration::Sample& sample_) const; + + bool AcceptRegistrationSample(const Registration::Sample& sample_); + + SampleApplier::SAttributes m_attributes; + + std::mutex m_callback_custom_apply_sample_map_mtx; + // We need to check the performance now. Unlike before the pub / subgates also go through the map + std::map m_callback_custom_apply_sample_map; + }; + } +} diff --git a/ecal/core/src/registration/ecal_registration_sample_applier_gates.cpp b/ecal/core/src/registration/ecal_registration_sample_applier_gates.cpp new file mode 100644 index 0000000000..e761409695 --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_sample_applier_gates.cpp @@ -0,0 +1,74 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "registration/ecal_registration_sample_applier_gates.h" +#include "ecal_global_accessors.h" +#include "pubsub/ecal_subgate.h" +#include "pubsub/ecal_pubgate.h" +#include "service/ecal_clientgate.h" + +#include + +namespace eCAL +{ + namespace Registration + { + void CSampleApplierGates::ApplySample(const eCAL::Registration::Sample& sample_) + { + switch (sample_.cmd_type) + { + case bct_none: + case bct_set_sample: + case bct_reg_process: + case bct_unreg_process: + break; +#if ECAL_CORE_SERVICE + case bct_reg_service: + if (g_clientgate() != nullptr) g_clientgate()->ApplyServiceRegistration(sample_); + break; +#endif + case bct_unreg_service: + break; + case bct_reg_client: + case bct_unreg_client: + // current client implementation doesn't need that information + break; +#if ECAL_CORE_PUBLISHER + case bct_reg_subscriber: + if (g_pubgate() != nullptr) g_pubgate()->ApplySubRegistration(sample_); + break; + case bct_unreg_subscriber: + if (g_pubgate() != nullptr) g_pubgate()->ApplySubUnregistration(sample_); + break; +#endif +#if ECAL_CORE_SUBSCRIBER + case bct_reg_publisher: + if (g_subgate() != nullptr) g_subgate()->ApplyPubRegistration(sample_); + break; + case bct_unreg_publisher: + if (g_subgate() != nullptr) g_subgate()->ApplyPubUnregistration(sample_); + break; +#endif + default: + Logging::Log(log_level_debug1, "CGatesApplier::ApplySample : unknown sample type"); + break; + } + } + } +} \ No newline at end of file diff --git a/ecal/core/src/config/ecal_config_reader_hlp.h b/ecal/core/src/registration/ecal_registration_sample_applier_gates.h similarity index 63% rename from ecal/core/src/config/ecal_config_reader_hlp.h rename to ecal/core/src/registration/ecal_registration_sample_applier_gates.h index 14bb5cb9b5..c2bf8bf7f3 100644 --- a/ecal/core/src/config/ecal_config_reader_hlp.h +++ b/ecal/core/src/registration/ecal_registration_sample_applier_gates.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,23 @@ */ /** - * @brief Global database class helper + * @brief eCAL Sample Applier Gates + * + * This class applies incoming samples to the registration gates **/ #pragma once -#include "ecal_config_reader.h" -#include "ecal_def_ini.h" -#include "ecal_global_accessors.h" +#include "serialization/ecal_struct_sample_registration.h" -// macro to simplify configuration parameter access (internal use only) -// eCALPAR(GROUP,PAR) -> eCAL::g_config()->get(GROUP_SECTION_S, GROUP_PAR_S, GROUP_PAR) -#define eCALPAR(x,y) eCAL::g_config()->get(x##_SECTION_S, x##_##y##_S, x##_##y) +namespace eCAL +{ + namespace Registration + { + class CSampleApplierGates + { + public: + static void ApplySample(const eCAL::Registration::Sample& sample_); + }; + } +} diff --git a/ecal/core/src/registration/ecal_registration_sample_applier_user.cpp b/ecal/core/src/registration/ecal_registration_sample_applier_user.cpp new file mode 100644 index 0000000000..0edae0f70e --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_sample_applier_user.cpp @@ -0,0 +1,120 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "registration/ecal_registration_sample_applier_user.h" +#include "serialization/ecal_serialize_sample_registration.h" + +namespace eCAL +{ + namespace Registration + { + bool CSampleApplierUser::AddRegistrationCallback(eCAL_Registration_Event event_, const RegistrationCallbackT& callback_) + { + switch (event_) + { + case reg_event_publisher: + m_callback_pub = callback_; + return true; + case reg_event_subscriber: + m_callback_sub = callback_; + return true; + case reg_event_service: + m_callback_service = callback_; + return true; + case reg_event_client: + m_callback_client = callback_; + return true; + case reg_event_process: + m_callback_process = callback_; + return true; + default: + return false; + } + } + + bool CSampleApplierUser::RemRegistrationCallback(eCAL_Registration_Event event_) + { + switch (event_) + { + case reg_event_publisher: + m_callback_pub = nullptr; + return true; + case reg_event_subscriber: + m_callback_sub = nullptr; + return true; + case reg_event_service: + m_callback_service = nullptr; + return true; + case reg_event_client: + m_callback_client = nullptr; + return true; + case reg_event_process: + m_callback_process = nullptr; + return true; + default: + return false; + } + } + + void CSampleApplierUser::ApplySample(const eCAL::Registration::Sample& sample_) + { + RegistrationCallbackT reg_callback(nullptr); + switch (sample_.cmd_type) + { + case bct_none: + case bct_set_sample: + break; + case bct_reg_process: + case bct_unreg_process: + // unregistration event not implemented currently + reg_callback = m_callback_process; + break; + case bct_reg_service: + case bct_unreg_service: + reg_callback = m_callback_service; + break; + case bct_reg_client: + case bct_unreg_client: + // current client implementation doesn't need that information + reg_callback = m_callback_client; + break; + case bct_reg_subscriber: + case bct_unreg_subscriber: + reg_callback = m_callback_sub; + break; + case bct_reg_publisher: + case bct_unreg_publisher: + reg_callback = m_callback_pub; + break; + default: + break; + } + + // call user registration callback + if (reg_callback) + { + std::string reg_sample; + if (SerializeToBuffer(sample_, reg_sample)) + { + reg_callback(reg_sample.c_str(), static_cast(reg_sample.size())); + } + } + } + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/ecal_registration_sample_applier_user.h b/ecal/core/src/registration/ecal_registration_sample_applier_user.h new file mode 100644 index 0000000000..f6fcd1f07d --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_sample_applier_user.h @@ -0,0 +1,57 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL registration receiver + * + * Receives registration information from the sample applier and forwards them to + * user defined callbacks. +**/ + +#pragma once + +#include +#include + +#include "serialization/ecal_struct_sample_registration.h" + +namespace eCAL +{ + namespace Registration + { + class CSampleApplierUser + { + public: + bool AddRegistrationCallback(enum eCAL_Registration_Event event_, const RegistrationCallbackT& callback_); + bool RemRegistrationCallback(enum eCAL_Registration_Event event_); + + void ApplySample(const eCAL::Registration::Sample& sample_); + + private: + // in the future this may be stored in a map? or somehow differently + RegistrationCallbackT m_callback_pub = nullptr; + RegistrationCallbackT m_callback_sub = nullptr; + RegistrationCallbackT m_callback_service = nullptr; + RegistrationCallbackT m_callback_client = nullptr; + RegistrationCallbackT m_callback_process = nullptr; + + // protect by mutexes? very likeley need to! + }; + } +} diff --git a/ecal/core/src/registration/ecal_registration_sender.h b/ecal/core/src/registration/ecal_registration_sender.h new file mode 100644 index 0000000000..4f9d6e504f --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_sender.h @@ -0,0 +1,57 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL registration provider + * + * All process internal publisher/subscriber, server/clients register here with all their attributes. + * + * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. + * +**/ + +#pragma once + +#include "serialization/ecal_struct_sample_registration.h" + + +namespace eCAL +{ + class CRegistrationSender + { + public: + CRegistrationSender() = default; + virtual ~CRegistrationSender() = default; + + // Copy constructor + CRegistrationSender(const CRegistrationSender& other) = delete; + + // Copy assignment operator + CRegistrationSender& operator=(const CRegistrationSender & other) = delete; + + // Move constructor + CRegistrationSender(CRegistrationSender && other) noexcept = delete; + + // Move assignment operator + CRegistrationSender& operator=(CRegistrationSender && other) noexcept = delete; + + //virtual bool SendSample(const Registration::Sample& sample_) = 0; + virtual bool SendSampleList(const Registration::SampleList& sample_list) = 0; + }; +} diff --git a/ecal/core/src/registration/ecal_registration_timeout_provider.cpp b/ecal/core/src/registration/ecal_registration_timeout_provider.cpp new file mode 100644 index 0000000000..2817190807 --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_timeout_provider.cpp @@ -0,0 +1,121 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL registration receiver + * + * Receives registration information from external eCAL processes and forwards them to + * the internal publisher/subscriber, server/clients. + * +**/ + +#include "registration/ecal_registration_timeout_provider.h" + + +namespace eCAL +{ + namespace Registration + { + bool IsUnregistrationSample(const Registration::Sample& sample_) + { + return sample_.cmd_type == bct_unreg_client || + sample_.cmd_type == bct_unreg_process || + sample_.cmd_type == bct_unreg_publisher || + sample_.cmd_type == bct_unreg_service || + sample_.cmd_type == bct_unreg_subscriber; + } + + Sample CreateUnregisterSample(const Sample& sample_) + { + Sample unregister_sample; + + unregister_sample.cmd_type = GetUnregistrationType(sample_); + unregister_sample.identifier = sample_.identifier; + + if (IsProcessRegistration(unregister_sample)) + { + const auto& sample_process = sample_.process; + auto& unregister_sample_process = unregister_sample.process; + unregister_sample_process.pname = sample_process.pname; + unregister_sample_process.uname = sample_process.uname; + } + + if (IsTopicRegistration(unregister_sample)) + { + const auto& sample_topic = sample_.topic; + auto& unregister_sample_topic = unregister_sample.topic; + unregister_sample_topic.hgname = sample_topic.hgname; + unregister_sample_topic.pname = sample_topic.pname; + unregister_sample_topic.tname = sample_topic.tname; + unregister_sample_topic.uname = sample_topic.uname; + } + + if (unregister_sample.cmd_type == bct_unreg_service) + { + const auto& sample_service = sample_.service; + auto& unregister_sample_service = unregister_sample.service; + unregister_sample_service.pname = sample_service.pname; + unregister_sample_service.sname = sample_service.sname; + unregister_sample_service.uname = sample_service.uname; + unregister_sample_service.version = sample_service.version; + } + + if (unregister_sample.cmd_type == bct_unreg_client) + { + const auto& sample_client = sample_.client; + auto& unregister_sample_client = unregister_sample.client; + + unregister_sample_client.pname = sample_client.pname; + unregister_sample_client.sname = sample_client.sname; + unregister_sample_client.uname = sample_client.uname; + unregister_sample_client.version = sample_client.version; + } + return unregister_sample; + } + + eCmdType GetUnregistrationType(const Registration::Sample& sample_) + { + if (sample_.cmd_type == bct_reg_client) + return bct_unreg_client; + if (sample_.cmd_type == bct_reg_process) + return bct_unreg_process; + if (sample_.cmd_type == bct_reg_publisher) + return bct_unreg_publisher; + if (sample_.cmd_type == bct_reg_service) + return bct_unreg_service; + if (sample_.cmd_type == bct_reg_subscriber) + return bct_unreg_subscriber; + return bct_none; + } + + bool IsProcessRegistration(const Registration::Sample& sample_) + { + return sample_.cmd_type == bct_reg_process || + sample_.cmd_type == bct_unreg_process; + } + + bool IsTopicRegistration(const Registration::Sample& sample_) + { + return sample_.cmd_type == bct_reg_publisher || + sample_.cmd_type == bct_reg_subscriber || + sample_.cmd_type == bct_unreg_publisher || + sample_.cmd_type == bct_unreg_subscriber; + } + } +} diff --git a/ecal/core/src/registration/ecal_registration_timeout_provider.h b/ecal/core/src/registration/ecal_registration_timeout_provider.h new file mode 100644 index 0000000000..8aac623536 --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_timeout_provider.h @@ -0,0 +1,115 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL registration timeout provider + * + * Class that tracks incoming samples. + * It will call an unregistration sample callback, whenenver a sample has "timed out". + * This can be treated the same way if the other process had sent an unregister sample. + * +**/ + +#pragma once + +#include +#include + +#include + +namespace eCAL +{ + namespace Registration + { + bool IsUnregistrationSample(const Registration::Sample& sample_); + + // This function turns a registration sample into an unregistration sample + // This could happen also in another class / namespace + Registration::Sample CreateUnregisterSample(const Registration::Sample& sample_); + + // Returns the corresponding unregistration type + // RegSubscriber -> UnregSubscriber, ... + // Anything else will return bct_none + eCmdType GetUnregistrationType(const Registration::Sample& sample_); + + bool IsProcessRegistration(const Registration::Sample& sample_); + bool IsTopicRegistration(const Registration::Sample& sample_); + + + template < class ClockType = std::chrono::steady_clock> + class CTimeoutProvider + { + public: + CTimeoutProvider(const typename ClockType::duration& timeout_, const RegistrationApplySampleCallbackT& apply_sample_callback_) + : sample_tracker(timeout_) + , apply_sample_callback(apply_sample_callback_) + {} + + bool ApplySample(const Registration::Sample& sample_) { + // Is unregistration sample? + if (IsUnregistrationSample(sample_)) + { + DeleteUnregisterSample(sample_); + } + else + { + UpdateSample(sample_); + } + return true; + } + + // This function checks for timeouts. This means it scans the map for expired samples + // It then applies unregistration samples for all internally expired samples. + void CheckForTimeouts() + { + std::map expired_samples; + + { + std::lock_guard lock(sample_tracker_mutex); + expired_samples = sample_tracker.erase_expired(); + } + + for (const auto& registration_sample : expired_samples) + { + Sample unregistration_sample = CreateUnregisterSample(registration_sample.second); + apply_sample_callback(unregistration_sample); + } + } + + private: + void DeleteUnregisterSample(const Sample& sample_) + { + std::lock_guard lock(sample_tracker_mutex); + sample_tracker.erase(sample_.identifier); + } + + void UpdateSample(const Sample& sample_) + { + std::lock_guard lock(sample_tracker_mutex); + sample_tracker[sample_.identifier] = sample_; + } + + using SampleTrackerMap = Util::CExpirationMap; + SampleTrackerMap sample_tracker; + std::mutex sample_tracker_mutex; + + RegistrationApplySampleCallbackT apply_sample_callback; + }; + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/ecal_registration_types.h b/ecal/core/src/registration/ecal_registration_types.h new file mode 100644 index 0000000000..2fe25547b2 --- /dev/null +++ b/ecal/core/src/registration/ecal_registration_types.h @@ -0,0 +1,33 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include +#include + +namespace eCAL { + /** + * @brief Apply sample callback type. + * + * @param sample_ The sample protocol buffer registration payload buffer. + * @param sample_size_ The payload buffer size. + **/ + using RegistrationApplySampleCallbackT = std::function; +} \ No newline at end of file diff --git a/ecal/core/src/pubsub/ecal_publisher_config.cpp b/ecal/core/src/registration/shm/attributes/registration_shm_attributes.h similarity index 79% rename from ecal/core/src/pubsub/ecal_publisher_config.cpp rename to ecal/core/src/registration/shm/attributes/registration_shm_attributes.h index ede5ddddd9..152772698a 100644 --- a/ecal/core/src/pubsub/ecal_publisher_config.cpp +++ b/ecal/core/src/registration/shm/attributes/registration_shm_attributes.h @@ -17,20 +17,21 @@ * ========================= eCAL LICENSE ================================= */ -/** - * @brief eCAL publisher configuration -**/ +#pragma once -#include -#include +#include namespace eCAL { - namespace Publisher + namespace Registration { - Configuration::Configuration() + namespace SHM { - *this = GetConfiguration().publisher; + struct SAttributes + { + std::string domain; + unsigned int queue_size; + }; } } } diff --git a/ecal/core/src/registration/shm/ecal_memfile_broadcast.cpp b/ecal/core/src/registration/shm/ecal_memfile_broadcast.cpp index 18c29834e5..f23622d299 100644 --- a/ecal/core/src/registration/shm/ecal_memfile_broadcast.cpp +++ b/ecal/core/src/registration/shm/ecal_memfile_broadcast.cpp @@ -59,20 +59,20 @@ namespace eCAL return reinterpret_cast(static_cast(address) + GetMemfileHeader(address)->message_queue_offset); } - CMemoryFileBroadcast::CMemoryFileBroadcast(): m_created(false), m_max_queue_size(0), m_broadcast_memfile(std::make_unique()), m_event_queue(), m_last_timestamp(0) + CMemoryFileBroadcast::CMemoryFileBroadcast(): m_created(false), m_broadcast_memfile(std::make_unique()), m_event_queue(), m_last_timestamp(0) { } - bool CMemoryFileBroadcast::Create(const std::string &name, std::size_t max_queue_size) + bool CMemoryFileBroadcast::Create(const Registration::SHM::SAttributes& attr_) { if (m_created) return false; - m_max_queue_size = max_queue_size; - m_name = name; + m_attributes = attr_; + const auto presumably_memfile_size = - RelocatableCircularQueue::PresumablyOccupiedMemorySize(m_max_queue_size) + + RelocatableCircularQueue::PresumablyOccupiedMemorySize(m_attributes.queue_size) + sizeof(SMemfileBroadcastHeader); - if (!m_broadcast_memfile->Create(name.c_str(), true, presumably_memfile_size, true)) + if (!m_broadcast_memfile->Create(m_attributes.domain.c_str(), true, presumably_memfile_size, true)) { #ifndef NDEBUG std::cerr << "Unable to access broadcast memory file." << std::endl; @@ -136,7 +136,7 @@ namespace eCAL std::string CMemoryFileBroadcast::GetName() const { - return m_name; + return m_attributes.domain; } bool CMemoryFileBroadcast::IsMemfileVersionCompatible(const void *memfile_address) const @@ -150,7 +150,7 @@ namespace eCAL auto *header = GetMemfileHeader(memfile_address); *header = SMemfileBroadcastHeader(); m_event_queue.SetBaseAddress(GetEventQueueAddress(memfile_address)); - m_event_queue.Reset(m_max_queue_size); + m_event_queue.Reset(m_attributes.queue_size); #ifndef NDEBUG std::cout << "Broadcast memory file has been resetted" << std::endl; #endif @@ -246,7 +246,6 @@ namespace eCAL } } - bool CMemoryFileBroadcast::ReceiveEvents(MemfileBroadcastEventListT &event_list, std::int64_t timeout, bool enable_loopback) { if (m_broadcast_memfile->GetReadAccess(EXP_MEMFILE_ACCESS_TIMEOUT)) diff --git a/ecal/core/src/registration/shm/ecal_memfile_broadcast.h b/ecal/core/src/registration/shm/ecal_memfile_broadcast.h index 321a42b704..d8a183d68e 100644 --- a/ecal/core/src/registration/shm/ecal_memfile_broadcast.h +++ b/ecal/core/src/registration/shm/ecal_memfile_broadcast.h @@ -32,6 +32,7 @@ #include "relocatable_circular_queue.h" #include "io/shm/ecal_memfile.h" +#include "attributes/registration_shm_attributes.h" #include @@ -79,7 +80,7 @@ namespace eCAL public: CMemoryFileBroadcast(); - bool Create(const std::string& name, std::size_t max_queue_size); + bool Create(const Registration::SHM::SAttributes& attr_); bool Destroy(); std::string GetName() const; @@ -95,8 +96,7 @@ namespace eCAL void ResetMemfile(void * memfile_address); bool m_created; - std::string m_name; - std::size_t m_max_queue_size; + Registration::SHM::SAttributes m_attributes; std::unique_ptr m_broadcast_memfile; std::vector m_broadcast_memfile_local_buffer; diff --git a/ecal/core/src/registration/ecal_registration_receiver_shm.cpp b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp similarity index 56% rename from ecal/core/src/registration/ecal_registration_receiver_shm.cpp rename to ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp index 7ac78263cf..c8a51a0153 100644 --- a/ecal/core/src/registration/ecal_registration_receiver_shm.cpp +++ b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,11 +27,12 @@ #include "ecal_globals.h" -#include "ecal_registration_receiver_shm.h" +#include "registration/shm/ecal_registration_receiver_shm.h" #include "serialization/ecal_serialize_sample_registration.h" -#include -#include -#include + +#include "registration/shm/ecal_memfile_broadcast.h" +#include "registration/shm/ecal_memfile_broadcast_reader.h" +#include "util/ecal_thread.h" namespace eCAL { @@ -39,35 +40,35 @@ namespace eCAL // CMemfileRegistrationReceiver ////////////////////////////////////////////////////////////////// - CMemfileRegistrationReceiver::~CMemfileRegistrationReceiver() - { - Destroy(); - } - - void CMemfileRegistrationReceiver::Create(eCAL::CMemoryFileBroadcastReader* memfile_broadcast_reader_) + CRegistrationReceiverSHM::CRegistrationReceiverSHM(RegistrationApplySampleCallbackT apply_sample_callback, const Registration::SHM::SAttributes& attr_) + : m_apply_sample_callback(apply_sample_callback) { - if (m_created) return; + m_memfile_broadcast = std::make_unique(); + m_memfile_broadcast->Create(attr_); + m_memfile_broadcast->FlushLocalEventQueue(); - // start memfile broadcast receive thread - m_memfile_broadcast_reader = memfile_broadcast_reader_; - m_memfile_broadcast_reader_thread = std::make_shared(std::bind(&CMemfileRegistrationReceiver::Receive, this)); - m_memfile_broadcast_reader_thread->start(std::chrono::milliseconds(Config::GetRegistrationRefreshMs()/2)); + m_memfile_broadcast_reader = std::make_unique(); + // This is a bit unclean to take the raw adress of the reader here. + m_memfile_broadcast_reader->Bind(m_memfile_broadcast.get()); - m_created = true; + m_memfile_broadcast_reader_thread = std::make_unique(std::bind(&CRegistrationReceiverSHM::Receive, this)); + m_memfile_broadcast_reader_thread->start(std::chrono::milliseconds(Config::GetRegistrationRefreshMs() / 2)); } - void CMemfileRegistrationReceiver::Destroy() + CRegistrationReceiverSHM::~CRegistrationReceiverSHM() { - if (!m_created) return; - - // stop memfile broadcast receive thread m_memfile_broadcast_reader_thread->stop(); + m_memfile_broadcast_reader_thread = nullptr; + + // stop memfile registration receive thread and unbind reader + m_memfile_broadcast_reader->Unbind(); m_memfile_broadcast_reader = nullptr; - m_created = false; + m_memfile_broadcast->Destroy(); + m_memfile_broadcast = nullptr; } - void CMemfileRegistrationReceiver::Receive() + void CRegistrationReceiverSHM::Receive() { MemfileBroadcastMessageListT message_list; if (m_memfile_broadcast_reader->Read(message_list, 0)) @@ -79,7 +80,7 @@ namespace eCAL { for (const auto& sample : sample_list.samples) { - if (g_registration_receiver() != nullptr) g_registration_receiver()->ApplySample(sample); + m_apply_sample_callback(sample); } } } diff --git a/ecal/core/src/registration/ecal_registration_receiver_shm.h b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h similarity index 52% rename from ecal/core/src/registration/ecal_registration_receiver_shm.h rename to ecal/core/src/registration/shm/ecal_registration_receiver_shm.h index b4e0388720..d34f4d53e1 100644 --- a/ecal/core/src/registration/ecal_registration_receiver_shm.h +++ b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,38 +27,38 @@ #pragma once -#include "shm/ecal_memfile_broadcast.h" -#include "shm/ecal_memfile_broadcast_reader.h" - -#include "util/ecal_thread.h" #include +#include +#include "attributes/registration_shm_attributes.h" namespace eCAL { - class CMemfileRegistrationReceiver + class CCallbackThread; + class CMemoryFileBroadcast; + class CMemoryFileBroadcastReader; + + class CRegistrationReceiverSHM { public: - CMemfileRegistrationReceiver() = default; - ~CMemfileRegistrationReceiver(); + CRegistrationReceiverSHM(RegistrationApplySampleCallbackT apply_sample_callback, const Registration::SHM::SAttributes& attr_); + ~CRegistrationReceiverSHM(); // default copy constructor - CMemfileRegistrationReceiver(const CMemfileRegistrationReceiver& other) = delete; + CRegistrationReceiverSHM(const CRegistrationReceiverSHM& other) = delete; // default copy assignment operator - CMemfileRegistrationReceiver& operator=(const CMemfileRegistrationReceiver& other) = delete; + CRegistrationReceiverSHM& operator=(const CRegistrationReceiverSHM& other) = delete; // default move constructor - CMemfileRegistrationReceiver(CMemfileRegistrationReceiver&& other) noexcept = delete; + CRegistrationReceiverSHM(CRegistrationReceiverSHM&& other) noexcept = delete; // default move assignment operator - CMemfileRegistrationReceiver& operator=(CMemfileRegistrationReceiver&& other) noexcept = delete; - - void Create(CMemoryFileBroadcastReader* memfile_broadcast_reader_); - void Destroy(); + CRegistrationReceiverSHM& operator=(CRegistrationReceiverSHM&& other) noexcept = delete; private: void Receive(); - CMemoryFileBroadcastReader* m_memfile_broadcast_reader = nullptr; - std::shared_ptr m_memfile_broadcast_reader_thread; + std::unique_ptr m_memfile_broadcast; + std::unique_ptr m_memfile_broadcast_reader; + std::unique_ptr m_memfile_broadcast_reader_thread; - bool m_created = false; + RegistrationApplySampleCallbackT m_apply_sample_callback; }; } diff --git a/ecal/core/src/registration/shm/ecal_registration_sender_shm.cpp b/ecal/core/src/registration/shm/ecal_registration_sender_shm.cpp new file mode 100644 index 0000000000..a3f6e05f2b --- /dev/null +++ b/ecal/core/src/registration/shm/ecal_registration_sender_shm.cpp @@ -0,0 +1,67 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL registration provider + * + * All process internal publisher/subscriber, server/clients register here with all their attributes. + * + * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. + * +**/ + +#include "registration/shm/ecal_registration_sender_shm.h" +#include "serialization/ecal_serialize_sample_registration.h" + + +eCAL::CRegistrationSenderSHM::CRegistrationSenderSHM(const Registration::SHM::SAttributes& attr_) +{ + std::cout << "Shared memory monitoring is enabled (domain: " << attr_.domain << " - queue size: " << attr_.queue_size << ")" << '\n'; + m_memfile_broadcast.Create(attr_); + m_memfile_broadcast_writer.Bind(&m_memfile_broadcast); +} + +eCAL::CRegistrationSenderSHM::~CRegistrationSenderSHM() +{ + m_memfile_broadcast_writer.Unbind(); + m_memfile_broadcast.Destroy(); +} + +/* +bool eCAL::CRegistrationSenderSHM::SendSample(const Registration::Sample& sample_) +{ + return false; +} +*/ + +bool eCAL::CRegistrationSenderSHM::SendSampleList(const Registration::SampleList& sample_list) +{ + bool return_value{true}; + // serialize whole sample list + std::vector sample_list_buffer; + if (SerializeToBuffer(sample_list, sample_list_buffer)) + { + if (!sample_list_buffer.empty()) + { + // broadcast sample list over shm + return_value &= m_memfile_broadcast_writer.Write(sample_list_buffer.data(), sample_list_buffer.size()); + } + } + return return_value; +} diff --git a/ecal/core/src/registration/shm/ecal_registration_sender_shm.h b/ecal/core/src/registration/shm/ecal_registration_sender_shm.h new file mode 100644 index 0000000000..8cda6a9e67 --- /dev/null +++ b/ecal/core/src/registration/shm/ecal_registration_sender_shm.h @@ -0,0 +1,59 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL registration provider + * + * All process internal publisher/subscriber, server/clients register here with all their attributes. + * + * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. + * +**/ + +#pragma once + +#include "registration/ecal_registration_sender.h" + +#include "registration/shm/ecal_memfile_broadcast.h" +#include "registration/shm/ecal_memfile_broadcast_writer.h" + +#include "attributes/registration_shm_attributes.h" + +namespace eCAL +{ + class CRegistrationSenderSHM : public CRegistrationSender + { + public: + CRegistrationSenderSHM(const Registration::SHM::SAttributes& attr_); + ~CRegistrationSenderSHM() override; + + // Special member functionss + CRegistrationSenderSHM(const CRegistrationSenderSHM& other) = delete; + CRegistrationSenderSHM& operator=(const CRegistrationSenderSHM& other) = delete; + CRegistrationSenderSHM(CRegistrationSenderSHM&& other) noexcept = delete; + CRegistrationSenderSHM& operator=(CRegistrationSenderSHM&& other) noexcept = delete; + + //bool SendSample(const Registration::Sample& sample_) override; + bool SendSampleList(const Registration::SampleList& sample_list) override; + + private: + CMemoryFileBroadcast m_memfile_broadcast; + CMemoryFileBroadcastWriter m_memfile_broadcast_writer; + }; +} \ No newline at end of file diff --git a/ecal/core/src/registration/udp/attributes/registration_receiver_udp_attributes.h b/ecal/core/src/registration/udp/attributes/registration_receiver_udp_attributes.h new file mode 100644 index 0000000000..96695c675e --- /dev/null +++ b/ecal/core/src/registration/udp/attributes/registration_receiver_udp_attributes.h @@ -0,0 +1,40 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace Registration + { + namespace UDP + { + struct SReceiverAttributes + { + std::string address; + int port = 0; + bool broadcast = false; + bool loopback = true; + int rcvbuf = 1024 * 1024; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/udp/attributes/registration_sender_udp_attributes.h b/ecal/core/src/registration/udp/attributes/registration_sender_udp_attributes.h new file mode 100644 index 0000000000..8af5f51acd --- /dev/null +++ b/ecal/core/src/registration/udp/attributes/registration_sender_udp_attributes.h @@ -0,0 +1,41 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace Registration + { + namespace UDP + { + struct SSenderAttributes + { + std::string address; + int port; + int ttl; + bool broadcast; + bool loopback; + int sndbuf; + }; + } + } +} diff --git a/ecal/core/src/registration/udp/builder/udp_attribute_builder.cpp b/ecal/core/src/registration/udp/builder/udp_attribute_builder.cpp new file mode 100644 index 0000000000..345229fd19 --- /dev/null +++ b/ecal/core/src/registration/udp/builder/udp_attribute_builder.cpp @@ -0,0 +1,52 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "udp_attribute_builder.h" + +namespace eCAL +{ + namespace Registration + { + namespace UDP + { + eCAL::UDP::SSenderAttr ConvertToIOUDPSenderAttributes (const Registration::UDP::SSenderAttributes& sender_attr_) + { + eCAL::UDP::SSenderAttr attr; + attr.broadcast = sender_attr_.broadcast; + attr.loopback = sender_attr_.loopback; + attr.sndbuf = sender_attr_.sndbuf; + attr.port = sender_attr_.port; + attr.address = sender_attr_.address; + attr.ttl = sender_attr_.ttl; + return attr; + } + + eCAL::UDP::SReceiverAttr ConvertToIOUDPReceiverAttributes (const Registration::UDP::SReceiverAttributes& receiver_attr_) + { + eCAL::UDP::SReceiverAttr attr; + attr.broadcast = receiver_attr_.broadcast; + attr.loopback = receiver_attr_.loopback; + attr.rcvbuf = receiver_attr_.rcvbuf; + attr.port = receiver_attr_.port; + attr.address = receiver_attr_.address; + return attr; + } + } + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/udp/builder/udp_attribute_builder.h b/ecal/core/src/registration/udp/builder/udp_attribute_builder.h new file mode 100644 index 0000000000..beb1525209 --- /dev/null +++ b/ecal/core/src/registration/udp/builder/udp_attribute_builder.h @@ -0,0 +1,39 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#pragma once + + +#include "io/udp/ecal_udp_receiver_attr.h" +#include "io/udp/ecal_udp_sender_attr.h" + +#include "registration/udp/attributes/registration_sender_udp_attributes.h" +#include "registration/udp/attributes/registration_receiver_udp_attributes.h" + +namespace eCAL +{ + namespace Registration + { + namespace UDP + { + eCAL::UDP::SSenderAttr ConvertToIOUDPSenderAttributes (const Registration::UDP::SSenderAttributes& sender_attr_); + eCAL::UDP::SReceiverAttr ConvertToIOUDPReceiverAttributes (const Registration::UDP::SReceiverAttributes& receiver_attr_); + } + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/udp/ecal_registration_receiver_udp.cpp b/ecal/core/src/registration/udp/ecal_registration_receiver_udp.cpp new file mode 100644 index 0000000000..d1f0acd9ad --- /dev/null +++ b/ecal/core/src/registration/udp/ecal_registration_receiver_udp.cpp @@ -0,0 +1,43 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include "registration/udp/ecal_registration_receiver_udp.h" + +#include "io/udp/ecal_udp_sample_receiver.h" +#include "io/udp/ecal_udp_configurations.h" +#include "serialization/ecal_serialize_sample_registration.h" +#include + +#include "registration/udp/builder/udp_attribute_builder.h" + +using namespace eCAL; + +eCAL::CRegistrationReceiverUDP::CRegistrationReceiverUDP(RegistrationApplySampleCallbackT apply_sample_callback, const Registration::UDP::SReceiverAttributes& attr_) + : m_registration_receiver(std::make_unique( + Registration::UDP::ConvertToIOUDPReceiverAttributes(attr_), + [](const std::string& sample_name_) {return true; }, + [apply_sample_callback](const char* serialized_sample_data_, size_t serialized_sample_size_) { + Registration::Sample sample; + if (!DeserializeFromBuffer(serialized_sample_data_, serialized_sample_size_, sample)) return false; + return apply_sample_callback(sample); + } + )) +{} + +eCAL::CRegistrationReceiverUDP::~CRegistrationReceiverUDP() = default; diff --git a/ecal/core/src/registration/udp/ecal_registration_receiver_udp.h b/ecal/core/src/registration/udp/ecal_registration_receiver_udp.h new file mode 100644 index 0000000000..bcbe66fa4d --- /dev/null +++ b/ecal/core/src/registration/udp/ecal_registration_receiver_udp.h @@ -0,0 +1,53 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL UDP registration receiver + * + * Handles UDP samples coming from other processes + * +**/ + +#include +#include +#include "registration/udp/attributes/registration_receiver_udp_attributes.h" + +namespace eCAL +{ + namespace UDP + { + class CSampleReceiver; + } + + class CRegistrationReceiverUDP + { + public: + CRegistrationReceiverUDP(RegistrationApplySampleCallbackT apply_sample_callback, const Registration::UDP::SReceiverAttributes& attr_); + ~CRegistrationReceiverUDP(); + + // Special member functionss + CRegistrationReceiverUDP(const CRegistrationReceiverUDP& other) = delete; + CRegistrationReceiverUDP& operator=(const CRegistrationReceiverUDP& other) = delete; + CRegistrationReceiverUDP(CRegistrationReceiverUDP&& other) noexcept = delete; + CRegistrationReceiverUDP& operator=(CRegistrationReceiverUDP&& other) noexcept = delete; + + private: + std::unique_ptr m_registration_receiver; + }; +} \ No newline at end of file diff --git a/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp b/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp new file mode 100644 index 0000000000..936efead72 --- /dev/null +++ b/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp @@ -0,0 +1,67 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL registration provider + * + * All process internal publisher/subscriber, server/clients register here with all their attributes. + * + * These information will be send cyclic (registration refresh) via UDP to external eCAL processes. + * +**/ + +#include "registration/udp/ecal_registration_sender_udp.h" + +#include "serialization/ecal_serialize_sample_registration.h" +#include "io/udp/ecal_udp_configurations.h" +#include + +#include "registration/udp/builder/udp_attribute_builder.h" + +namespace eCAL +{ + CRegistrationSenderUDP::CRegistrationSenderUDP(const eCAL::Registration::UDP::SSenderAttributes& attr_) + : m_reg_sample_snd(Registration::UDP::ConvertToIOUDPSenderAttributes(attr_)) + { + } + + CRegistrationSenderUDP::~CRegistrationSenderUDP() = default; + + bool CRegistrationSenderUDP::SendSample(const Registration::Sample& sample_) + { + // serialize single sample + std::vector sample_buffer; + if (SerializeToBuffer(sample_, sample_buffer)) + { + // send single sample over udp + return m_reg_sample_snd.Send("reg_sample", sample_buffer) != 0; + } + return false; + } + + bool CRegistrationSenderUDP::SendSampleList(const Registration::SampleList& sample_list) + { + bool return_value{ true }; + for (const auto& sample : sample_list.samples) + { + return_value &= SendSample(sample); + } + return return_value; + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/udp/ecal_registration_sender_udp.h b/ecal/core/src/registration/udp/ecal_registration_sender_udp.h new file mode 100644 index 0000000000..c0a8e12511 --- /dev/null +++ b/ecal/core/src/registration/udp/ecal_registration_sender_udp.h @@ -0,0 +1,55 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief eCAL registration sender UDP + * + * A device which sends out registration information via UDP + * +**/ + +#pragma once + +#include "registration/ecal_registration_sender.h" + +#include "io/udp/ecal_udp_sample_sender.h" +#include "registration/udp/attributes/registration_sender_udp_attributes.h" + +namespace eCAL +{ + class CRegistrationSenderUDP : public CRegistrationSender + { + public: + CRegistrationSenderUDP(const eCAL::Registration::UDP::SSenderAttributes& attr_); + ~CRegistrationSenderUDP() override; + + // Special member functionss + CRegistrationSenderUDP(const CRegistrationSenderUDP& other) = delete; + CRegistrationSenderUDP& operator=(const CRegistrationSenderUDP& other) = delete; + CRegistrationSenderUDP(CRegistrationSenderUDP&& other) noexcept = delete; + CRegistrationSenderUDP& operator=(CRegistrationSenderUDP&& other) noexcept = delete; + + bool SendSampleList(const Registration::SampleList& sample_list) override; + + private: + bool SendSample(const Registration::Sample& sample_); + + UDP::CSampleSender m_reg_sample_snd; + }; +} \ No newline at end of file diff --git a/ecal/core/src/serialization/ecal_serialize_common.cpp b/ecal/core/src/serialization/ecal_serialize_common.cpp index 7f8c49092d..6bb0ff6214 100644 --- a/ecal/core/src/serialization/ecal_serialize_common.cpp +++ b/ecal/core/src/serialization/ecal_serialize_common.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -261,9 +261,10 @@ namespace eCAL } eCAL_pb_TLayer pb_layer = eCAL_pb_TLayer_init_default; - pb_layer.type = static_cast(layer.type); - pb_layer.version = layer.version; - pb_layer.confirmed = layer.confirmed; + pb_layer.type = static_cast(layer.type); + pb_layer.version = layer.version; + pb_layer.enabled = layer.enabled; + pb_layer.active = layer.active; // layer pb_layer.has_par_layer = true; @@ -313,9 +314,10 @@ namespace eCAL } // apply layer values - layer.type = static_cast(pb_layer.type); - layer.version = pb_layer.version; - layer.confirmed = pb_layer.confirmed; + layer.type = static_cast(pb_layer.type); + layer.version = pb_layer.version; + layer.enabled = pb_layer.enabled; + layer.active = pb_layer.active; // apply tcp layer parameter layer.par_layer.layer_par_tcp.port = pb_layer.par_layer.layer_par_tcp.port; diff --git a/ecal/core/src/serialization/ecal_serialize_logging.cpp b/ecal/core/src/serialization/ecal_serialize_logging.cpp index 8cc69fdfe3..ef67992538 100644 --- a/ecal/core/src/serialization/ecal_serialize_logging.cpp +++ b/ecal/core/src/serialization/ecal_serialize_logging.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -157,6 +157,7 @@ namespace if (!pb_decode(&pb_istream, eCAL_pb_LogMessage_fields, &pb_log_message)) { std::cerr << "NanoPb eCAL::Logging::LogMessage decode failed: " << pb_istream.errmsg << '\n'; + return false; } /////////////////////////////////////////////// @@ -293,6 +294,7 @@ namespace if (!pb_decode(&pb_istream, eCAL_pb_LogMessageList_fields, &pb_log_message_list)) { std::cerr << "NanoPb eCAL::Logging::LogMessageList decode failed: " << pb_istream.errmsg << '\n'; + return false; } return true; diff --git a/ecal/core/src/serialization/ecal_serialize_monitoring.cpp b/ecal/core/src/serialization/ecal_serialize_monitoring.cpp index 9776d4c536..5e0bd04a52 100644 --- a/ecal/core/src/serialization/ecal_serialize_monitoring.cpp +++ b/ecal/core/src/serialization/ecal_serialize_monitoring.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -126,7 +126,7 @@ namespace eCAL_pb_TLayer pb_layer = eCAL_pb_TLayer_init_default; pb_layer.type = static_cast(layer.type); pb_layer.version = layer.version; - pb_layer.confirmed = layer.confirmed; + pb_layer.active = layer.active; if (!pb_encode_submessage(stream, eCAL_pb_TLayer_fields, &pb_layer)) { @@ -572,7 +572,7 @@ namespace // apply layer values layer.type = static_cast(pb_layer.type); layer.version = pb_layer.version; - layer.confirmed = pb_layer.confirmed; + layer.active = pb_layer.active; // add layer auto* tgt_vector = static_cast*>(*arg); @@ -894,6 +894,7 @@ namespace if (!pb_decode(&pb_istream, eCAL_pb_Monitoring_fields, &pb_mon_message)) { std::cerr << "NanoPb eCAL::Monitoring::SMonitoring decode failed: " << pb_istream.errmsg << '\n'; + return false; } return true; diff --git a/ecal/core/src/serialization/ecal_serialize_sample_payload.cpp b/ecal/core/src/serialization/ecal_serialize_sample_payload.cpp index 1f8caf4106..45e2b92006 100644 --- a/ecal/core/src/serialization/ecal_serialize_sample_payload.cpp +++ b/ecal/core/src/serialization/ecal_serialize_sample_payload.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -168,6 +168,7 @@ namespace if (!pb_decode(&pb_istream, eCAL_pb_Sample_fields, &pb_sample)) { std::cerr << "NanoPb eCAL::Payload::Sample decode failed: " << pb_istream.errmsg << '\n'; + return false; } /////////////////////////////////////////////// diff --git a/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp b/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp index d758f79e7e..8390d1e071 100644 --- a/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp +++ b/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,161 +37,214 @@ namespace { - ///////////////////////////////////////////////////////////////////////////////// - // eCAL::Registration::Sample - ///////////////////////////////////////////////////////////////////////////////// - void PrepareEncoding(const eCAL::Registration::Sample& registration_, eCAL_pb_Sample& pb_sample_) + /////////////////////////////////////////////// + // process information + /////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Registration::Sample& registration_, eCAL_pb_Process& pb_process_) { - // command type - pb_sample_.cmd_type = static_cast(registration_.cmd_type); - - /////////////////////////////////////////////// - // host information - /////////////////////////////////////////////// - pb_sample_.has_host = true; - - // hname - eCAL::nanopb::encode_string(pb_sample_.host.hname, registration_.host.hname); - - /////////////////////////////////////////////// - // process information - /////////////////////////////////////////////// - pb_sample_.has_process = true; - + const auto& registration_process_ = registration_.process; + const auto& registration_identifier_ = registration_.identifier; // rclock - pb_sample_.process.rclock = registration_.process.rclock; + pb_process_.rclock = registration_process_.rclock; // hname - eCAL::nanopb::encode_string(pb_sample_.process.hname, registration_.process.hname); + eCAL::nanopb::encode_string(pb_process_.hname, registration_identifier_.host_name); // hgname - eCAL::nanopb::encode_string(pb_sample_.process.hgname, registration_.process.hgname); + eCAL::nanopb::encode_string(pb_process_.hgname, registration_process_.hgname); // pid - pb_sample_.process.pid = registration_.process.pid; + pb_process_.pid = registration_identifier_.process_id; // pname - eCAL::nanopb::encode_string(pb_sample_.process.pname, registration_.process.pname); + eCAL::nanopb::encode_string(pb_process_.pname, registration_process_.pname); // uname - eCAL::nanopb::encode_string(pb_sample_.process.uname, registration_.process.uname); + eCAL::nanopb::encode_string(pb_process_.uname, registration_process_.uname); // pparam - eCAL::nanopb::encode_string(pb_sample_.process.pparam, registration_.process.pparam); + eCAL::nanopb::encode_string(pb_process_.pparam, registration_process_.pparam); + // state - pb_sample_.process.has_state = true; + pb_process_.has_state = true; // state.severity - pb_sample_.process.state.severity = static_cast(registration_.process.state.severity); + pb_process_.state.severity = static_cast(registration_process_.state.severity); // state.severity_level - pb_sample_.process.state.severity_level = static_cast(registration_.process.state.severity_level); + pb_process_.state.severity_level = static_cast(registration_process_.state.severity_level); // state.info - eCAL::nanopb::encode_string(pb_sample_.process.state.info, registration_.process.state.info); + eCAL::nanopb::encode_string(pb_process_.state.info, registration_process_.state.info); // process.tsync_state - pb_sample_.process.tsync_state = static_cast(registration_.process.tsync_state); + pb_process_.tsync_state = static_cast(registration_process_.tsync_state); // tsync_mod_name - eCAL::nanopb::encode_string(pb_sample_.process.tsync_mod_name, registration_.process.tsync_mod_name); + eCAL::nanopb::encode_string(pb_process_.tsync_mod_name, registration_process_.tsync_mod_name); // component_init_state - pb_sample_.process.component_init_state = registration_.process.component_init_state; + pb_process_.component_init_state = registration_process_.component_init_state; // component_init_info - eCAL::nanopb::encode_string(pb_sample_.process.component_init_info, registration_.process.component_init_info); + eCAL::nanopb::encode_string(pb_process_.component_init_info, registration_process_.component_init_info); // ecal_runtime_version - eCAL::nanopb::encode_string(pb_sample_.process.ecal_runtime_version, registration_.process.ecal_runtime_version); + eCAL::nanopb::encode_string(pb_process_.ecal_runtime_version, registration_process_.ecal_runtime_version); + } - /////////////////////////////////////////////// - // service information - /////////////////////////////////////////////// - pb_sample_.has_service = true; + /////////////////////////////////////////////// + // service information + /////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Registration::Sample& registration_, eCAL_pb_Service& pb_service_) + { + const auto& registration_service_ = registration_.service; + const auto& registration_identifier_ = registration_.identifier; // rclock - pb_sample_.service.rclock = registration_.service.rclock; + pb_service_.rclock = registration_service_.rclock; // hname - eCAL::nanopb::encode_string(pb_sample_.service.hname, registration_.service.hname); + eCAL::nanopb::encode_string(pb_service_.hname, registration_identifier_.host_name); // pname - eCAL::nanopb::encode_string(pb_sample_.service.pname, registration_.service.pname); + eCAL::nanopb::encode_string(pb_service_.pname, registration_service_.pname); // uname - eCAL::nanopb::encode_string(pb_sample_.service.uname, registration_.service.uname); + eCAL::nanopb::encode_string(pb_service_.uname, registration_service_.uname); // pid - pb_sample_.service.pid = registration_.service.pid; + pb_service_.pid = registration_identifier_.process_id; // sname - eCAL::nanopb::encode_string(pb_sample_.service.sname, registration_.service.sname); + eCAL::nanopb::encode_string(pb_service_.sname, registration_service_.sname); // sid - eCAL::nanopb::encode_string(pb_sample_.service.sid, registration_.service.sid); + eCAL::nanopb::encode_string(pb_service_.sid, registration_identifier_.entity_id); // methods - eCAL::nanopb::encode_service_methods(pb_sample_.service.methods, registration_.service.methods); + eCAL::nanopb::encode_service_methods(pb_service_.methods, registration_service_.methods); // version - pb_sample_.service.version = registration_.service.version; + pb_service_.version = registration_service_.version; // tcp_port_v0 - pb_sample_.service.tcp_port_v0 = registration_.service.tcp_port_v0; + pb_service_.tcp_port_v0 = registration_service_.tcp_port_v0; // tcp_port_v1 - pb_sample_.service.tcp_port_v1 = registration_.service.tcp_port_v1; + pb_service_.tcp_port_v1 = registration_service_.tcp_port_v1; + } - /////////////////////////////////////////////// - // client information - /////////////////////////////////////////////// - pb_sample_.has_client = true; + /////////////////////////////////////////////// + // client information + /////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Registration::Sample& registration_, eCAL_pb_Client& pb_client_) + { + const auto& registration_client_ = registration_.client; + const auto& registration_producer_ = registration_.identifier; // rclock - pb_sample_.client.rclock = registration_.client.rclock; + pb_client_.rclock = registration_client_.rclock; // hname - eCAL::nanopb::encode_string(pb_sample_.client.hname, registration_.client.hname); + eCAL::nanopb::encode_string(pb_client_.hname, registration_producer_.host_name); // pname - eCAL::nanopb::encode_string(pb_sample_.client.pname, registration_.client.pname); + eCAL::nanopb::encode_string(pb_client_.pname, registration_client_.pname); // uname - eCAL::nanopb::encode_string(pb_sample_.client.uname, registration_.client.uname); + eCAL::nanopb::encode_string(pb_client_.uname, registration_client_.uname); // pid - pb_sample_.client.pid = registration_.client.pid; + pb_client_.pid = registration_producer_.process_id; // sname - eCAL::nanopb::encode_string(pb_sample_.client.sname, registration_.client.sname); + eCAL::nanopb::encode_string(pb_client_.sname, registration_client_.sname); // sid - eCAL::nanopb::encode_string(pb_sample_.client.sid, registration_.client.sid); + eCAL::nanopb::encode_string(pb_client_.sid, registration_producer_.entity_id); // methods - eCAL::nanopb::encode_service_methods(pb_sample_.client.methods, registration_.client.methods); + eCAL::nanopb::encode_service_methods(pb_client_.methods, registration_client_.methods); // version - pb_sample_.client.version = registration_.client.version; + pb_client_.version = registration_client_.version; + } - /////////////////////////////////////////////// - // topic information - /////////////////////////////////////////////// - pb_sample_.has_topic = true; + /////////////////////////////////////////////// + // topic information + /////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Registration::Sample& registration_sample_, eCAL_pb_Topic& pb_topic_) + { + const auto& registration_identifier_ = registration_sample_.identifier; + const auto& registration_topic_ = registration_sample_.topic; // rclock - pb_sample_.topic.rclock = registration_.topic.rclock; + pb_topic_.rclock = registration_topic_.rclock; // hname - eCAL::nanopb::encode_string(pb_sample_.topic.hname, registration_.topic.hname); + eCAL::nanopb::encode_string(pb_topic_.hname, registration_identifier_.host_name); // hgname - eCAL::nanopb::encode_string(pb_sample_.topic.hgname, registration_.topic.hgname); + eCAL::nanopb::encode_string(pb_topic_.hgname, registration_topic_.hgname); // pid - pb_sample_.topic.pid = registration_.topic.pid; + pb_topic_.pid = registration_identifier_.process_id; // pname - eCAL::nanopb::encode_string(pb_sample_.topic.pname, registration_.topic.pname); + eCAL::nanopb::encode_string(pb_topic_.pname, registration_topic_.pname); // uname - eCAL::nanopb::encode_string(pb_sample_.topic.uname, registration_.topic.uname); + eCAL::nanopb::encode_string(pb_topic_.uname, registration_topic_.uname); // tid - eCAL::nanopb::encode_string(pb_sample_.topic.tid, registration_.topic.tid); + eCAL::nanopb::encode_string(pb_topic_.tid, registration_identifier_.entity_id); // tname - eCAL::nanopb::encode_string(pb_sample_.topic.tname, registration_.topic.tname); + eCAL::nanopb::encode_string(pb_topic_.tname, registration_topic_.tname); // direction - eCAL::nanopb::encode_string(pb_sample_.topic.direction, registration_.topic.direction); + eCAL::nanopb::encode_string(pb_topic_.direction, registration_topic_.direction); // tdatatype - pb_sample_.topic.has_tdatatype = true; + pb_topic_.has_tdatatype = true; // tdatatype.name - eCAL::nanopb::encode_string(pb_sample_.topic.tdatatype.name, registration_.topic.tdatatype.name); + eCAL::nanopb::encode_string(pb_topic_.tdatatype.name, registration_topic_.tdatatype.name); // tdatatype.encoding - eCAL::nanopb::encode_string(pb_sample_.topic.tdatatype.encoding, registration_.topic.tdatatype.encoding); + eCAL::nanopb::encode_string(pb_topic_.tdatatype.encoding, registration_topic_.tdatatype.encoding); // tdatatype.desc - eCAL::nanopb::encode_string(pb_sample_.topic.tdatatype.desc, registration_.topic.tdatatype.descriptor); + eCAL::nanopb::encode_string(pb_topic_.tdatatype.desc, registration_topic_.tdatatype.descriptor); // tsize - pb_sample_.topic.tsize = registration_.topic.tsize; + pb_topic_.tsize = registration_topic_.tsize; // connections_loc - pb_sample_.topic.connections_loc = registration_.topic.connections_loc; + pb_topic_.connections_loc = registration_topic_.connections_loc; // connections_ext - pb_sample_.topic.connections_ext = registration_.topic.connections_ext; + pb_topic_.connections_ext = registration_topic_.connections_ext; // message_drops - pb_sample_.topic.message_drops = registration_.topic.message_drops; + pb_topic_.message_drops = registration_topic_.message_drops; // did - pb_sample_.topic.did = registration_.topic.did; + pb_topic_.did = registration_topic_.did; // dclock - pb_sample_.topic.dclock = registration_.topic.dclock; + pb_topic_.dclock = registration_topic_.dclock; // dfreq - pb_sample_.topic.dfreq = registration_.topic.dfreq; + pb_topic_.dfreq = registration_topic_.dfreq; // tlayer - eCAL::nanopb::encode_registration_layer(pb_sample_.topic.tlayer, registration_.topic.tlayer); + eCAL::nanopb::encode_registration_layer(pb_topic_.tlayer, registration_topic_.tlayer); // attr - eCAL::nanopb::encode_map(pb_sample_.topic.attr, registration_.topic.attr); + eCAL::nanopb::encode_map(pb_topic_.attr, registration_topic_.attr); + } + + ///////////////////////////////////////////////////////////////////////////////// + // eCAL::Registration::Sample + ///////////////////////////////////////////////////////////////////////////////// + void PrepareEncoding(const eCAL::Registration::Sample& registration_, eCAL_pb_Sample& pb_sample_) + { + // command type + pb_sample_.cmd_type = static_cast(registration_.cmd_type); + + /////////////////////////////////////////////// + // host information + /////////////////////////////////////////////// + pb_sample_.has_host = true; + + // hname + eCAL::nanopb::encode_string(pb_sample_.host.hname, registration_.host.hname); + + pb_sample_.has_process = false; + pb_sample_.has_service = false; + pb_sample_.has_client = false; + pb_sample_.has_topic = false; + + switch (registration_.cmd_type) + { + case eCAL::bct_reg_process: + case eCAL::bct_unreg_process: + pb_sample_.has_process = true; + PrepareEncoding(registration_, pb_sample_.process); + break; + + case eCAL::bct_reg_service: + case eCAL::bct_unreg_service: + pb_sample_.has_service = true; + PrepareEncoding(registration_, pb_sample_.service); + break; + + case eCAL::bct_reg_client: + case eCAL::bct_unreg_client: + pb_sample_.has_client = true; + PrepareEncoding(registration_, pb_sample_.client); + break; + + case eCAL::bct_reg_publisher: + case eCAL::bct_unreg_publisher: + case eCAL::bct_reg_subscriber: + case eCAL::bct_unreg_subscriber: + pb_sample_.has_topic = true; + PrepareEncoding(registration_, pb_sample_.topic); + break; + + default: + break; + } } size_t RegistrationStruct2PbSample(const eCAL::Registration::Sample& registration_, eCAL_pb_Sample& pb_sample_) @@ -259,7 +312,7 @@ namespace // process information /////////////////////////////////////////////// // hname - eCAL::nanopb::decode_string(pb_sample_.process.hname, registration_.process.hname); + eCAL::nanopb::decode_string(pb_sample_.process.hname, registration_.identifier.host_name); // hgname eCAL::nanopb::decode_string(pb_sample_.process.hgname, registration_.process.hgname); // pname @@ -281,7 +334,7 @@ namespace // service information /////////////////////////////////////////////// // hname - eCAL::nanopb::decode_string(pb_sample_.service.hname, registration_.service.hname); + eCAL::nanopb::decode_string(pb_sample_.service.hname, registration_.identifier.host_name); // pname eCAL::nanopb::decode_string(pb_sample_.service.pname, registration_.service.pname); // uname @@ -289,7 +342,7 @@ namespace // sname eCAL::nanopb::decode_string(pb_sample_.service.sname, registration_.service.sname); // sid - eCAL::nanopb::decode_string(pb_sample_.service.sid, registration_.service.sid); + eCAL::nanopb::decode_string(pb_sample_.service.sid, registration_.identifier.entity_id); // methods eCAL::nanopb::decode_service_methods(pb_sample_.service.methods, registration_.service.methods); @@ -297,7 +350,7 @@ namespace // client information /////////////////////////////////////////////// // hname - eCAL::nanopb::decode_string(pb_sample_.client.hname, registration_.client.hname); + eCAL::nanopb::decode_string(pb_sample_.client.hname, registration_.identifier.host_name); // pname eCAL::nanopb::decode_string(pb_sample_.client.pname, registration_.client.pname); // uname @@ -305,7 +358,7 @@ namespace // sname eCAL::nanopb::decode_string(pb_sample_.client.sname, registration_.client.sname); // sid - eCAL::nanopb::decode_string(pb_sample_.client.sid, registration_.client.sid); + eCAL::nanopb::decode_string(pb_sample_.client.sid, registration_.identifier.entity_id); // methods eCAL::nanopb::decode_service_methods(pb_sample_.client.methods, registration_.client.methods); @@ -313,7 +366,7 @@ namespace // topic information /////////////////////////////////////////////// // hname - eCAL::nanopb::decode_string(pb_sample_.topic.hname, registration_.topic.hname); + eCAL::nanopb::decode_string(pb_sample_.topic.hname, registration_.identifier.host_name); // hgname eCAL::nanopb::decode_string(pb_sample_.topic.hgname, registration_.topic.hgname); // pname @@ -321,7 +374,7 @@ namespace // uname eCAL::nanopb::decode_string(pb_sample_.topic.uname, registration_.topic.uname); // tid - eCAL::nanopb::decode_string(pb_sample_.topic.tid, registration_.topic.tid); + eCAL::nanopb::decode_string(pb_sample_.topic.tid, registration_.identifier.entity_id); // tname eCAL::nanopb::decode_string(pb_sample_.topic.tname, registration_.topic.tname); // direction @@ -345,68 +398,71 @@ namespace /////////////////////////////////////////////// // command type registration_.cmd_type = static_cast(pb_sample_.cmd_type); - - /////////////////////////////////////////////// - // process information - /////////////////////////////////////////////// - // rclock - registration_.process.rclock = pb_sample_.process.rclock; - // pid - registration_.process.pid = pb_sample_.process.pid; - // state.severity - registration_.process.state.severity = static_cast(pb_sample_.process.state.severity); - // state.severity_level - registration_.process.state.severity_level = static_cast(pb_sample_.process.state.severity_level); - // tsync_state - registration_.process.tsync_state = static_cast(pb_sample_.process.tsync_state); - // component_init_state - registration_.process.component_init_state = pb_sample_.process.component_init_state; - - /////////////////////////////////////////////// - // service information - /////////////////////////////////////////////// - // rclock - registration_.service.rclock = pb_sample_.service.rclock; - // pid - registration_.service.pid = pb_sample_.service.pid; - // version - registration_.service.version = pb_sample_.service.version; - // tcp_port_v0 - registration_.service.tcp_port_v0 = pb_sample_.service.tcp_port_v0; - // tcp_port_v1 - registration_.service.tcp_port_v1 = pb_sample_.service.tcp_port_v1; - - /////////////////////////////////////////////// - // client information - /////////////////////////////////////////////// - // rclock - registration_.client.rclock = pb_sample_.client.rclock; - // pid - registration_.client.pid = pb_sample_.client.pid; - // version - registration_.client.version = pb_sample_.client.version; - - /////////////////////////////////////////////// - // topic information - /////////////////////////////////////////////// - // rclock - registration_.topic.rclock = pb_sample_.topic.rclock; - // pid - registration_.topic.pid = pb_sample_.topic.pid; - // tsize - registration_.topic.tsize = pb_sample_.topic.tsize; - // connections_loc - registration_.topic.connections_loc = pb_sample_.topic.connections_loc; - // connections_ext - registration_.topic.connections_ext = pb_sample_.topic.connections_ext; - // message_drops - registration_.topic.message_drops = pb_sample_.topic.message_drops; - // did - registration_.topic.did = pb_sample_.topic.did; - // dclock - registration_.topic.dclock = pb_sample_.topic.dclock; - // dfreq - registration_.topic.dfreq = pb_sample_.topic.dfreq; + switch (registration_.cmd_type) + { + case eCAL::bct_reg_process: + case eCAL::bct_unreg_process: + // rclock + registration_.process.rclock = pb_sample_.process.rclock; + // pid + registration_.identifier.process_id = pb_sample_.process.pid; + // state.severity + registration_.process.state.severity = static_cast(pb_sample_.process.state.severity); + // state.severity_level + registration_.process.state.severity_level = static_cast(pb_sample_.process.state.severity_level); + // tsync_state + registration_.process.tsync_state = static_cast(pb_sample_.process.tsync_state); + // component_init_state + registration_.process.component_init_state = pb_sample_.process.component_init_state; + break; + case eCAL::bct_reg_service: + case eCAL::bct_unreg_service: + // rclock + registration_.service.rclock = pb_sample_.service.rclock; + // pid + registration_.identifier.process_id = pb_sample_.service.pid; + // version + registration_.service.version = pb_sample_.service.version; + // tcp_port_v0 + registration_.service.tcp_port_v0 = pb_sample_.service.tcp_port_v0; + // tcp_port_v1 + registration_.service.tcp_port_v1 = pb_sample_.service.tcp_port_v1; + break; + case eCAL::bct_reg_client: + case eCAL::bct_unreg_client: + // rclock + registration_.client.rclock = pb_sample_.client.rclock; + // pid + registration_.identifier.process_id = pb_sample_.client.pid; + // version + registration_.client.version = pb_sample_.client.version; + break; + case eCAL::bct_reg_publisher: + case eCAL::bct_unreg_publisher: + case eCAL::bct_reg_subscriber: + case eCAL::bct_unreg_subscriber: + // rclock + registration_.topic.rclock = pb_sample_.topic.rclock; + // pid + registration_.identifier.process_id = pb_sample_.topic.pid; + // tsize + registration_.topic.tsize = pb_sample_.topic.tsize; + // connections_loc + registration_.topic.connections_loc = pb_sample_.topic.connections_loc; + // connections_ext + registration_.topic.connections_ext = pb_sample_.topic.connections_ext; + // message_drops + registration_.topic.message_drops = pb_sample_.topic.message_drops; + // did + registration_.topic.did = pb_sample_.topic.did; + // dclock + registration_.topic.dclock = pb_sample_.topic.dclock; + // dfreq + registration_.topic.dfreq = pb_sample_.topic.dfreq; + break; + default: + break; + } } bool Buffer2RegistrationStruct(const char* data_, size_t size_, eCAL::Registration::Sample& registration_) @@ -430,6 +486,7 @@ namespace if (!pb_decode(&pb_istream, eCAL_pb_Sample_fields, &pb_sample)) { std::cerr << "NanoPb eCAL::Registration::Sample decode failed: " << pb_istream.errmsg << '\n'; + return false; } /////////////////////////////////////////////// @@ -568,6 +625,7 @@ namespace if (!pb_decode(&pb_istream, eCAL_pb_SampleList_fields, &pb_sample_list)) { std::cerr << "NanoPb eCAL::Registration::Sample decode failed: " << pb_istream.errmsg << '\n'; + return false; } return true; diff --git a/ecal/core/src/serialization/ecal_serialize_service.cpp b/ecal/core/src/serialization/ecal_serialize_service.cpp index 0a9bb72337..9bf6260d51 100644 --- a/ecal/core/src/serialization/ecal_serialize_service.cpp +++ b/ecal/core/src/serialization/ecal_serialize_service.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -144,6 +144,7 @@ namespace if (!pb_decode(&pb_istream, eCAL_pb_Request_fields, &pb_request)) { std::cerr << "NanoPb eCAL::Service::Request decode failed: " << pb_istream.errmsg << '\n'; + return false; } /////////////////////////////////////////////// @@ -270,6 +271,7 @@ namespace if (!pb_decode(&pb_istream, eCAL_pb_Response_fields, &pb_response)) { std::cerr << "NanoPb eCAL::Service::Response decode failed: " << pb_istream.errmsg << '\n'; + return false; } /////////////////////////////////////////////// diff --git a/ecal/core/src/serialization/ecal_struct_sample_registration.h b/ecal/core/src/serialization/ecal_struct_sample_registration.h index 383b8712c1..a6835b434e 100644 --- a/ecal/core/src/serialization/ecal_struct_sample_registration.h +++ b/ecal/core/src/serialization/ecal_struct_sample_registration.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ #include #include #include +#include namespace eCAL { @@ -69,6 +70,10 @@ namespace eCAL struct OSInfo { std::string osname; // name + + bool operator==(const OSInfo& other) const { + return osname == other.osname; + } }; // eCAL host @@ -76,6 +81,10 @@ namespace eCAL { std::string hname; // host name OSInfo os; // operating system details + + bool operator==(const Host& other) const { + return hname == other.hname && os == other.os; + } }; // Process severity information @@ -84,23 +93,40 @@ namespace eCAL eProcessSeverity severity = proc_sev_unknown; // severity eProcessSeverityLevel severity_level = proc_sev_level_unknown; // severity level std::string info; // info string + + bool operator==(const ProcessState& other) const { + return severity == other.severity && severity_level == other.severity_level && info == other.info; + } }; // Transport layer parameters for ecal udp multicast struct LayerParUdpMC { + bool operator==(const LayerParUdpMC& /*other*/) const { + // Assuming there are no member variables to compare + return true; + } }; // Transport layer parameters for ecal tcp struct LayerParTcp { int32_t port = 0; // tcp writers port number + + bool operator==(const LayerParTcp& other) const { + return port == other.port; + } + }; // Transport layer parameters for ecal shm struct LayerParShm { std::list memory_file_list; // list of memory file names + + bool operator==(const LayerParShm& other) const { + return memory_file_list == other.memory_file_list; + } }; // Connection parameter for reader/writer @@ -109,6 +135,12 @@ namespace eCAL LayerParUdpMC layer_par_udpmc; // parameter for ecal udp multicast LayerParTcp layer_par_tcp; // parameter for ecal tcp LayerParShm layer_par_shm; // parameter for ecal shm + + bool operator==(const ConnectionPar& other) const { + return layer_par_udpmc == other.layer_par_udpmc && + layer_par_tcp == other.layer_par_tcp && + layer_par_shm == other.layer_par_shm; + } }; // Transport layer information @@ -116,17 +148,24 @@ namespace eCAL { eTLayerType type = tl_none; // transport layer type int32_t version = 0; // transport layer version - bool confirmed = false; // transport layer used? + bool enabled = false; // transport layer enabled ? + bool active = false; // transport layer in use ? ConnectionPar par_layer; // transport layer parameter + + bool operator==(const TLayer& other) const { + return type == other.type && + version == other.version && + enabled == other.enabled && + active == other.active && + par_layer == other.par_layer; + } }; // Process information struct Process { int32_t rclock = 0; // registration clock - std::string hname; // host name std::string hgname; // host group name - int32_t pid = 0; // process id std::string pname; // process name std::string uname; // unit name std::string pparam; // process parameter @@ -136,18 +175,29 @@ namespace eCAL int32_t component_init_state = 0; // eCAL component initialization state (eCAL::Initialize(..)) std::string component_init_info; // like comp_init_state as a human-readable string (pub|sub|srv|mon|log|time|proc) std::string ecal_runtime_version; // loaded/runtime eCAL version of a component + + bool operator==(const Process& other) const { + return rclock == other.rclock && + hgname == other.hgname && + pname == other.pname && + uname == other.uname && + pparam == other.pparam && + state == other.state && + tsync_state == other.tsync_state && + tsync_mod_name == other.tsync_mod_name && + component_init_state == other.component_init_state && + component_init_info == other.component_init_info && + ecal_runtime_version == other.ecal_runtime_version; + } }; // eCAL topic information struct Topic { int32_t rclock = 0; // registration clock (heart beat) - std::string hname; // host name std::string hgname; // host group name - int32_t pid = 0; // process id std::string pname; // process name std::string uname; // unit name - std::string tid; // topic id std::string tname; // topic name std::string direction; // direction (publisher, subscriber) SDataTypeInformation tdatatype; // topic datatype information (encoding & type & description) @@ -164,17 +214,66 @@ namespace eCAL int32_t dfreq = 0; // data frequency (send / receive registrations per second) [mHz] std::map attr; // generic topic description + + bool operator==(const Topic& other) const { + return rclock == other.rclock && + hgname == other.hgname && + pname == other.pname && + uname == other.uname && + tname == other.tname && + direction == other.direction && + tdatatype == other.tdatatype && + tlayer == other.tlayer && + tsize == other.tsize && + connections_loc == other.connections_loc && + connections_ext == other.connections_ext && + message_drops == other.message_drops && + did == other.did && + dclock == other.dclock && + dfreq == other.dfreq && + attr == other.attr; + } + }; + + struct SampleIdentifier + { + std::string entity_id; // unique id within that process + int32_t process_id = 0; // process id which produced the sample + std::string host_name; // host which produced the sample + + bool operator==(const SampleIdentifier& other) const { + return entity_id == other.entity_id && + process_id == other.process_id && + host_name == other.host_name; + } + + bool operator<(const SampleIdentifier& other) const + { + return std::tie(process_id, entity_id, host_name) + < std::tie(other.process_id, other.entity_id, other.host_name); + } }; // Registration sample struct Sample { + SampleIdentifier identifier; // Unique identifier to see who produced the sample (publisher / subscriber / ...) eCmdType cmd_type = bct_none; // registration command type Host host; // host information Process process; // process information Service::Service service; // service information Service::Client client ; // client information Topic topic; // topic information + + bool operator==(const Sample& other) const { + return identifier == other.identifier && + cmd_type == other.cmd_type && + host == other.host && + process == other.process && + service == other.service && + client == other.client && + topic == other.topic; + } }; // Registration sample list diff --git a/ecal/core/src/serialization/ecal_struct_service.h b/ecal/core/src/serialization/ecal_struct_service.h index c3384a3785..470f029845 100644 --- a/ecal/core/src/serialization/ecal_struct_service.h +++ b/ecal/core/src/serialization/ecal_struct_service.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ #include #include #include +#include namespace eCAL { @@ -50,6 +51,16 @@ namespace eCAL std::string error; // Error message int32_t id = 0; // Session id eMethodCallState state = none; // Method call state + + bool operator==(const ServiceHeader& other) const { + return hname == other.hname && + sname == other.sname && + sid == other.sid && + mname == other.mname && + error == other.error && + id == other.id && + state == other.state; + } }; // Service Request @@ -57,6 +68,11 @@ namespace eCAL { ServiceHeader header; // Common service header std::string request; // Request payload + + bool operator==(const Request& other) const { + return header == other.header && + request == other.request; + } }; // Service Response @@ -65,6 +81,12 @@ namespace eCAL ServiceHeader header; // Common service header std::string response; // Response payload int64_t ret_state = 0; // Callback return state + + bool operator==(const Response& other) const { + return header == other.header && + response == other.response && + ret_state == other.ret_state; + } }; // Service Method @@ -76,36 +98,59 @@ namespace eCAL std::string resp_type; // Response type std::string resp_desc; // Response descriptor int64_t call_count = 0; // Call counter + + bool operator==(const Method& other) const { + return mname == other.mname && + req_type == other.req_type && + req_desc == other.req_desc && + resp_type == other.resp_type && + resp_desc == other.resp_desc && + call_count == other.call_count; + } }; // Service struct Service { int32_t rclock = 0; // Registration clock - std::string hname; // Host name std::string pname; // Process name std::string uname; // Unit name - int32_t pid = 0; // Process id std::string sname; // Service name - std::string sid; // Service id std::vector methods; // List of methods uint32_t version = 0; // Service protocol version uint32_t tcp_port_v0 = 0; // The TCP port used for that service (v0) uint32_t tcp_port_v1 = 0; // The TCP port used for that service (v1) + + bool operator==(const Service& other) const { + return rclock == other.rclock && + pname == other.pname && + uname == other.uname && + sname == other.sname && + methods == other.methods && + version == other.version && + tcp_port_v0 == other.tcp_port_v0 && + tcp_port_v1 == other.tcp_port_v1; + } }; // Client struct Client { int32_t rclock = 0; // Registration clock - std::string hname; // Host name std::string pname; // Process name std::string uname; // Unit name - int32_t pid = 0; // Process id std::string sname; // Service name - std::string sid; // Service id std::vector methods; // List of methods uint32_t version = 0; // Client protocol version + + bool operator==(const Client& other) const { + return rclock == other.rclock && + pname == other.pname && + uname == other.uname && + sname == other.sname && + methods == other.methods && + version == other.version; + } }; } } diff --git a/ecal/core/src/serialization/nanopb/ecal.pb.h b/ecal/core/src/serialization/nanopb/ecal.pb.h index 20c84373f2..8711ae4c83 100644 --- a/ecal/core/src/serialization/nanopb/ecal.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal.pb.h @@ -14,7 +14,8 @@ #endif /* Enum definitions */ -typedef enum _eCAL_pb_eCmdType { +typedef enum _eCAL_pb_eCmdType { /* Reserved fields in enums are not supported in protobuf 3.0 + reserved 7 to 11; */ eCAL_pb_eCmdType_bct_none = 0, /* undefined command */ eCAL_pb_eCmdType_bct_set_sample = 1, /* set sample content */ eCAL_pb_eCmdType_bct_reg_publisher = 2, /* register publisher */ @@ -30,7 +31,8 @@ typedef enum _eCAL_pb_eCmdType { } eCAL_pb_eCmdType; /* Struct definitions */ -typedef struct _eCAL_pb_Content { +typedef struct _eCAL_pb_Content { /* Reserved fields in enums are not supported in protobuf 3.0 + reserved 5; */ int64_t id; /* sample id */ int64_t clock; /* internal used clock */ int64_t time; /* time the content was updated */ diff --git a/ecal/core/src/serialization/nanopb/layer.pb.h b/ecal/core/src/serialization/nanopb/layer.pb.h index 9abe83c506..64f39f35ff 100644 --- a/ecal/core/src/serialization/nanopb/layer.pb.h +++ b/ecal/core/src/serialization/nanopb/layer.pb.h @@ -10,7 +10,8 @@ #endif /* Enum definitions */ -typedef enum _eCAL_pb_eTLayerType { +typedef enum _eCAL_pb_eTLayerType { /* Reserved fields in enums are not supported in protobuf 3.0 + reserved 2, 3, 42; */ eCAL_pb_eTLayerType_tl_none = 0, /* undefined */ eCAL_pb_eTLayerType_tl_ecal_udp_mc = 1, /* ecal udp multicast */ /* 2 = ecal udp unicast (not supported anymore) @@ -34,7 +35,8 @@ typedef struct _eCAL_pb_LayerParTcp { int32_t port; /* tcp writers port number */ } eCAL_pb_LayerParTcp; -typedef struct _eCAL_pb_ConnnectionPar { +typedef struct _eCAL_pb_ConnnectionPar { /* Reserved fields in enums are not supported in protobuf 3.0 + reserved 3; */ bool has_layer_par_udpmc; eCAL_pb_LayerParUdpMC layer_par_udpmc; /* parameter for ecal udp multicast */ bool has_layer_par_shm; @@ -44,12 +46,14 @@ typedef struct _eCAL_pb_ConnnectionPar { eCAL_pb_LayerParTcp layer_par_tcp; /* parameter for ecal tcp */ } eCAL_pb_ConnnectionPar; -typedef struct _eCAL_pb_TLayer { +typedef struct _eCAL_pb_TLayer { /* Reserved fields in enums are not supported in protobuf 3.0 + reserved 4; */ eCAL_pb_eTLayerType type; /* transport layer type */ int32_t version; /* transport layer version */ - bool confirmed; /* transport layer used ? */ + bool active; /* transport layer in use ? */ bool has_par_layer; eCAL_pb_ConnnectionPar par_layer; /* transport layer parameter */ + bool enabled; /* transport layer enabled ? */ } eCAL_pb_TLayer; @@ -74,12 +78,12 @@ extern "C" { #define eCAL_pb_LayerParShm_init_default {{{NULL}, NULL}} #define eCAL_pb_LayerParTcp_init_default {0} #define eCAL_pb_ConnnectionPar_init_default {false, eCAL_pb_LayerParUdpMC_init_default, false, eCAL_pb_LayerParShm_init_default, false, eCAL_pb_LayerParTcp_init_default} -#define eCAL_pb_TLayer_init_default {_eCAL_pb_eTLayerType_MIN, 0, 0, false, eCAL_pb_ConnnectionPar_init_default} +#define eCAL_pb_TLayer_init_default {_eCAL_pb_eTLayerType_MIN, 0, 0, false, eCAL_pb_ConnnectionPar_init_default, 0} #define eCAL_pb_LayerParUdpMC_init_zero {0} #define eCAL_pb_LayerParShm_init_zero {{{NULL}, NULL}} #define eCAL_pb_LayerParTcp_init_zero {0} #define eCAL_pb_ConnnectionPar_init_zero {false, eCAL_pb_LayerParUdpMC_init_zero, false, eCAL_pb_LayerParShm_init_zero, false, eCAL_pb_LayerParTcp_init_zero} -#define eCAL_pb_TLayer_init_zero {_eCAL_pb_eTLayerType_MIN, 0, 0, false, eCAL_pb_ConnnectionPar_init_zero} +#define eCAL_pb_TLayer_init_zero {_eCAL_pb_eTLayerType_MIN, 0, 0, false, eCAL_pb_ConnnectionPar_init_zero, 0} /* Field tags (for use in manual encoding/decoding) */ #define eCAL_pb_LayerParShm_memory_file_list_tag 1 @@ -89,8 +93,9 @@ extern "C" { #define eCAL_pb_ConnnectionPar_layer_par_tcp_tag 4 #define eCAL_pb_TLayer_type_tag 1 #define eCAL_pb_TLayer_version_tag 2 -#define eCAL_pb_TLayer_confirmed_tag 3 +#define eCAL_pb_TLayer_active_tag 3 #define eCAL_pb_TLayer_par_layer_tag 5 +#define eCAL_pb_TLayer_enabled_tag 6 /* Struct field encoding specification for nanopb */ #define eCAL_pb_LayerParUdpMC_FIELDLIST(X, a) \ @@ -121,8 +126,9 @@ X(a, STATIC, OPTIONAL, MESSAGE, layer_par_tcp, 4) #define eCAL_pb_TLayer_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, type, 1) \ X(a, STATIC, SINGULAR, INT32, version, 2) \ -X(a, STATIC, SINGULAR, BOOL, confirmed, 3) \ -X(a, STATIC, OPTIONAL, MESSAGE, par_layer, 5) +X(a, STATIC, SINGULAR, BOOL, active, 3) \ +X(a, STATIC, OPTIONAL, MESSAGE, par_layer, 5) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 6) #define eCAL_pb_TLayer_CALLBACK NULL #define eCAL_pb_TLayer_DEFAULT NULL #define eCAL_pb_TLayer_par_layer_MSGTYPE eCAL_pb_ConnnectionPar diff --git a/ecal/core/src/serialization/nanopb/process.pb.h b/ecal/core/src/serialization/nanopb/process.pb.h index e3d3d4ae64..592f0b5f17 100644 --- a/ecal/core/src/serialization/nanopb/process.pb.h +++ b/ecal/core/src/serialization/nanopb/process.pb.h @@ -40,7 +40,8 @@ typedef struct _eCAL_pb_ProcessState { eCAL_pb_eProcessSeverityLevel severity_level; /* severity level */ } eCAL_pb_ProcessState; -typedef struct _eCAL_pb_Process { +typedef struct _eCAL_pb_Process { /* Reserved fields in enums are not supported in protobuf 3.0 + reserved 7 to 11; */ int32_t rclock; /* registration clock */ pb_callback_t hname; /* host name */ int32_t pid; /* process id */ diff --git a/ecal/core/src/serialization/nanopb/service.pb.h b/ecal/core/src/serialization/nanopb/service.pb.h index 753b7292f4..611f224e07 100644 --- a/ecal/core/src/serialization/nanopb/service.pb.h +++ b/ecal/core/src/serialization/nanopb/service.pb.h @@ -101,7 +101,7 @@ extern "C" { #define eCAL_pb_Response_init_default {false, eCAL_pb_ServiceHeader_init_default, {{NULL}, NULL}, 0} #define eCAL_pb_Method_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}} #define eCAL_pb_Service_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0} -#define eCAL_pb_Client_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define eCAL_pb_Client_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0, {{NULL}, NULL}} #define eCAL_pb_ServiceHeader_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, _eCAL_pb_ServiceHeader_eCallState_MIN, {{NULL}, NULL}} #define eCAL_pb_Request_init_zero {false, eCAL_pb_ServiceHeader_init_zero, {{NULL}, NULL}} #define eCAL_pb_Response_init_zero {false, eCAL_pb_ServiceHeader_init_zero, {{NULL}, NULL}, 0} diff --git a/ecal/core/src/serialization/nanopb/topic.pb.h b/ecal/core/src/serialization/nanopb/topic.pb.h index 9bcc66ed79..c7eb1ffc22 100644 --- a/ecal/core/src/serialization/nanopb/topic.pb.h +++ b/ecal/core/src/serialization/nanopb/topic.pb.h @@ -17,7 +17,8 @@ typedef struct _eCAL_pb_DataTypeInformation { pb_callback_t desc; /* descriptor information of the datatype (necessary for reflection) */ } eCAL_pb_DataTypeInformation; -typedef struct _eCAL_pb_Topic { +typedef struct _eCAL_pb_Topic { /* Reserved fields in enums are not supported in protobuf 3.0 + reserved 9, 10, 11, 14, 15, 22 to 26, 29; */ int32_t rclock; /* registration clock (heart beat) */ pb_callback_t hname; /* host name */ int32_t pid; /* process id */ diff --git a/ecal/core/src/service/ecal_clientgate.cpp b/ecal/core/src/service/ecal_clientgate.cpp index f3e49e33d3..f72ca78c44 100644 --- a/ecal/core/src/service/ecal_clientgate.cpp +++ b/ecal/core/src/service/ecal_clientgate.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -101,12 +101,13 @@ namespace eCAL { SServiceAttr service; const auto& ecal_sample_service = ecal_sample_.service; - service.hname = ecal_sample_service.hname; + const auto& ecal_sample_identifier = ecal_sample_.identifier; + service.hname = ecal_sample_identifier.host_name; service.pname = ecal_sample_service.pname; service.uname = ecal_sample_service.uname; service.sname = ecal_sample_service.sname; - service.sid = ecal_sample_service.sid; - service.pid = static_cast(ecal_sample_service.pid); + service.sid = ecal_sample_identifier.entity_id; + service.pid = static_cast(ecal_sample_identifier.process_id); // internal protocol specifics service.version = static_cast(ecal_sample_service.version); @@ -124,7 +125,7 @@ namespace eCAL m_service_register_map[service.key] = service; // remove timeouted services - m_service_register_map.remove_deprecated(); + m_service_register_map.erase_expired(); } // inform matching clients @@ -156,16 +157,15 @@ namespace eCAL return(ret_vec); } - void CClientGate::RefreshRegistrations() + void CClientGate::GetRegistrations(Registration::SampleList& reg_sample_list_) { if (!m_created) return; - // refresh client registrations - const std::shared_lock lock(m_client_set_sync); - for (auto *iter : m_client_set) + // read service registrations + std::shared_lock const lock(m_client_set_sync); + for (const auto& service_client_impl : m_client_set) { - // force client to (re)register itself on registration provider - iter->RefreshRegistration(); + reg_sample_list_.samples.emplace_back(service_client_impl->GetRegistration()); } } } diff --git a/ecal/core/src/service/ecal_clientgate.h b/ecal/core/src/service/ecal_clientgate.h index d3a539d9cb..910c0c0b77 100644 --- a/ecal/core/src/service/ecal_clientgate.h +++ b/ecal/core/src/service/ecal_clientgate.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ namespace eCAL std::vector GetServiceAttr(const std::string& service_name_); - void RefreshRegistrations(); + void GetRegistrations(Registration::SampleList& reg_sample_list_); protected: static std::atomic m_created; @@ -64,7 +64,7 @@ namespace eCAL std::shared_timed_mutex m_client_set_sync; ServiceNameServiceImplSetT m_client_set; - using ConnectedMapT = Util::CExpMap; + using ConnectedMapT = Util::CExpirationMap; std::shared_timed_mutex m_service_register_map_sync; ConnectedMapT m_service_register_map; }; diff --git a/ecal/core/src/service/ecal_service_client_impl.cpp b/ecal/core/src/service/ecal_service_client_impl.cpp index 90e2c37579..7f6ca3f75d 100644 --- a/ecal/core/src/service/ecal_service_client_impl.cpp +++ b/ecal/core/src/service/ecal_service_client_impl.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,7 +89,7 @@ namespace eCAL m_created = true; // register this client - Register(true); + Register(); return(true); } @@ -427,10 +427,15 @@ namespace eCAL } // called by eCAL:CClientGate every second to update registration layer - void CServiceClientImpl::RefreshRegistration() + Registration::Sample CServiceClientImpl::GetRegistration() { - if (!m_created) return; - Register(false); + // refresh connected services map + CheckForNewServices(); + + // check for disconnected services + CheckForDisconnectedServices(); + + return GetRegistrationSample(); } std::shared_ptr>> @@ -629,22 +634,21 @@ namespace eCAL response_.response = std::string(response_struct_.response.data(), response_struct_.response.size()); } - void CServiceClientImpl::Register(const bool force_) + Registration::Sample CServiceClientImpl::GetRegistrationSample() { - if (!m_created) return; - if (m_service_name.empty()) return; + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_reg_client; - Registration::Sample sample; - sample.cmd_type = bct_reg_client; - auto& service_client = sample.client; + auto& service_identifier = ecal_reg_sample.identifier; + service_identifier.entity_id = m_service_id; + service_identifier.process_id = Process::GetProcessID(); + service_identifier.host_name = Process::GetHostName(); + + auto& service_client = ecal_reg_sample.client; service_client.version = m_client_version; - service_client.hname = Process::GetHostName(); service_client.pname = Process::GetProcessName(); service_client.uname = Process::GetUnitName(); - service_client.pid = Process::GetProcessID(); service_client.sname = m_service_name; - service_client.sid = m_service_id; - { const std::lock_guard lock(m_method_sync); @@ -655,72 +659,53 @@ namespace eCAL const auto& method_information = method_information_pair.second; Service::Method method; - method.mname = method_name; - method.req_type = method_information.request_type.name; - method.req_desc = method_information.request_type.descriptor; - method.resp_type = method_information.response_type.name; - method.resp_desc = method_information.response_type.descriptor; + method.mname = method_name; + method.req_type = method_information.request_type.name; + method.req_desc = method_information.request_type.descriptor; + method.resp_type = method_information.response_type.name; + method.resp_desc = method_information.response_type.descriptor; method.call_count = m_method_call_count_map.at(method_name); service_client.methods.push_back(method); } } - // register entity - if (g_registration_provider() != nullptr) g_registration_provider()->ApplySample(sample, force_); - - // refresh connected services map - CheckForNewServices(); - - // check for disconnected services - { - std::lock_guard const lock(m_client_map_sync); - for (auto& client : m_client_map) - { - if (client.second->get_state() == eCAL::service::State::FAILED) - { - std::string const service_key = client.first; - - // is the service still in the connecting map ? - auto iter = m_connected_services_map.find(service_key); - if (iter != m_connected_services_map.end()) - { - // call disconnect event - std::lock_guard const lock_cb(m_event_callback_map_sync); - auto e_iter = m_event_callback_map.find(client_event_disconnected); - if (e_iter != m_event_callback_map.end()) - { - SClientEventCallbackData sdata; - sdata.type = client_event_disconnected; - sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - sdata.attr = iter->second; - (e_iter->second)(m_service_name.c_str(), &sdata); - } - // remove service - m_connected_services_map.erase(iter); - } - } - } - } + return ecal_reg_sample; } - void CServiceClientImpl::Unregister() + Registration::Sample CServiceClientImpl::GetUnregistrationSample() { - if (m_service_name.empty()) return; + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_unreg_client; + + auto& service_identifier = ecal_reg_sample.identifier; + service_identifier.entity_id = m_service_id; + service_identifier.process_id = Process::GetProcessID(); + service_identifier.host_name = Process::GetHostName(); - Registration::Sample sample; - sample.cmd_type = bct_unreg_client; - auto& service_client = sample.client; - service_client.hname = Process::GetHostName(); + auto& service_client = ecal_reg_sample.client; service_client.pname = Process::GetProcessName(); service_client.uname = Process::GetUnitName(); - service_client.pid = Process::GetProcessID(); service_client.sname = m_service_name; - service_client.sid = m_service_id; service_client.version = m_client_version; + return ecal_reg_sample; + } + + void CServiceClientImpl::Register() + { + if (!m_created) return; + if (m_service_name.empty()) return; + + // register entity + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); + } + + void CServiceClientImpl::Unregister() + { + if (m_service_name.empty()) return; // unregister entity - if (g_registration_provider() != nullptr) g_registration_provider()->ApplySample(sample, false); + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); } void CServiceClientImpl::CheckForNewServices() @@ -754,7 +739,7 @@ namespace eCAL const std::vector> endpoint_list { {iter.hname, port_to_use}, - {iter.hname + ".local", port_to_use}, // TODO: Make this configurable from the ecal.ini + {iter.hname + ".local", port_to_use}, // TODO: Make this configurable from the ecal.yaml }; const auto new_client_session = client_manager->create_client(static_cast(protocol_version), endpoint_list, event_callback); if (new_client_session) @@ -763,6 +748,37 @@ namespace eCAL } } + void CServiceClientImpl::CheckForDisconnectedServices() + { + std::lock_guard const lock(m_client_map_sync); + for (auto& client : m_client_map) + { + if (client.second->get_state() == eCAL::service::State::FAILED) + { + std::string const service_key = client.first; + + // is the service still in the connecting map ? + auto iter = m_connected_services_map.find(service_key); + if (iter != m_connected_services_map.end()) + { + // call disconnect event + std::lock_guard const lock_cb(m_event_callback_map_sync); + auto e_iter = m_event_callback_map.find(client_event_disconnected); + if (e_iter != m_event_callback_map.end()) + { + SClientEventCallbackData sdata; + sdata.type = client_event_disconnected; + sdata.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + sdata.attr = iter->second; + (e_iter->second)(m_service_name.c_str(), &sdata); + } + // remove service + m_connected_services_map.erase(iter); + } + } + } + } + void CServiceClientImpl::ErrorCallback(const std::string& method_name_, const std::string& error_message_) { std::lock_guard const lock(m_response_callback_sync); diff --git a/ecal/core/src/service/ecal_service_client_impl.h b/ecal/core/src/service/ecal_service_client_impl.h index c1368c10f6..cb9059d124 100644 --- a/ecal/core/src/service/ecal_service_client_impl.h +++ b/ecal/core/src/service/ecal_service_client_impl.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,9 +25,11 @@ #include #include #include - #include +#include "serialization/ecal_serialize_sample_registration.h" +#include "serialization/ecal_struct_service.h" + #include #include #include @@ -78,7 +80,7 @@ namespace eCAL void RegisterService(const std::string& key_, const SServiceAttr& service_); // called by eCAL:CClientGate every second to update registration layer - void RefreshRegistration(); + Registration::Sample GetRegistration(); std::string GetServiceName() { return m_service_name; }; @@ -94,10 +96,14 @@ namespace eCAL static void fromSerializedProtobuf(const std::string& response_pb_, eCAL::SServiceResponse& response_); static void fromStruct(const Service::Response& response_struct_, eCAL::SServiceResponse& response_); - void Register(bool force_); + Registration::Sample GetRegistrationSample(); + Registration::Sample GetUnregistrationSample(); + + void Register(); void Unregister(); void CheckForNewServices(); + void CheckForDisconnectedServices(); void ErrorCallback(const std::string &method_name_, const std::string &error_message_); @@ -128,7 +134,7 @@ namespace eCAL ServiceMethodInformationMapT m_method_information_map; using MethodCallCountMapT = std::map; - MethodCallCountMapT m_method_call_count_map; + MethodCallCountMapT m_method_call_count_map; std::atomic m_created; }; diff --git a/ecal/core/src/service/ecal_service_server_impl.cpp b/ecal/core/src/service/ecal_service_server_impl.cpp index bc35617942..ec1812af69 100644 --- a/ecal/core/src/service/ecal_service_server_impl.cpp +++ b/ecal/core/src/service/ecal_service_server_impl.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -128,9 +128,6 @@ namespace eCAL // mark as created m_created = true; - // register this service - Register(false); - return(true); } @@ -138,12 +135,6 @@ namespace eCAL { if (!m_created) return(false); - if (m_tcp_server_v0) - m_tcp_server_v0->stop(); - - if (m_tcp_server_v1) - m_tcp_server_v1->stop(); - // reset method callback map { std::lock_guard const lock(m_method_map_sync); @@ -156,6 +147,12 @@ namespace eCAL m_event_callback_map.clear(); } + if (m_tcp_server_v0) + m_tcp_server_v0->stop(); + + if (m_tcp_server_v1) + m_tcp_server_v1->stop(); + // mark as no more created m_created = false; @@ -200,9 +197,6 @@ namespace eCAL } } - // register this service - Register(false); - return true; } @@ -238,9 +232,6 @@ namespace eCAL } } - // register this service - Register(false); - return true; } @@ -304,40 +295,42 @@ namespace eCAL } // called by the eCAL::CServiceGate to register a client - void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) // TODO: This function is empty, why does it exist???? + void CServiceServerImpl::RegisterClient(const std::string& /*key_*/, const SClientAttr& /*client_*/) { + // this function is just a placeholder to implement logic if a new client connects + // currently there is no need to do so } // called by eCAL:CServiceGate every second to update registration layer - void CServiceServerImpl::RefreshRegistration() + Registration::Sample CServiceServerImpl::GetRegistration() { - if (!m_created) return; - Register(false); + return GetRegistrationSample(); } - void CServiceServerImpl::Register(const bool force_) + Registration::Sample CServiceServerImpl::GetRegistrationSample() { - if (!m_created) return; - if (m_service_name.empty()) return; + // create registration sample + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_reg_service; // might be zero in contruction phase unsigned short const server_tcp_port_v0(m_tcp_server_v0 ? m_tcp_server_v0->get_port() : 0); - if ((Config::IsServiceProtocolV0Enabled()) && (server_tcp_port_v0 == 0)) return; + if ((Config::IsServiceProtocolV0Enabled()) && (server_tcp_port_v0 == 0)) return ecal_reg_sample; unsigned short const server_tcp_port_v1(m_tcp_server_v1 ? m_tcp_server_v1->get_port() : 0); - if ((Config::IsServiceProtocolV1Enabled()) && (server_tcp_port_v1 == 0)) return; + if ((Config::IsServiceProtocolV1Enabled()) && (server_tcp_port_v1 == 0)) return ecal_reg_sample; - // create service registration sample - Registration::Sample sample; - sample.cmd_type = bct_reg_service; - auto& service = sample.service; + auto& identifier = ecal_reg_sample.identifier; + identifier.entity_id = m_service_id; + identifier.process_id = Process::GetProcessID(); + identifier.host_name = Process::GetHostName(); + + auto& service = ecal_reg_sample.service; service.version = m_server_version; - service.hname = Process::GetHostName(); service.pname = Process::GetProcessName(); service.uname = Process::GetUnitName(); - service.pid = Process::GetProcessID(); service.sname = m_service_name; - service.sid = m_service_id; + service.tcp_port_v0 = server_tcp_port_v0; service.tcp_port_v1 = server_tcp_port_v1; @@ -357,28 +350,51 @@ namespace eCAL } } - // register entity - if (g_registration_provider() != nullptr) g_registration_provider()->ApplySample(sample, force_); + return ecal_reg_sample; } - void CServiceServerImpl::Unregister() + Registration::Sample CServiceServerImpl::GetUnregistrationSample() { - if (m_service_name.empty()) return; + // create registration sample + Registration::Sample ecal_reg_sample; + ecal_reg_sample.cmd_type = bct_unreg_service; + + auto& identifier = ecal_reg_sample.identifier; + identifier.entity_id = m_service_id; + identifier.process_id = Process::GetProcessID(); + identifier.host_name = Process::GetHostName(); + + auto& service = ecal_reg_sample.service; + service.version = m_server_version; + service.pname = Process::GetProcessName(); + service.uname = Process::GetUnitName(); + service.sname = m_service_name; + + return ecal_reg_sample; + } - // create service registration sample - Registration::Sample sample; - sample.cmd_type = bct_unreg_service; - auto& service = sample.service; - service.version = m_server_version; - service.hname = Process::GetHostName(); - service.pname = Process::GetProcessName(); - service.uname = Process::GetUnitName(); - service.pid = Process::GetProcessID(); - service.sname = m_service_name; - service.sid = m_service_id; + void CServiceServerImpl::Register() + { +#if ECAL_CORE_REGISTRATION + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug4, m_service_name + "::CServiceServerImpl::Register"); +#endif +#endif // ECAL_CORE_REGISTRATION + } + + void CServiceServerImpl::Unregister() + { +#if ECAL_CORE_REGISTRATION + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); - // unregister entity - if (g_registration_provider() != nullptr) g_registration_provider()->ApplySample(sample, false); +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug4, m_service_name + "::CServiceServerImpl::Unregister"); +#endif +#endif // ECAL_CORE_REGISTRATION } int CServiceServerImpl::RequestCallback(const std::string& request_pb_, std::string& response_pb_) diff --git a/ecal/core/src/service/ecal_service_server_impl.h b/ecal/core/src/service/ecal_service_server_impl.h index fec56d9071..ccf741bef2 100644 --- a/ecal/core/src/service/ecal_service_server_impl.h +++ b/ecal/core/src/service/ecal_service_server_impl.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,17 +26,17 @@ #include #include #include +#include + +#include "serialization/ecal_serialize_sample_registration.h" +#include "serialization/ecal_struct_service.h" #include #include #include #include - -#include #include -#include "serialization/ecal_struct_service.h" - namespace eCAL { /** @@ -80,14 +80,17 @@ namespace eCAL void RegisterClient(const std::string& key_, const SClientAttr& client_); // called by eCAL:CServiceGate every second to update registration layer - void RefreshRegistration(); + Registration::Sample GetRegistration(); std::string GetServiceName() { return m_service_name; }; protected: - void Register(bool force_); + void Register(); void Unregister(); + Registration::Sample GetRegistrationSample(); + Registration::Sample GetUnregistrationSample(); + /** * @brief Calls the request callback based on the request and fills the response * diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/ecal/core/src/service/ecal_servicegate.cpp index fe587ea929..1b275a5d50 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,15 +94,15 @@ namespace eCAL return(ret_state); } - void CServiceGate::RefreshRegistrations() + void CServiceGate::GetRegistrations(Registration::SampleList& reg_sample_list_) { if (!m_created) return; - // refresh service registrations + // read service registrations std::shared_lock const lock(m_service_set_sync); for (const auto& service_server_impl : m_service_set) { - service_server_impl->RefreshRegistration(); + reg_sample_list_.samples.emplace_back(service_server_impl->GetRegistration()); } } } diff --git a/ecal/core/src/service/ecal_servicegate.h b/ecal/core/src/service/ecal_servicegate.h index da3047f6a1..687c527e3a 100644 --- a/ecal/core/src/service/ecal_servicegate.h +++ b/ecal/core/src/service/ecal_servicegate.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ #pragma once #include "ecal_def.h" +#include "serialization/ecal_struct_sample_registration.h" #include #include @@ -45,7 +46,7 @@ namespace eCAL bool Register (CServiceServerImpl* service_); bool Unregister(CServiceServerImpl* service_); - void RefreshRegistrations(); + void GetRegistrations(Registration::SampleList& reg_sample_list_); protected: static std::atomic m_created; diff --git a/ecal/core/src/time/ecal_timegate.cpp b/ecal/core/src/time/ecal_timegate.cpp index bfca24c0d1..6e2b528a8a 100644 --- a/ecal/core/src/time/ecal_timegate.cpp +++ b/ecal/core/src/time/ecal_timegate.cpp @@ -26,7 +26,6 @@ #include -#include "config/ecal_config_reader_hlp.h" #include "ecal_def.h" #include "ecal_timegate.h" #include "util/getenvvar.h" diff --git a/ecal/core/src/types/ecal_custom_data_types.cpp b/ecal/core/src/types/ecal_custom_data_types.cpp index 5fbf5615cd..e6fe22c541 100644 --- a/ecal/core/src/types/ecal_custom_data_types.cpp +++ b/ecal/core/src/types/ecal_custom_data_types.cpp @@ -22,6 +22,7 @@ **/ #include "ecal/types/ecal_custom_data_types.h" + #include #include #include @@ -42,10 +43,6 @@ namespace eCAL { namespace Types { - - // IpAddressV4 definitions - IpAddressV4::IpAddressV4() : IpAddressV4(NET_UDP_MULTICAST_GROUP) {}; - IpAddressV4::IpAddressV4(const std::string& ip_address_) { validateIpString(ip_address_); @@ -79,10 +76,17 @@ namespace eCAL throw std::invalid_argument("[IpAddressV4] No valid IP address: " + ip_address_); } - std::string IpAddressV4::Get() const { return m_ip_address; }; - IpAddressV4& IpAddressV4::operator=(const std::string& ip_string) { this->validateIpString(ip_string); return *this; }; - IpAddressV4::operator std::string() { return m_ip_address; }; + std::string IpAddressV4::Get() const { return m_ip_address; } + IpAddressV4& IpAddressV4::operator=(const std::string& ip_string_) { this->validateIpString(ip_string_); return *this; } + IpAddressV4& IpAddressV4::operator=(const char* ip_string_) { this->validateIpString(ip_string_); return *this; } + IpAddressV4::operator std::string() const { return m_ip_address; } - std::ostream& operator<<(std::ostream& os, const IpAddressV4& ipv4) { os << ipv4.Get(); return os; }; - } + std::ostream& operator<<(std::ostream& os, const IpAddressV4& ipv4) { os << ipv4.Get(); return os; } + + bool IpAddressV4::operator==(const eCAL::Types::IpAddressV4& rhs) const { return m_ip_address == rhs.Get(); } + bool operator==(eCAL::Types::IpAddressV4 lhs, const char* ip_string_) { return lhs.Get() == std::string(ip_string_); } + bool operator==(const char* ip_string_, eCAL::Types::IpAddressV4 rhs) { return rhs == ip_string_; } + bool operator==(eCAL::Types::IpAddressV4 lhs, const std::string& ip_string_) { return lhs.Get() == ip_string_; } + bool operator==(const std::string& ip_string_, eCAL::Types::IpAddressV4 rhs) { return rhs == ip_string_; } + } } diff --git a/ecal/core/src/types/ecal_registration_options.cpp b/ecal/core/src/types/ecal_registration_options.cpp deleted file mode 100644 index 2454ca1d69..0000000000 --- a/ecal/core/src/types/ecal_registration_options.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2024 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -/** - * @brief Definition of custom data types. -**/ - -#include -#include "ecal_def.h" - -namespace eCAL -{ - namespace Registration - { - Configuration::Configuration() - : share_ttype(PUB_SHARE_TTYPE) - , share_tdesc(PUB_SHARE_TDESC) - , m_registration_timeout(CMN_REGISTRATION_TO) - , m_registration_refresh(CMN_REGISTRATION_REFRESH) - {} - - Configuration::Configuration(unsigned int reg_timeout_, unsigned int reg_refresh_) - : share_ttype(PUB_SHARE_TTYPE) - , share_tdesc(PUB_SHARE_TDESC) - { - if (reg_refresh_ < reg_timeout_) - { - m_registration_timeout = reg_timeout_; - m_registration_refresh = reg_refresh_; - } - else - { - throw std::invalid_argument("[RegistrationOptions] Refresh(" + std::to_string(reg_refresh_) + ") >= registration timeout (" + std::to_string(reg_timeout_) + ")."); - } - } - - unsigned int Configuration::getTimeoutMS() const { return m_registration_timeout; } - unsigned int Configuration::getRefreshMS() const { return m_registration_refresh; } - } -} \ No newline at end of file diff --git a/ecal/core/src/util/ecal_expmap.h b/ecal/core/src/util/ecal_expmap.h index 9d41864ad3..496ea80e72 100644 --- a/ecal/core/src/util/ecal_expmap.h +++ b/ecal/core/src/util/ecal_expmap.h @@ -37,22 +37,35 @@ namespace eCAL namespace Util { /** - * @brief A time expiration map - **/ + * @brief A map that stores key-value pairs and the time at which they have last been updated. + * + * @tparam Key The type of the keys. + * @tparam T The type of the values. + * @tparam ClockType The type of the clock based on which the Map expires its elements + * + * This class is *not* threadsafe and needs to be protected by locks / mutexes in multithreaded environments. + * + * From the outside / for the user, this class acts as a regular std::map. + * However, it provides one additional function (erase_expired) that removes expired elements from this map. + * Elements are considered to be expired, if they have not been accessed (via `operator[]`) within a given timeout period. + * + * Internally, this is realized by storing both a map and a list. + * The map stores the regular key, and then the actual value and additional an iterator into the timestamp list. + * The timestamp list stores together a timestamp and the key which was inserted into the list at that given timestamp. + * It is always kept in a sorted order. + * + * Whenever a map element is accessed, the responding timestamp is updated and moved to the end of the list. + * This happens in constant time. + */ template, class Alloc = std::allocator > > - class CExpMap + class CExpirationMap { public: - // Key access history, most recent at back - using key_tracker_type = std::list>; - - // Key to value and key history iterator - using key_to_value_type = std::map>; - + // Type declarations necessary to be compliant to a regular map. using allocator_type = Alloc; using value_type = std::pair; using reference = typename Alloc::reference; @@ -60,6 +73,27 @@ namespace eCAL using key_type = Key; using mapped_type = T; + private: + struct AccessTimestampListEntry + { + typename ClockType::time_point timestamp; + Key corresponding_map_key; + }; + + // Key access history, most recent at back + using AccessTimestampListType = std::list; + + struct InternalMapEntry + { + T map_value; + typename AccessTimestampListType::iterator timestamp_list_iterator; + }; + + // Key to value and key history iterator + using InternalMapType = std::map; + + public: + class iterator { friend class const_iterator; @@ -71,7 +105,7 @@ namespace eCAL using pointer = std::pair*; using reference = std::pair&; - explicit iterator(const typename key_to_value_type::iterator _it) + explicit iterator(const typename InternalMapType::iterator _it) : it(_it) {} @@ -89,7 +123,7 @@ namespace eCAL std::pair operator*() const { - return std::make_pair(it->first, it->second.first); + return std::make_pair(it->first, it->second.map_value); } //friend void swap(iterator& lhs, iterator& rhs); //C++11 I think @@ -97,7 +131,7 @@ namespace eCAL bool operator!=(const iterator& rhs) const { return it != rhs.it; } private: - typename key_to_value_type::iterator it; + typename InternalMapType::iterator it; }; class const_iterator @@ -113,7 +147,7 @@ namespace eCAL : it(other.it) {} - explicit const_iterator(const typename key_to_value_type::const_iterator _it) + explicit const_iterator(const typename InternalMapType::const_iterator _it) : it(_it) {} @@ -132,7 +166,7 @@ namespace eCAL std::pair operator*() const { - return std::make_pair(it->first, it->second.first); + return std::make_pair(it->first, it->second.map_value); } //friend void swap(iterator& lhs, iterator& rhs); //C++11 I think @@ -140,74 +174,79 @@ namespace eCAL bool operator!=(const const_iterator& rhs) const { return it != rhs.it; } private: - typename key_to_value_type::const_iterator it; + typename InternalMapType::const_iterator it; }; // Constructor specifies the timeout of the map - CExpMap() : _timeout(std::chrono::milliseconds(5000)) {}; - explicit CExpMap(typename clock_type::duration t) : _timeout(t) {}; + CExpirationMap() : _timeout(std::chrono::milliseconds(5000)) {}; + explicit CExpirationMap(typename ClockType::duration t) : _timeout(t) {}; /** * @brief set expiration time **/ - void set_expiration(typename clock_type::duration t) { _timeout = t; }; + void set_expiration(typename ClockType::duration t) { _timeout = t; }; // Iterators: iterator begin() noexcept { - return iterator(_key_to_value.begin()); + return iterator(_internal_map.begin()); } iterator end() noexcept { - return iterator(_key_to_value.end()); + return iterator(_internal_map.end()); } const_iterator begin() const noexcept { - return const_iterator(_key_to_value.begin()); + return const_iterator(_internal_map.begin()); } const_iterator end() const noexcept { - return const_iterator(_key_to_value.end()); + return const_iterator(_internal_map.end()); } // Const begin and end functions const_iterator cbegin() const noexcept { - return const_iterator(_key_to_value.cbegin()); + return const_iterator(_internal_map.cbegin()); } const_iterator cend() const noexcept { - return const_iterator(_key_to_value.cend()); + return const_iterator(_internal_map.cend()); } // Capacity bool empty() const noexcept { - return _key_to_value.empty(); + return _internal_map.empty(); } size_type size() const noexcept { - return _key_to_value.size(); + return _internal_map.size(); } size_type max_size() const noexcept { - return _key_to_value.max_size(); + return _internal_map.max_size(); } - // Element access - // Obtain value of the cached function for k + /** + * @brief Accesses the value associated with the given key, resetting its expiration time. + * + * @param key The key to access the value for. + * + * @return The value associated with the key. + */ T& operator[](const Key& k) { // Attempt to find existing record - typename key_to_value_type::iterator it - = _key_to_value.find(k); + typename InternalMapType::iterator it + = _internal_map.find(k); - if (it == _key_to_value.end()) + if (it == _internal_map.end()) { // We don't have it: // Evaluate function and create new record @@ -224,17 +263,17 @@ namespace eCAL } // Return the retrieved value - return (*it).second.first; + return (*it).second.map_value; }; mapped_type& at(const key_type& k) { - return _key_to_value.at(k).first; + return _internal_map.at(k).first; } const mapped_type& at(const key_type& k) const { - return _key_to_value.at(k).first; + return _internal_map.at(k).first; } // Modifiers @@ -247,39 +286,46 @@ namespace eCAL // Operations iterator find(const key_type& k) { - return iterator(_key_to_value.find(k)); + return iterator(_internal_map.find(k)); } const_iterator find(const Key& k) const { - return const_iterator(_key_to_value.find(k)); + return const_iterator(_internal_map.find(k)); } - // Purge the timed out elements from the cache - void remove_deprecated(std::list* key_erased = nullptr) //-V826 + /** + * @brief Erase all expired key-value pairs from the map. + * + * This function erases all expired key-value pairs from the internal map / timestamp list. + * The CExpirationMap class does not call this function internally, it has to be called explicitly by the user. + */ + std::map erase_expired() { - // Assert method is never called when cache is empty - //assert(!_key_tracker.empty()); - typename clock_type::time_point eviction_limit = get_curr_time() - _timeout; - - auto it(_key_tracker.begin()); - - while (it != _key_tracker.end() && it->first < eviction_limit) + std::map erased_values; + // To erase timestamps from the map, the time point of the last access is calculated, all older entries will be erased. + // Since the list is sorted, we need to remove everything from the first element until the eviction limit. + typename ClockType::time_point eviction_limit = get_curr_time() - _timeout; + auto it(_access_timestamps_list.begin()); + while (it != _access_timestamps_list.end() && it->timestamp < eviction_limit) { - if (key_erased != nullptr) key_erased->push_back(it->second); - _key_to_value.erase(it->second); // erase the element from the map - it = _key_tracker.erase(it); // erase the element from the list + auto erased_value = _internal_map.find(it->corresponding_map_key); + // for performance reason, we should be able to move from the map, however with C++17 we can use std::map::extract + erased_values[it->corresponding_map_key] = erased_value->second.map_value; + _internal_map.erase(it->corresponding_map_key); // erase the element from the map + it = _access_timestamps_list.erase(it); // erase the element from the list } + return erased_values; } // Remove specific element from the cache bool erase(const Key& k) { - auto it = _key_to_value.find(k); - if (it != _key_to_value.end()) + auto it = _internal_map.find(k); + if (it != _internal_map.end()) { - _key_tracker.erase(it->second.second); // erase the element from the list - _key_to_value.erase(k); // erase the element from the map + _access_timestamps_list.erase(it->second.timestamp_list_iterator); // erase the element from the list + _internal_map.erase(k); // erase the element from the map return true; } return false; @@ -288,8 +334,8 @@ namespace eCAL // Remove all elements from the cache void clear() { - _key_to_value.clear(); // erase all elements from the map - _key_tracker.clear(); // erase all elements from the list + _internal_map.clear(); // erase all elements from the map + _access_timestamps_list.clear(); // erase all elements from the list } private: @@ -297,49 +343,49 @@ namespace eCAL // Maybe pass the iterator instead of the key? or at least only get k once void update_timestamp(const Key& k) { - auto it_in_map = _key_to_value.find(k); - if (it_in_map != _key_to_value.end()) + auto it_in_map = _internal_map.find(k); + if (it_in_map != _internal_map.end()) { - auto& it_in_list = it_in_map->second.second; + auto& it_in_list = it_in_map->second.timestamp_list_iterator; // move the element to the end of the list - _key_tracker.splice(_key_tracker.end(), _key_tracker, it_in_list); + _access_timestamps_list.splice(_access_timestamps_list.end(), _access_timestamps_list, it_in_list); // update the timestamp - it_in_list->first = get_curr_time(); + it_in_list->timestamp = get_curr_time(); } } - // Record a fresh key-value pair in the cache - std::pair insert(const Key& k, const T& v) + // Record a fresh key-value pair in the cache + std::pair insert(const Key& k, const T& v) { // sorted list, containing (pair ( timestamp, K)) - auto it = _key_tracker.emplace(_key_tracker.end(), std::make_pair(get_curr_time(), k)); + auto it = _access_timestamps_list.emplace(_access_timestamps_list.end(), AccessTimestampListEntry{ get_curr_time(), k }); // entry mapping k -> pair (T, iterator(pair(timestamp, K))) - auto ret = _key_to_value.emplace( + auto ret = _internal_map.emplace( std::make_pair( k, - std::make_pair(v, it) + InternalMapEntry{ v, it } ) ); // return iterator to newly inserted element. return ret; } - typename clock_type::time_point get_curr_time() + typename ClockType::time_point get_curr_time() { - return clock_type::now(); + return ClockType::now(); } // Key access history - key_tracker_type _key_tracker; + AccessTimestampListType _access_timestamps_list; // Key-to-value lookup - key_to_value_type _key_to_value; + InternalMapType _internal_map; // Timeout of map - typename clock_type::duration _timeout; + typename ClockType::duration _timeout; }; } } diff --git a/ecal/core/src/util/ecal_thread.h b/ecal/core/src/util/ecal_thread.h index 49aec75437..7aa1d13fba 100644 --- a/ecal/core/src/util/ecal_thread.h +++ b/ecal/core/src/util/ecal_thread.h @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -84,12 +84,28 @@ namespace eCAL } } + /** + * @brief Trigger the callback thread to interrupt the current sleep without stopping it. + * The callback function will be executed immediately. + */ + void trigger() + { + { + const std::unique_lock lock(mtx_); + // Set the flag to signal the callback thread to trigger + triggerThread_ = true; + // Notify the callback thread to wake up and check the flag + cv_.notify_one(); + } + } + private: std::thread callbackThread_; /**< The callback thread object. */ std::function callback_; /**< The callback function to be executed in the callback thread. */ std::mutex mtx_; /**< Mutex for thread synchronization. */ std::condition_variable cv_; /**< Condition variable for signaling between threads. */ - bool stopThread_{false}; /**< Flag to indicate whether the callback thread should stop. */ + bool stopThread_{ false }; /**< Flag to indicate whether the callback thread should stop. */ + bool triggerThread_{ false }; /**< Flag to indicate whether the callback thread should be triggered. */ /** * @brief Callback function that runs in the callback thread. @@ -105,10 +121,17 @@ namespace eCAL { std::unique_lock lock(mtx_); // Wait for a signal or a timeout - if (cv_.wait_for(lock, timeout, [this] { return stopThread_; })) + if (cv_.wait_for(lock, timeout, [this] { return stopThread_ || triggerThread_; })) { - // If the stopThread flag is true, break out of the loop - break; + if (stopThread_) { + // If the stopThread flag is true, break out of the loop + break; + } + + if (triggerThread_) { + // If the triggerThread flag is true, reset it and proceed + triggerThread_ = false; + } } } diff --git a/ecal/core/src/util/frequency_calculator.h b/ecal/core/src/util/frequency_calculator.h index 1ec85f4f04..05bda24a3f 100644 --- a/ecal/core/src/util/frequency_calculator.h +++ b/ecal/core/src/util/frequency_calculator.h @@ -1,6 +1,6 @@ -/* ========================= eCAL LICENSE ================================= +/* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,8 +52,8 @@ namespace eCAL FrequencyCalculator(const time_point& now_) : counted_elements(0) - , first_tick(now_) - , last_tick(now_) + , first_tick_time(now_) + , last_tick_time(now_) , previous_frequency(0.0) { } @@ -61,7 +61,7 @@ namespace eCAL void addTick(const time_point& now) { counted_elements++; - last_tick = now; + last_tick_time = now; } double getFrequency() @@ -71,16 +71,16 @@ namespace eCAL return previous_frequency; } - previous_frequency = calculateFrequency(first_tick, last_tick, counted_elements); - first_tick = last_tick; + previous_frequency = calculateFrequency(first_tick_time, last_tick_time, counted_elements); + first_tick_time = last_tick_time; counted_elements = 0; return previous_frequency; } private: long long counted_elements; - time_point first_tick; - time_point last_tick; + time_point first_tick_time; + time_point last_tick_time; double previous_frequency; }; @@ -105,22 +105,37 @@ namespace eCAL { calculator->addTick(now); } - last_tick = now; + last_tick_time = now; + received_tick_since_get_frequency_called = true; } double getFrequency(const time_point& now) { double frequency = calculator ? calculator->getFrequency() : 0.0; + // if the frequency is 0.0, return it right away if (frequency == 0.0) { return 0.0; } - // calculate theoretical frequency to detect timeouts; - double theoretical_frequency = calculateFrequency(last_tick, now, 1); - // If the frequency is higher than reset_factor * theoretical_frequency, we reset. - if (frequency >= theoretical_frequency * reset_factor) + // if we have received ticks, this means we don't want to reset the calculation + // so we return the frequency that the calculator has calculated + if (received_tick_since_get_frequency_called) + { + received_tick_since_get_frequency_called = false; + return frequency; + } + + // if we have not received ticks since the last call of getFrequency + // then we calculate the ΔT, e.g. the timespan in seconds between last received and next incoming element + // Based on the last time we received a tick we could calculate when we expect the next tick + // We can also calculate when we want to time out, based on the reset factor + // timeout = last_tick + ΔT * reset_factor + // If the current time is greater than the timeout time, we reset the calculator and return 0 + auto expected_time_in_seconds_between_last_and_next_tick = std::chrono::duration(1 / frequency); + time_point timeout_time = last_tick_time + std::chrono::duration_cast(expected_time_in_seconds_between_last_and_next_tick * reset_factor); + if (now > timeout_time) { calculator.reset(); return 0.0; @@ -131,7 +146,8 @@ namespace eCAL private: float reset_factor; - time_point last_tick; + time_point last_tick_time; + bool received_tick_since_get_frequency_called = false; std::unique_ptr> calculator; }; } diff --git a/ecal/core_pb/src/ecal/core/pb/ecal.proto b/ecal/core_pb/src/ecal/core/pb/ecal.proto index 2b4605b192..b5c0044940 100644 --- a/ecal/core_pb/src/ecal/core/pb/ecal.proto +++ b/ecal/core_pb/src/ecal/core/pb/ecal.proto @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,8 @@ package eCAL.pb; message Content // topic content { - reserved 5; + // Reserved fields in enums are not supported in protobuf 3.0 + // reserved 5; int64 id = 1; // sample id int64 clock = 2; // internal used clock diff --git a/ecal/core_pb/src/ecal/core/pb/layer.proto b/ecal/core_pb/src/ecal/core/pb/layer.proto index dc45cd1b1a..db9f6d7b25 100644 --- a/ecal/core_pb/src/ecal/core/pb/layer.proto +++ b/ecal/core_pb/src/ecal/core/pb/layer.proto @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,8 @@ message LayerParTcp message ConnnectionPar // connection parameter for reader / writer { - reserved 3; + // Reserved fields in enums are not supported in protobuf 3.0 + // reserved 3; LayerParUdpMC layer_par_udpmc = 1; // parameter for ecal udp multicast LayerParShm layer_par_shm = 2; // parameter for ecal shared memory @@ -62,10 +63,12 @@ enum eTLayerType // transport layer message TLayer { - reserved 4; + // Reserved fields in enums are not supported in protobuf 3.0 + // reserved 4; eTLayerType type = 1; // transport layer type int32 version = 2; // transport layer version - bool confirmed = 3; // transport layer used ? + bool enabled = 6; // transport layer enabled ? + bool active = 3; // transport layer in use ? ConnnectionPar par_layer = 5; // transport layer parameter } diff --git a/ecal/core_pb/src/ecal/core/pb/process.proto b/ecal/core_pb/src/ecal/core/pb/process.proto index 014b51229f..e59d3dae77 100644 --- a/ecal/core_pb/src/ecal/core/pb/process.proto +++ b/ecal/core_pb/src/ecal/core/pb/process.proto @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,7 +56,8 @@ enum eTSyncState // time synchronisatio message Process // process { - reserved 7 to 11; + // Reserved fields in enums are not supported in protobuf 3.0 + // reserved 7 to 11; int32 rclock = 1; // registration clock string hname = 2; // host name diff --git a/ecal/core_pb/src/ecal/core/pb/topic.proto b/ecal/core_pb/src/ecal/core/pb/topic.proto index 84d657bf15..0da480cf68 100644 --- a/ecal/core_pb/src/ecal/core/pb/topic.proto +++ b/ecal/core_pb/src/ecal/core/pb/topic.proto @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,8 @@ message DataTypeInformation message Topic // eCAL topic { - reserved 9, 10, 11, 14, 15, 22 to 26, 29; + // Reserved fields in enums are not supported in protobuf 3.0 + // reserved 9, 10, 11, 14, 15, 22 to 26, 29; int32 rclock = 1; // registration clock (heart beat) string hname = 2; // host name diff --git a/ecal/ecal-core-options.cmake b/ecal/ecal-core-options.cmake index 60d20fdcef..7a1903de66 100644 --- a/ecal/ecal-core-options.cmake +++ b/ecal/ecal-core-options.cmake @@ -12,7 +12,7 @@ option(ECAL_CORE_BUILD_TESTS "Build the eCAL google # ------------------------------------------------------------------------------------------------------------------------------------------------------------------ # core internal feature configuration (adapt to your needs) # ------------------------------------------------------------------------------------------------------------------------------------------------------------------ -option(ECAL_CORE_CONFIG_INIFILE "Enables to configure eCAL via ecal.ini file" ON) +option(ECAL_CORE_CONFIGURATION "Enables to configure eCAL at runtime via ecal.yaml file" ON) option(ECAL_CORE_COMMAND_LINE "Enables eCAL application cmd line interfaces" ON) option(ECAL_CORE_REGISTRATION "Enables the eCAL registration layer" ON) option(ECAL_CORE_MONITORING "Enables the eCAL monitoring functionality" ON) diff --git a/ecal/samples/CMakeLists.txt b/ecal/samples/CMakeLists.txt index ccb0f0a67c..eb751a33bd 100644 --- a/ecal/samples/CMakeLists.txt +++ b/ecal/samples/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -66,6 +66,7 @@ endif() if(ECAL_CORE_PUBLISHER AND ECAL_CORE_SUBSCRIBER) add_subdirectory(cpp/benchmarks/perftool) + add_subdirectory(cpp/benchmarks/massive_pub_sub) endif() # misc diff --git a/ecal/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp b/ecal/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp index 6349ee7a6f..90e491acdd 100644 --- a/ecal/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp +++ b/ecal/samples/cpp/benchmarks/datarate_snd/src/datarate_snd.cpp @@ -67,11 +67,11 @@ int main(int argc, char **argv) // create publisher config eCAL::Publisher::Configuration pub_config; // set zero copy - pub_config.shm.zero_copy_mode = zero_copy; + pub_config.layer.shm.zero_copy_mode = zero_copy; // set buffering - pub_config.shm.memfile_buffer_count = buffer_count; + pub_config.layer.shm.memfile_buffer_count = buffer_count; // set handshake acknowledgement timeout [ms] - pub_config.shm.acknowledge_timeout_ms = acknowledge_time; + pub_config.layer.shm.acknowledge_timeout_ms = acknowledge_time; // new publisher eCAL::CPublisher pub(topic_name, pub_config); diff --git a/ecal/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp b/ecal/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp index bddb0e29ff..d4822471ec 100644 --- a/ecal/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp +++ b/ecal/samples/cpp/benchmarks/latency_snd/src/latency_snd.cpp @@ -52,11 +52,11 @@ void do_run(const int runs, int snd_size /*kB*/, int mem_buffer, bool zero_copy) // create publisher config eCAL::Publisher::Configuration pub_config; // set number of publisher memory buffers - pub_config.shm.memfile_buffer_count = mem_buffer; + pub_config.layer.shm.memfile_buffer_count = mem_buffer; // enable zero copy mode - pub_config.shm.zero_copy_mode = zero_copy; + pub_config.layer.shm.zero_copy_mode = zero_copy; // set acknowledgement timeout to 100ms - pub_config.shm.acknowledge_timeout_ms = 100; + pub_config.layer.shm.acknowledge_timeout_ms = 100; // create publisher eCAL::CPublisher pub("ping", pub_config); diff --git a/ecal/samples/cpp/benchmarks/massive_pub_sub/CMakeLists.txt b/ecal/samples/cpp/benchmarks/massive_pub_sub/CMakeLists.txt new file mode 100644 index 0000000000..5c2f37e547 --- /dev/null +++ b/ecal/samples/cpp/benchmarks/massive_pub_sub/CMakeLists.txt @@ -0,0 +1,41 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2024 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) + +project(massive_pub_sub) + +find_package(eCAL REQUIRED) + +set(massive_pub_sub_src + src/massive_pub_sub.cpp +) + +ecal_add_sample(${PROJECT_NAME} ${massive_pub_sub_src}) + +target_link_libraries(${PROJECT_NAME} + eCAL::core +) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_sample(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/benchmarks/massive_pub_sub) diff --git a/ecal/samples/cpp/benchmarks/massive_pub_sub/src/massive_pub_sub.cpp b/ecal/samples/cpp/benchmarks/massive_pub_sub/src/massive_pub_sub.cpp new file mode 100644 index 0000000000..fb30f34784 --- /dev/null +++ b/ecal/samples/cpp/benchmarks/massive_pub_sub/src/massive_pub_sub.cpp @@ -0,0 +1,184 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include +#include +#include +#include +#include + +const int subscriber_number (10000); + +const int publisher_number (10000); +const int publisher_type_encoding_size_bytes (10*1024); +const int publisher_type_descriptor_size_bytes (10*1024); + +const int in_between_sleep_sec (5); +const int final_sleep_sec (0); + +std::string GenerateSizedString(const std::string& name, size_t totalSize) +{ + if (name.empty() || totalSize == 0) { + return ""; + } + + std::string result; + result.reserve(totalSize); + + while (result.size() + name.size() <= totalSize) { + result += name; + } + + if (result.size() < totalSize) { + result += name.substr(0, totalSize - result.size()); + } + + return result; +} + +int main(int argc, char** argv) +{ + // initialize eCAL API with shm monitoring + eCAL::Configuration configuration; + configuration.registration.layer.shm.enable = true; + configuration.registration.layer.udp.enable = false; + eCAL::Initialize(configuration, "massive_pub_sub"); + + eCAL::Util::EnableLoopback(true); + + // create subscriber + std::vector vector_of_subscriber; + std::cout << "Subscriber creation started. (" << subscriber_number << ")" << std::endl; + { + // start time measurement + auto start_time = std::chrono::high_resolution_clock::now(); + + for (int i = 0; i < subscriber_number; i++) + { + // publisher topic name + std::stringstream tname; + tname << "TOPIC_" << i; + + // create subscriber + vector_of_subscriber.emplace_back(tname.str()); + } + // stop time measurement + auto end_time = std::chrono::high_resolution_clock::now(); + + // calculate the duration + auto duration = std::chrono::duration_cast(end_time - start_time).count(); + std::cout << "Time taken for subscriber creation: " << duration << " milliseconds" << std::endl << std::endl; + } + + // sleep for a few seconds + std::this_thread::sleep_for(std::chrono::seconds(in_between_sleep_sec)); + + // create publisher + std::vector vector_of_publisher; + std::cout << "Publisher creation started. (" << publisher_number << ")" << std::endl; + { + // start time measurement + auto start_time = std::chrono::high_resolution_clock::now(); + + eCAL::SDataTypeInformation data_type_info; + data_type_info.name = "TOPIC_TYPE_NAME"; + data_type_info.encoding = GenerateSizedString("TOPIC_TYPE_ENCODING", publisher_type_encoding_size_bytes); + data_type_info.descriptor = GenerateSizedString("TOPIC_TYPE_DESCRIPTOR", publisher_type_descriptor_size_bytes); + + for (int i = 0; i < publisher_number; i++) + { + // publisher topic name + std::stringstream tname; + tname << "TOPIC_" << i; + + // create publisher + vector_of_publisher.emplace_back(tname.str(), data_type_info); + } + // stop time measurement + auto end_time = std::chrono::high_resolution_clock::now(); + + // calculate the duration + auto duration = std::chrono::duration_cast(end_time - start_time).count(); + std::cout << "Time taken for publisher creation: " << duration << " milliseconds" << std::endl << std::endl; + } + + // sleep for a few seconds + std::this_thread::sleep_for(std::chrono::seconds(in_between_sleep_sec)); + + // wait for full registration + std::cout << "Wait for publisher/subscriber registration." << std::endl; + { + // start time measurement + auto start_time = std::chrono::high_resolution_clock::now(); + + size_t num_pub(0); + size_t num_sub(0); + while ((num_pub < publisher_number) || (num_sub < subscriber_number)) + { + num_pub = eCAL::Registration::GetPublisherIDs().size(); + num_sub = eCAL::Registration::GetSubscriberIDs().size(); + + std::cout << "Registered publisher : " << num_pub << std::endl; + std::cout << "Registered subscriber: " << num_sub << std::endl; + + // sleep for 1000 milliseconds + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + + // stop time measurement + auto end_time = std::chrono::high_resolution_clock::now(); + + // calculate the duration + auto duration = std::chrono::duration_cast(end_time - start_time).count(); + std::cout << "Time taken to get all registered: " << duration << " milliseconds" << std::endl << std::endl; + } + + // get publisher information + std::cout << "Get publisher information. ("; + size_t num_pub(0); + { + // start time measurement + auto start_time = std::chrono::high_resolution_clock::now(); + + const auto pub_ids = eCAL::Registration::GetPublisherIDs(); + num_pub = pub_ids.size(); + for (const auto& id : pub_ids) + { + eCAL::Registration::SQualityTopicInfo topic_info; + eCAL::Registration::GetPublisherInfo(id, topic_info); + } + + // stop time measurement + auto end_time = std::chrono::high_resolution_clock::now(); + + // calculate the duration + auto duration = std::chrono::duration_cast(end_time - start_time).count(); + std::cout << num_pub << ")" << std::endl << "Time taken to get publisher information: " << duration << " milliseconds" << std::endl << std::endl; + } + + // sleep for a few seconds + std::this_thread::sleep_for(std::chrono::seconds(final_sleep_sec)); + + // finalize eCAL API + eCAL::Finalize(); + + return(0); +} diff --git a/ecal/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp b/ecal/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp index 0e99485386..3889442710 100644 --- a/ecal/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp +++ b/ecal/samples/cpp/benchmarks/performance_snd/src/performance_snd.cpp @@ -50,15 +50,15 @@ int main(int argc, char **argv) // enable zero copy mode std::cout << "Zero copy mode: " << zero_copy << std::endl; - pub_config.shm.zero_copy_mode = zero_copy; + pub_config.layer.shm.zero_copy_mode = zero_copy; // set write buffer count std::cout << "Number of write buffers: " << buffer_count << std::endl; - pub_config.shm.memfile_buffer_count = buffer_count; + pub_config.layer.shm.memfile_buffer_count = buffer_count; // enable handshake mode std::cout << "Acknowledge timeout: " << acknowledge_timeout_ms << " ms" << std::endl; - pub_config.shm.acknowledge_timeout_ms = acknowledge_timeout_ms; + pub_config.layer.shm.acknowledge_timeout_ms = acknowledge_timeout_ms; std::cout << std::endl; // create publisher diff --git a/ecal/samples/cpp/benchmarks/pubsub_throughput/src/pubsub_throughput.cpp b/ecal/samples/cpp/benchmarks/pubsub_throughput/src/pubsub_throughput.cpp index f306a9f819..734b9ba051 100644 --- a/ecal/samples/cpp/benchmarks/pubsub_throughput/src/pubsub_throughput.cpp +++ b/ecal/samples/cpp/benchmarks/pubsub_throughput/src/pubsub_throughput.cpp @@ -37,27 +37,27 @@ void throughput_test(int snd_size, int snd_loops, eCAL::TLayer::eTransportLayer eCAL::Publisher::Configuration pub_config; // set transport layer - pub_config.shm.enable = false; - pub_config.udp.enable = false; - pub_config.tcp.enable = false; + pub_config.layer.shm.enable = false; + pub_config.layer.udp.enable = false; + pub_config.layer.tcp.enable = false; switch (layer) { case eCAL::TLayer::tlayer_shm: - pub_config.shm.enable = true; + pub_config.layer.shm.enable = true; break; case eCAL::TLayer::tlayer_udp_mc: - pub_config.udp.enable = true; + pub_config.layer.udp.enable = true; break; case eCAL::TLayer::tlayer_tcp: - pub_config.tcp.enable = true; + pub_config.layer.tcp.enable = true; break; } // enable zero copy mode - pub_config.shm.zero_copy_mode = zero_copy; + pub_config.layer.shm.zero_copy_mode = zero_copy; // enable handshake mode - pub_config.shm.acknowledge_timeout_ms = 100; + pub_config.layer.shm.acknowledge_timeout_ms = 100; // create publisher eCAL::CPublisher pub("throughput", pub_config); diff --git a/ecal/samples/cpp/misc/config/src/config_sample.cpp b/ecal/samples/cpp/misc/config/src/config_sample.cpp index 42ad2f27f0..ebe041c551 100644 --- a/ecal/samples/cpp/misc/config/src/config_sample.cpp +++ b/ecal/samples/cpp/misc/config/src/config_sample.cpp @@ -27,8 +27,8 @@ int main(int argc, char **argv) // creating config object eCAL::Configuration my_config(argc, argv); - // setting configuration - my_config.monitoring.network_monitoring = true; + // setting a configuration + my_config.registration.network_enabled = true; // initialize eCAL API eCAL::Initialize(my_config, "config sample"); diff --git a/ecal/samples/cpp/monitoring/monitoring_get_services/src/monitoring_get_services.cpp b/ecal/samples/cpp/monitoring/monitoring_get_services/src/monitoring_get_services.cpp index fa72608997..0d52b41719 100644 --- a/ecal/samples/cpp/monitoring/monitoring_get_services/src/monitoring_get_services.cpp +++ b/ecal/samples/cpp/monitoring/monitoring_get_services/src/monitoring_get_services.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,12 +38,12 @@ int main(int argc, char **argv) { // GetServices { - std::map service_info_map; + std::map service_info_map; start_time = std::chrono::steady_clock::now(); for (run = 0; run < runs; ++run) { - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); } auto num_services = service_info_map.size(); @@ -54,12 +54,12 @@ int main(int argc, char **argv) // GetServiceMethodNames { - std::set service_method_names; + std::set service_method_names; start_time = std::chrono::steady_clock::now(); for (run = 0; run < runs; ++run) { - eCAL::Util::GetServiceMethodNames(service_method_names); + eCAL::Registration::GetServiceMethodNames(service_method_names); } auto num_services = service_method_names.size(); diff --git a/ecal/samples/cpp/monitoring/monitoring_get_topics/src/monitoring_get_topics.cpp b/ecal/samples/cpp/monitoring/monitoring_get_topics/src/monitoring_get_topics.cpp index 08eea271e5..e1d1fb0a11 100644 --- a/ecal/samples/cpp/monitoring/monitoring_get_topics/src/monitoring_get_topics.cpp +++ b/ecal/samples/cpp/monitoring/monitoring_get_topics/src/monitoring_get_topics.cpp @@ -43,7 +43,7 @@ int main(int argc, char **argv) start_time = std::chrono::steady_clock::now(); for (run = 0; run < runs; ++run) { - eCAL::Util::GetTopics(topic_info_map); + eCAL::Registration::GetTopics(topic_info_map); } auto num_topics = topic_info_map.size(); @@ -59,7 +59,7 @@ int main(int argc, char **argv) start_time = std::chrono::steady_clock::now(); for (run = 0; run < runs; ++run) { - eCAL::Util::GetTopicNames(topic_names); + eCAL::Registration::GetTopicNames(topic_names); } auto num_topics = topic_names.size(); diff --git a/ecal/samples/cpp/monitoring/monitoring_rec/src/monitoring_rec.cpp b/ecal/samples/cpp/monitoring/monitoring_rec/src/monitoring_rec.cpp index 30e605ea2b..b8bde04b0f 100644 --- a/ecal/samples/cpp/monitoring/monitoring_rec/src/monitoring_rec.cpp +++ b/ecal/samples/cpp/monitoring/monitoring_rec/src/monitoring_rec.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -195,18 +195,18 @@ int main(int argc, char** argv) default: break; } - std::cout << " tlayer.type : " << layer_type << std::endl; // transport layers type - std::cout << " tlayer.confirmed: " << layer.confirmed() << std::endl; // transport layers confirmation + std::cout << " tlayer.type : " << layer_type << std::endl; // transport layers type + std::cout << " tlayer.active : " << layer.active() << std::endl; // transport layers confirmation } - std::cout << "tsize : " << topic.tsize() << std::endl; // topic size + std::cout << "tsize : " << topic.tsize() << std::endl; // topic size std::cout << "connections_loc : " << topic.connections_loc() << std::endl; // number of local connected entities std::cout << "connections_ext : " << topic.connections_ext() << std::endl; // number of external connected entities - std::cout << "message_drops : " << topic.message_drops() << std::endl; // dropped messages + std::cout << "message_drops : " << topic.message_drops() << std::endl; // dropped messages - std::cout << "did : " << topic.did() << std::endl; // data send id (publisher setid) - std::cout << "dclock : " << topic.dclock() << std::endl; // data clock (send / receive action) - std::cout << "dfreq : " << topic.dfreq() << std::endl; // data frequency (send / receive samples per second * 1000) + std::cout << "did : " << topic.did() << std::endl; // data send id (publisher setid) + std::cout << "dclock : " << topic.dclock() << std::endl; // data clock (send / receive action) + std::cout << "dfreq : " << topic.dfreq() << std::endl; // data frequency (send / receive samples per second * 1000) std::cout << std::endl; } diff --git a/ecal/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp b/ecal/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp index 4524357bc1..065353cd1c 100644 --- a/ecal/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp +++ b/ecal/samples/cpp/pubsub/binary/binary_zero_copy_snd/src/binary_zero_copy_snd.cpp @@ -127,7 +127,7 @@ int main(int argc, char** argv) eCAL::Publisher::Configuration pub_config; // turn zero copy mode on - pub_config.shm.zero_copy_mode = true; + pub_config.layer.shm.zero_copy_mode = true; // create the publisher eCAL::CPublisher pub(topicName, { "custom", structTypeName, "" }, pub_config); diff --git a/ecal/samples/cpp/pubsub/protobuf/person_rec/src/person_rec.cpp b/ecal/samples/cpp/pubsub/protobuf/person_rec/src/person_rec.cpp index 9bb405b705..63a80c26a6 100644 --- a/ecal/samples/cpp/pubsub/protobuf/person_rec/src/person_rec.cpp +++ b/ecal/samples/cpp/pubsub/protobuf/person_rec/src/person_rec.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,8 +53,16 @@ int main(int argc, char **argv) // set process state eCAL::Process::SetState(proc_sev_healthy, proc_sev_level1, "I feel good !"); + // create a subscriber config + eCAL::Subscriber::Configuration sub_config; + + // activate transport layer + sub_config.layer.shm.enable = true; + sub_config.layer.udp.enable = true; + sub_config.layer.tcp.enable = true; + // create a subscriber (topic name "person") - eCAL::protobuf::CSubscriber sub("person"); + eCAL::protobuf::CSubscriber sub("person", sub_config); // add receive callback function (_1 = topic_name, _2 = msg, _3 = time, _4 = clock, _5 = id) auto callback = std::bind(OnPerson, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/person_snd_tcp.cpp b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/person_snd_tcp.cpp index 69432a494b..b7feae04aa 100644 --- a/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/person_snd_tcp.cpp +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_tcp/src/person_snd_tcp.cpp @@ -36,9 +36,9 @@ int main(int argc, char **argv) eCAL::Publisher::Configuration pub_config; // switch shm and udp layer off, tcp layer on - pub_config.shm.enable = false; - pub_config.udp.enable = false; - pub_config.tcp.enable = true; + pub_config.layer.shm.enable = false; + pub_config.layer.udp.enable = false; + pub_config.layer.tcp.enable = true; // create a publisher (topic name "person") eCAL::protobuf::CPublisher pub("person", pub_config); diff --git a/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/person_snd_udp.cpp b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/person_snd_udp.cpp index 655909392f..e5a8d1f786 100644 --- a/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/person_snd_udp.cpp +++ b/ecal/samples/cpp/pubsub/protobuf/person_snd_udp/src/person_snd_udp.cpp @@ -36,9 +36,9 @@ int main(int argc, char **argv) eCAL::Publisher::Configuration pub_config; // switch shm and tcp layer off, udp layer on - pub_config.shm.enable = false; - pub_config.udp.enable = true; - pub_config.tcp.enable = false; + pub_config.layer.shm.enable = false; + pub_config.layer.udp.enable = true; + pub_config.layer.tcp.enable = false; // create a publisher (topic name "person") eCAL::protobuf::CPublisher pub("person", pub_config); diff --git a/ecal/samples/cpp/services/latency_client/src/latency_client.cpp b/ecal/samples/cpp/services/latency_client/src/latency_client.cpp index b5523f88b1..2c9a5f07d4 100644 --- a/ecal/samples/cpp/services/latency_client/src/latency_client.cpp +++ b/ecal/samples/cpp/services/latency_client/src/latency_client.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ int main(int argc, char** argv) eCAL::CServiceClient latency_client("latency"); // waiting for service - while (!latency_client.IsConnected()) + while (eCAL::Ok() && !latency_client.IsConnected()) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::cout << "Waiting for the service .." << std::endl; diff --git a/ecal/samples/cpp/services/math_client/src/math_client.cpp b/ecal/samples/cpp/services/math_client/src/math_client.cpp index 2a5d93bf57..e54921c4df 100644 --- a/ecal/samples/cpp/services/math_client/src/math_client.cpp +++ b/ecal/samples/cpp/services/math_client/src/math_client.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,7 +105,7 @@ int main(int argc, char **argv) int inp2(0); // waiting for service - while (!math_client.IsConnected()) + while (eCAL::Ok() && !math_client.IsConnected()) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::cout << "Waiting for the service .." << std::endl; diff --git a/ecal/samples/cpp/services/ping_client/src/ping_client.cpp b/ecal/samples/cpp/services/ping_client/src/ping_client.cpp index 75ab654eed..e2022da6cb 100644 --- a/ecal/samples/cpp/services/ping_client/src/ping_client.cpp +++ b/ecal/samples/cpp/services/ping_client/src/ping_client.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ int main(int argc, char **argv) eCAL::protobuf::CServiceClient ping_client("ping service"); // waiting for service - while (!ping_client.IsConnected()) + while (eCAL::Ok() && !ping_client.IsConnected()) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::cout << "Waiting for the service .." << std::endl; diff --git a/ecal/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp b/ecal/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp index c7b140ba27..7b5f78a554 100644 --- a/ecal/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp +++ b/ecal/samples/cpp/services/ping_client_dyn/src/ping_client_dyn.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ int main(int argc, char **argv) eCAL::CServiceClient ping_client(service_name); // waiting for service - while (!ping_client.IsConnected()) + while (eCAL::Ok() && !ping_client.IsConnected()) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::cout << "Waiting for the service .." << '\n'; @@ -49,7 +49,7 @@ int main(int argc, char **argv) // get service method type names std::string req_type; std::string resp_type; - if (!eCAL::Util::GetServiceTypeNames(service_name, method_name, req_type, resp_type)) + if (!eCAL::Registration::GetServiceTypeNames(service_name, method_name, req_type, resp_type)) { throw std::runtime_error("Could not get service type names !"); } @@ -57,7 +57,7 @@ int main(int argc, char **argv) // get service method type descriptions std::string req_desc; std::string resp_desc; - if (!eCAL::Util::GetServiceDescription(service_name, method_name, req_desc, resp_desc)) + if (!eCAL::Registration::GetServiceDescription(service_name, method_name, req_desc, resp_desc)) { throw std::runtime_error("Could not get service type descriptions !"); } diff --git a/ecal/tests/CMakeLists.txt b/ecal/tests/CMakeLists.txt index 5e2fc66b9f..81bc6ea624 100644 --- a/ecal/tests/CMakeLists.txt +++ b/ecal/tests/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,14 +32,17 @@ add_subdirectory(cpp/descgate_test) add_subdirectory(cpp/config_test) + if(ECAL_CORE_REGISTRATION) - add_subdirectory(cpp/util_test) + add_subdirectory(cpp/registration_test) + add_subdirectory(cpp/registration_test_public) endif() add_subdirectory(cpp/event_test) add_subdirectory(cpp/expmap_test) add_subdirectory(cpp/serialization_test) add_subdirectory(cpp/topic2mcast_test) +add_subdirectory(cpp/util_test) if(ECAL_CORE_REGISTRATION_SHM OR ECAL_CORE_TRANSPORT_SHM) add_subdirectory(cpp/io_memfile_test) diff --git a/ecal/tests/cpp/clientserver_proto_test/src/clientserver_test_proto.cpp b/ecal/tests/cpp/clientserver_proto_test/src/clientserver_test_proto.cpp index 83a9f5ad27..d6c702419f 100644 --- a/ecal/tests/cpp/clientserver_proto_test/src/clientserver_test_proto.cpp +++ b/ecal/tests/cpp/clientserver_proto_test/src/clientserver_test_proto.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,6 +71,9 @@ TEST(core_cpp_clientserver_proto, ProtoCallback) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver proto callback test"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create MathService server std::shared_ptr math_service_impl = std::make_shared(); eCAL::protobuf::CServiceServer math_server(math_service_impl); @@ -160,6 +163,9 @@ TEST(core_cpp_clientserver_proto, ProtoBlocking) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver proto blocking test"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create PingService server std::shared_ptr ping_service_impl = std::make_shared(); eCAL::protobuf::CServiceServer ping_server(ping_service_impl); diff --git a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp index 049995cb07..e930233680 100644 --- a/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp +++ b/ecal/tests/cpp/clientserver_test/src/clientserver_test.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,6 +89,9 @@ TEST(core_cpp_clientserver, ClientConnectEvent) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base connect event callback"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create client eCAL::CServiceClient client("service"); @@ -158,6 +161,9 @@ TEST(core_cpp_clientserver, ServerConnectEvent) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base connect event callback"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create server eCAL::CServiceServer server("service"); @@ -231,6 +237,9 @@ TEST(core_cpp_clientserver, BaseCallback) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base callback test"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create service servers ServiceVecT service_vec; for (auto s = 0; s < num_services; ++s) @@ -345,6 +354,9 @@ TEST(core_cpp_clientserver, BaseCallbackTimeout) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base callback test with timeout"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create service servers ServiceVecT service_vec; for (auto s = 0; s < num_services; ++s) @@ -513,6 +525,9 @@ TEST(core_cpp_clientserver, BaseAsyncCallback) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base async callback test"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create service server eCAL::CServiceServer server("service"); @@ -586,6 +601,9 @@ TEST(core_cpp_clientserver, BaseAsync) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base async callback test with timeout"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create service server eCAL::CServiceServer server("service"); @@ -696,6 +714,9 @@ TEST(core_cpp_clientserver, BaseBlocking) // initialize eCAL API eCAL::Initialize(0, nullptr, "clientserver base blocking test"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create service servers ServiceVecT service_vec; for (auto s = 0; s < num_services; ++s) @@ -797,6 +818,9 @@ TEST(core_cpp_clientserver, NestedRPCCall) // initialize eCAL API eCAL::Initialize(0, nullptr, "nested rpc call test"); + // enable loop back communication in the same thread + eCAL::Util::EnableLoopback(true); + // create service server eCAL::CServiceServer server("service"); diff --git a/ecal/tests/cpp/config_test/CMakeLists.txt b/ecal/tests/cpp/config_test/CMakeLists.txt index 3eb4cde46c..f6fa9b496d 100644 --- a/ecal/tests/cpp/config_test/CMakeLists.txt +++ b/ecal/tests/cpp/config_test/CMakeLists.txt @@ -22,17 +22,32 @@ find_package(Threads REQUIRED) find_package(GTest REQUIRED) find_package(tclap REQUIRED) +if(ECAL_CORE_CONFIGURATION) + find_package(yaml-cpp REQUIRED) + include(${ECAL_PROJECT_ROOT}/thirdparty/yaml-cpp/compatibility-yaml-cpp.cmake) + yaml_cpp_create_compatibility_targets() +endif() + set(cmd_parser_src ${ECAL_CORE_PROJECT_ROOT}/core/src/config/ecal_cmd_parser.cpp - ${ECAL_PROJECT_ROOT}/lib/ecal_utils/src/filesystem.cpp # Ideally we should reference relatively to ECAL_CORE_PROJECT_ROOT + ${ECAL_CORE_PROJECT_ROOT}/core/src/util/advanced_tclap_output.cpp + ${ECAL_PROJECT_ROOT}/lib/ecal_utils/src/filesystem.cpp ${ECAL_PROJECT_ROOT}/lib/ecal_utils/src/str_convert.cpp ) set(config_test_src src/config_test.cpp ${cmd_parser_src} + ${ECAL_CORE_PROJECT_ROOT}/core/src/config/default_configuration.cpp ) +if(ECAL_CORE_CONFIGURATION) + list(APPEND config_test_src + ${ECAL_CORE_PROJECT_ROOT}/core/src/config/configuration_reader.cpp + ${ECAL_CORE_PROJECT_ROOT}/core/src/config/configuration_to_yaml.cpp + ) +endif() + ecal_add_gtest(${PROJECT_NAME} ${config_test_src}) target_include_directories(${PROJECT_NAME} PRIVATE @@ -46,9 +61,13 @@ target_link_libraries(${PROJECT_NAME} eCAL::core Threads::Threads tclap::tclap - CustomTclap ) +if(ECAL_CORE_CONFIGURATION) + target_link_libraries(${PROJECT_NAME} PRIVATE yaml-cpp) + target_compile_definitions(${PROJECT_NAME} PRIVATE ECAL_CORE_CONFIGURATION) +endif() + target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) target_compile_definitions(${PROJECT_NAME} PRIVATE ECAL_CORE_COMMAND_LINE) diff --git a/ecal/tests/cpp/config_test/src/config_test.cpp b/ecal/tests/cpp/config_test/src/config_test.cpp index 89d861dfda..da90f6b30a 100644 --- a/ecal/tests/cpp/config_test/src/config_test.cpp +++ b/ecal/tests/cpp/config_test/src/config_test.cpp @@ -29,6 +29,11 @@ #include #include "ecal_cmd_parser.h" +#ifdef ECAL_CORE_CONFIGURATION + #include "configuration_reader.h" +#endif +#include "default_configuration.h" +#include "ecal_def.h" template void SetValue(MEMBER& member, VALUE value) @@ -36,264 +41,290 @@ void SetValue(MEMBER& member, VALUE value) member = value; } -TEST(core_cpp_config, user_config_passing) +TEST(core_cpp_config /*unused*/, user_config_passing /*unused*/) { - eCAL::Configuration custom_config(0, nullptr); + // Registration options + const unsigned int registration_timeout = 80000U; + const unsigned int registration_refresh = 2000U; - // Test value assignments from each category - // How the user would utilize it - // Transport layer options - const bool network_enabled = true; - std::string ip_address = "238.200.100.2"; - const int upd_snd_buff = (5242880 + 1024); + const bool drop_out_of_order_messages = true; + std::string ip_address = "238.200.100.2"; + const int upd_snd_buff = (5242880 + 1024); // Monitoring options - const unsigned int mon_timeout = 6000U; - const std::string mon_filter_excl = "_A.*"; - const eCAL_Logging_Filter mon_log_filter_con = log_level_warning; - const eCAL::Monitoring::Types::Mode monitoring_mode = eCAL::Monitoring::Types::Mode::udp_monitoring; + const std::string mon_filter_excl = "_A.*"; + const eCAL_Logging_Filter mon_log_filter_con = log_level_warning; // Publisher options - const bool pub_use_shm = true; + const bool pub_use_shm = false; - // Registration options - const unsigned int registration_timeout = 80000U; - const unsigned int registration_refresh = 2000U; - const eCAL::Registration::Configuration registration = eCAL::Registration::Configuration(registration_timeout, registration_refresh); - - try{ - custom_config.transport_layer.network_enabled = network_enabled; - custom_config.transport_layer.mc_options.group = ip_address; - custom_config.transport_layer.mc_options.sndbuf = upd_snd_buff; + eCAL::Configuration custom_config(0, nullptr); + try + { + custom_config.subscriber.drop_out_of_order_messages = drop_out_of_order_messages; + custom_config.transport_layer.udp.network.group = ip_address; + custom_config.transport_layer.udp.send_buffer = upd_snd_buff; - custom_config.monitoring.monitoring_timeout = mon_timeout; - custom_config.monitoring.filter_excl = mon_filter_excl; - custom_config.monitoring.monitoring_mode = monitoring_mode; - custom_config.logging.filter_log_con = mon_log_filter_con; + custom_config.monitoring.filter_excl = mon_filter_excl; + custom_config.logging.sinks.console.filter_log_con = mon_log_filter_con; - custom_config.publisher.shm.enable = pub_use_shm; + custom_config.publisher.layer.shm.enable = pub_use_shm; - custom_config.registration = registration; + custom_config.registration.registration_refresh = registration_refresh; + custom_config.registration.registration_timeout = registration_timeout; } catch (std::invalid_argument& e) { - throw std::runtime_error("Error while configuring Configuration: " + std::string(e.what())); + FAIL() << "Error while configuring Configuration: " << std::string(e.what()); + return; } // Initialize ecal api with custom config EXPECT_EQ(0, eCAL::Initialize(custom_config, "User Config Passing Test", eCAL::Init::Default)); // Test boolean assignment, default is false - EXPECT_EQ(network_enabled, eCAL::GetConfiguration().transport_layer.network_enabled); + EXPECT_EQ(drop_out_of_order_messages, eCAL::GetConfiguration().subscriber.drop_out_of_order_messages); // Test IP address assignment, default is 239.0.0.1 - EXPECT_EQ(ip_address, static_cast(eCAL::GetConfiguration().transport_layer.mc_options.group)); + EXPECT_EQ(ip_address, eCAL::GetConfiguration().transport_layer.udp.network.group); // Test UDP send buffer assignment, default is 5242880 - EXPECT_EQ(upd_snd_buff, static_cast(eCAL::GetConfiguration().transport_layer.mc_options.sndbuf)); - - // Test monitoring timeout assignment, default is 5000U - EXPECT_EQ(mon_timeout, eCAL::GetConfiguration().monitoring.monitoring_timeout); + EXPECT_EQ(upd_snd_buff, eCAL::GetConfiguration().transport_layer.udp.send_buffer); // Test monitoring filter exclude assignment, default is "_.*" EXPECT_EQ(mon_filter_excl, eCAL::GetConfiguration().monitoring.filter_excl); // Test monitoring console log assignment, default is (log_level_info | log_level_warning | log_level_error | log_level_fatal) - EXPECT_EQ(mon_log_filter_con, eCAL::GetConfiguration().logging.filter_log_con); - - // Test monitoring mode assignment, default is eCAL::Types::MonitoringMode::none - EXPECT_EQ(monitoring_mode, eCAL::GetConfiguration().monitoring.monitoring_mode); + EXPECT_EQ(mon_log_filter_con, eCAL::GetConfiguration().logging.sinks.console.filter_log_con); // Test publisher sendmode assignment, default is eCAL::TLayer::eSendMode::smode_auto - EXPECT_EQ(pub_use_shm, eCAL::GetConfiguration().publisher.shm.enable); + EXPECT_EQ(pub_use_shm, eCAL::GetConfiguration().publisher.layer.shm.enable); - // Test registration option assignment, default timeout is 60000U and default refresh is 1000U - EXPECT_EQ(registration_timeout, eCAL::GetConfiguration().registration.getTimeoutMS()); - EXPECT_EQ(registration_refresh, eCAL::GetConfiguration().registration.getRefreshMS()); + // Test registration option assignment, default timeout is 10000U and default refresh is 1000U + EXPECT_EQ(registration_timeout, eCAL::GetConfiguration().registration.registration_timeout); + EXPECT_EQ(registration_refresh, eCAL::GetConfiguration().registration.registration_refresh); // Finalize eCAL API EXPECT_EQ(0, eCAL::Finalize()); } -TEST(ConfigDeathTest, user_config_death_test) +TEST(ConfigDeathTest /*unused*/, user_config_death_test /*unused*/) { eCAL::Configuration custom_config(0, nullptr); // Test the IpAddressV4 class with wrong values ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("42")), + SetValue(custom_config.transport_layer.udp.network.group, "42"), std::invalid_argument); // Test the IpAddressV4 class with invalid addresses ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("256.0.0.0")), + SetValue(custom_config.transport_layer.udp.network.group, "256.0.0.0"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("127.0.0.1")), + SetValue(custom_config.transport_layer.udp.network.group, "127.0.0.1"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("255.255.255.255")), + SetValue(custom_config.transport_layer.udp.network.group, "255.255.255.255"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("FFF.FF.FF.FF")), + SetValue(custom_config.transport_layer.udp.network.group, "FFF.FF.FF.FF"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("FF.FF.FF.FF")), + SetValue(custom_config.transport_layer.udp.network.group, "FF.FF.FF.FF"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("Ff.fF.ff.Ff")), + SetValue(custom_config.transport_layer.udp.network.group, "Ff.fF.ff.Ff"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("7f.0.0.1")), + SetValue(custom_config.transport_layer.udp.network.group, "7f.0.0.1"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("0.0.0.0")), + SetValue(custom_config.transport_layer.udp.network.group, "0.0.0.0"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("00.00.00.00")), + SetValue(custom_config.transport_layer.udp.network.group, "00.00.00.00"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("000.000.000.000")), + SetValue(custom_config.transport_layer.udp.network.group, "000.000.000.000"), std::invalid_argument); ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.group, std::string("0.00.000.0")), + SetValue(custom_config.transport_layer.udp.network.group, "0.00.000.0"), std::invalid_argument); // Test the ConstrainedInteger class with wrong values. Default are MIN = 5242880, STEP = 1024 // Value below MIN ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.sndbuf, 42), + SetValue(custom_config.transport_layer.udp.send_buffer, 42), std::invalid_argument); // Wrong step. Default STEP = 1024 ASSERT_THROW( - SetValue(custom_config.transport_layer.mc_options.sndbuf, (5242880 + 512)), + SetValue(custom_config.transport_layer.udp.send_buffer, (5242880 + 512)), std::invalid_argument); - // Value exceeds MAX. Default MAX = 100 - ASSERT_THROW( - SetValue(custom_config.transport_layer.shm_options.memfile_reserve, 150), - std::invalid_argument); - - // Test the registration option limits - // Refresh timeout > registration timeout - ASSERT_THROW( - eCAL::Registration::Configuration(2000U, 3000U), std::invalid_argument); - - // Refresh timeout = registration timeout - ASSERT_THROW( - eCAL::Registration::Configuration(2000U, 2000U), std::invalid_argument); } -TEST(core_cpp_config, config_custom_datatypes_tests) +TEST(core_cpp_config /*unused*/, config_custom_datatypes_tests /*unused*/) { // test custom datatype assignment operators - eCAL::Types::IpAddressV4 ip1; - eCAL::Types::IpAddressV4 ip2; - EXPECT_EQ(static_cast(ip1), static_cast(ip2)); + eCAL::Types::IpAddressV4 ip1 { "192.168.0.1" }; + eCAL::Types::IpAddressV4 ip2 { "192.168.0.1" }; + EXPECT_EQ(ip1, ip2); ip1 = "192.168.0.2"; ip2 = ip1; - EXPECT_EQ(static_cast(ip1), static_cast(ip2)); + EXPECT_EQ(ip1, ip2); eCAL::Types::ConstrainedInteger<0,1,10> s1; eCAL::Types::ConstrainedInteger<0,1,10> s2; - EXPECT_EQ(static_cast(s1), static_cast(s2)); + EXPECT_EQ(s1, s2); s1 = 5; s2 = s1; - EXPECT_EQ(static_cast(s1), static_cast(s2)); + EXPECT_EQ(s1, s2); // test copy method for config structure eCAL::Configuration config1(0, nullptr); eCAL::Configuration config2(0, nullptr); - std::string testValue = std::string("234.0.3.2"); - config2.transport_layer.mc_options.group = testValue; + std::string testValue = "234.0.3.2"; + config2.transport_layer.udp.network.group = testValue; auto& config2ref = config2; config1 = config2ref; - EXPECT_EQ(static_cast(config1.transport_layer.mc_options.group), testValue); + EXPECT_EQ(config1.transport_layer.udp.network.group, testValue); } -TEST(core_cpp_config, config_cmd_parser) +TEST(CmdParserTest /*unused*/, config_cmd_parser_test /*unused*/) +{ + const std::string some_file_name = "someFileName.yml"; + + eCAL::Config::CmdParser parser{}; + + EXPECT_EQ(parser.getUserIni(), ""); + EXPECT_EQ(parser.getDumpConfig(), false); + + std::vector arguments{}; + + arguments.push_back("test_config_cmd_parser_test"); + // set a file name as ini file + arguments.push_back("--ecal-config-file " + some_file_name); + // set the dump config flag + arguments.push_back("--ecal-dump-config"); + + parser.parseArguments(arguments); + + EXPECT_EQ(parser.getUserIni(), some_file_name); + EXPECT_EQ(parser.getDumpConfig(), true); +} + +#ifdef ECAL_CORE_CONFIGURATION +TEST(YamlConfigReaderTest /*unused*/, read_write_file_test /*unused*/) { // create a custom ini file - std::string ini_file_name = "customIni.ini"; + std::string ini_file_name = "customIni.yml"; std::ofstream custom_ini_file(ini_file_name); if (custom_ini_file.is_open()) { - custom_ini_file << ini_file_as_string; + custom_ini_file << ini_file_as_string_yaml; custom_ini_file.close(); } else { std::cerr << "Error opening file for ini writing" << "\n"; + FAIL() << "Error opening file for ini writing"; return; } - eCAL::Config::CmdParser parser; + eCAL::Configuration config{}; + EXPECT_NO_THROW(eCAL::Config::YamlFileToConfig(ini_file_name, config)); + + EXPECT_EQ(true, eCAL::Config::ConfigToYamlFile("myTest.yml", config)); + + remove(ini_file_name.data()); + remove("myTest.yml"); +} + +TEST(YamlConfigReaderTest /*unused*/, parse_values_test /*unused*/) +{ + eCAL::Configuration config{}; + EXPECT_NO_THROW(eCAL::Config::YamlStringToConfig(ini_file_as_string_yaml, config)); + + // Check string + EXPECT_EQ(config.application.startup.terminal_emulator, "myTestTerminal"); + + // Check equality of IpAddressV4 + EXPECT_EQ(config.transport_layer.udp.network.group, "239.5.0.1"); + + // Check constrained Integer + EXPECT_EQ(config.transport_layer.udp.port, 14010); - std::vector arguments; + // Check boolean + EXPECT_EQ(config.transport_layer.udp.npcap_enabled, true); + + // Check unsigned size_t + EXPECT_EQ(config.transport_layer.tcp.max_reconnections, 7); + + // Check unsigned int + EXPECT_EQ(config.publisher.layer.shm.acknowledge_timeout_ms, 346U); +} + +TEST(YamlConfigReaderTest /*unused*/, yaml_node_merger /*unused*/) +{ + YAML::Node node_1{}; + YAML::Node node_2{}; - const std::string set_config_key = "--ecal-set-config-key "; - const std::string sep_slash = "/"; - const std::string sep_col = ":"; + node_1["test"] = 1; + node_2["test"] = 2; + node_2[3] = "I have an int key!"; - const std::string network = "network"; - const std::string host_group_name = "host_group_name"; - const std::string config_test_machine = "ConfigTestMachine"; - const std::string network_enabled = "network_enabled"; - const std::string is_network_enabled = "true"; + node_1["test2"] = 3; - const std::string common = "common"; - const std::string registration_timeout = "registration_timeout"; - const std::string registration_refresh = "registration_refresh"; - const std::string reg_to_value = "6000"; - const std::string reg_rf_value = "1000"; - - arguments.push_back("test_config_cmd_parser"); - arguments.push_back("--ecal-ini-file customIni.ini"); - arguments.push_back(set_config_key + network + sep_slash + host_group_name + sep_col + config_test_machine); - arguments.push_back(set_config_key + network + sep_slash + network_enabled + sep_col + is_network_enabled); - arguments.push_back(set_config_key + common + sep_slash + registration_timeout + sep_col + reg_to_value); - arguments.push_back(set_config_key + common + sep_slash + registration_refresh + sep_col + reg_rf_value); + node_1["firstLayer1"]["secondLayer1"] = "192.168.0.2"; + node_2["firstLayer1"]["secondLayer1"] = "192.168.0.5"; - try + // try also with a sequence + node_2["firstLayer2"]["secondLayer2"] = YAML::Load("[1, 2, 3]"); + + eCAL::Config::MergeYamlNodes(node_1, node_2); + + EXPECT_EQ(node_1["test"], node_2["test"]); + EXPECT_EQ(node_1[3], node_2[3]); + EXPECT_EQ(node_1["3"], node_2["3"]); + EXPECT_EQ(node_1["firstLayer1"]["secondLayer1"], node_2["firstLayer1"]["secondLayer1"]); + EXPECT_EQ(node_1["firstLayer2"]["secondLayer2"], node_2["firstLayer2"]["secondLayer2"]); +} + +TEST(YamlConfigReaderTest /*unused*/, yaml_to_config_merger /*unused*/) +{ + // create a custom ini file + std::string ini_file_name = "customIni.yml"; + std::ofstream custom_ini_file(ini_file_name); + + if (custom_ini_file.is_open()) { - parser.parseArguments(arguments); + custom_ini_file << ini_file_as_string_yaml; + custom_ini_file.close(); } - catch(const std::runtime_error& e) + else { - std::cerr << e.what() << '\n'; + std::cerr << "Error opening file for ini writing" << "\n"; + FAIL() << "Error opening file for ini writing"; + return; } - - // Expect a valid ini file - EXPECT_NE(parser.getUserIni(), std::string("")); - // Expect a proper key-value map in the config key map - EXPECT_EQ(parser.getConfigKeysMap()[network][host_group_name], config_test_machine); - EXPECT_EQ(parser.getConfigKeysMap()[network][network_enabled], is_network_enabled); - EXPECT_EQ(parser.getConfigKeysMap()[common][registration_timeout], reg_to_value); - EXPECT_EQ(parser.getConfigKeysMap()[common][registration_refresh], reg_rf_value); + eCAL::Configuration config{}; - remove(ini_file_name.data()); -} - -TEST(CmdParserDeathTest, config_cmd_parser_death_test) -{ - eCAL::Config::CmdParser parser; + EXPECT_TRUE(config.publisher.layer.shm.enable); - std::vector arguments; + eCAL::Config::MergeYamlIntoConfiguration(ini_file_name, config); - arguments.push_back("test_config_cmd_parser_death_test"); - arguments.push_back("--ecal-ini-file someNotValidFileName.ini"); + EXPECT_FALSE(config.publisher.layer.shm.enable); - ASSERT_THROW( - parser.parseArguments(arguments), - std::runtime_error - ); -} \ No newline at end of file + remove(ini_file_name.data()); +} +#endif \ No newline at end of file diff --git a/ecal/tests/cpp/config_test/src/ini_file.h b/ecal/tests/cpp/config_test/src/ini_file.h index fafd498d88..bb3f27a78c 100644 --- a/ecal/tests/cpp/config_test/src/ini_file.h +++ b/ecal/tests/cpp/config_test/src/ini_file.h @@ -1,194 +1,228 @@ #include -static const std::string ini_file_as_string = -"; --------------------------------------------------\n" -"; NETWORK SETTINGS\n" -"; --------------------------------------------------\n" -"; network_enabled = true / false true = all eCAL components communicate over network boundaries\n" -"; false = local host only communication\n" -";\n" -"; multicast_config_version = v1 / v2 UDP configuration version (Since eCAL 5.12.)\n" -"; v1: default behavior\n" -"; v2: new behavior, comes with a bit more intuitive handling regarding masking of the groups\n" -"; multicast_group = 239.0.0.1 UDP multicast group base\n" -"; All registration and logging is sent on this address\n" -"; multicast_mask = 0.0.0.1-0.0.0.255 v1: Mask maximum number of dynamic multicast group\n" -"; 255.0.0.0-255.255.255.255 v2: masks are now considered like routes masking\n" -";\n" -"; multicast_port = 14000 + x UDP multicast port number (eCAL will use at least the 2 following port\n" -"; numbers too, so please modify in steps of 10 (e.g. 1010, 1020 ...)\n" -";\n" -"; multicast_ttl = 0 + x UDP ttl value, also known as hop limit, is used in determining \n" -"; the intermediate routers being traversed towards the destination\n" -";\n" -"; multicast_sndbuf = 1024 * x UDP send buffer in bytes\n" -"; \n" -"; multicast_rcvbuf = 1024 * x UDP receive buffer in bytes\n" -";\n" -"; multicast_join_all_if = false Linux specific setting to enable joining multicast groups on all network interfacs\n" -"; independent of their link state. Enabling this makes sure that eCAL processes\n" -"; receive data if they are started before network devices are up and running.\n" -"; \n" -"; shm_rec_enabled = true Enable to receive on eCAL shared memory layer\n" -"; tcp_rec_enabled = true Enable to receive on eCAL tcp layer\n" -"; udp_mc_rec_enabled = true Enable to receive on eCAL udp multicast layer\n" -";\n" -"; npcap_enabled = false Enable to receive UDP traffic with the Npcap based receiver\n" -";\n" -"; tcp_pubsub_num_executor_reader = 4 Tcp_pubsub reader amount of threads that shall execute workload\n" -"; tcp_pubsub_num_executor_writer = 4 Tcp_pubsub writer amount of threads that shall execute workload\n" -"; tcp_pubsub_max_reconnections = 5 Tcp_pubsub reconnection attemps the session will try to reconnect in \n" -"; case of an issue (a negative value means infinite reconnection attemps)\n" -";\n" -"; host_group_name = Common host group name that enables interprocess mechanisms across \n" -"; (virtual) host borders (e.g, Docker); by default equivalent to local host name\n" -"; --------------------------------------------------\n" -"\n" -"[network]\n" -"network_enabled = false\n" -"multicast_config_version = v1\n" -"multicast_group = 239.0.0.1\n" -"multicast_mask = 0.0.0.15\n" -"multicast_port = 14000\n" -"multicast_ttl = 2\n" -"multicast_sndbuf = 5242880\n" -"multicast_rcvbuf = 5242880\n" -"\n" -"multicast_join_all_if = false\n" -"\n" -"shm_rec_enabled = true\n" -"tcp_rec_enabled = true\n" -"udp_mc_rec_enabled = true\n" -"\n" -"npcap_enabled = false\n" -"\n" -"tcp_pubsub_num_executor_reader = 4\n" -"tcp_pubsub_num_executor_writer = 4\n" -"tcp_pubsub_max_reconnections = 5\n" -"\n" -"host_group_name =\n" -"\n" -"; --------------------------------------------------\n" -"; COMMON SETTINGS\n" -"; --------------------------------------------------\n" -"; registration_timeout = 60000 Timeout for topic registration in ms (internal)\n" -"; registration_refresh = 1000 Topic registration refresh cylce (has to be smaller then registration timeout !)\n" -"\n" -"; --------------------------------------------------\n" -"[common]\n" -"registration_timeout = 60000\n" -"registration_refresh = 1000\n" -"\n" -"; --------------------------------------------------\n" -"; TIME SETTINGS\n" -"; --------------------------------------------------\n" -"; timesync_module_rt = ecaltime-localtime Time synchronisation interface name (dynamic library)\n" -"; The name will be extended with platform suffix (32|64), debug suffix (d) and platform extension (.dll|.so)\n" -";\n" -"; Available modules are:\n" -"; - ecaltime-localtime local system time without synchronization \n" -"; - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux\n" -"; (device configuration in ecaltime.ini)\n" -"; --------------------------------------------------\n" -"[time]\n" -"timesync_module_rt = ecaltime-localtime\n" -"\n" -"; ---------------------------------------------\n" -"; PROCESS SETTINGS\n" -"; ---------------------------------------------\n" -";\n" -"; terminal_emulator = /usr/bin/x-terminal-emulator -e command for starting applications with an external terminal emulator. If empty, the command will be ignored. Ignored on Windows.\n" -"; e.g. /usr/bin/x-terminal-emulator -e\n" -"; /usr/bin/gnome-terminal -x\n" -"; /usr/bin/xterm -e\n" -";\n" -"; ---------------------------------------------\n" -"[process]\n" -"terminal_emulator = \n" -"\n" -"; --------------------------------------------------\n" -"; PUBLISHER SETTINGS\n" -"; --------------------------------------------------\n" -"; use_shm = 0, 1, 2 Use shared memory transport layer (0 = off, 1 = on, 2 = auto, default = 2)\n" -"; use_tcp = 0, 1, 2 Use tcp transport layer (0 = off, 1 = on, 2 = auto, default = 0)\n" -"; use_udp_mc = 0, 1, 2 Use udp multicast transport layer (0 = off, 1 = on, 2 = auto, default = 2)\n" -";\n" -"; memfile_minsize = x * 4096 kB Default memory file size for new publisher\n" -";\n" -"; memfile_reserve = 50 .. x % Dynamic file size reserve before recreating memory file if topic size changes\n" -";\n" -"; memfile_ack_timeout = 0 .. x ms Publisher timeout for ack event from subscriber that memory file content is processed\n" -";\n" -"; memfile_buffer_count = 1 .. x Number of parallel used memory file buffers for 1:n publish/subscribe ipc connections (default = 1)\n" -"; memfile_zero_copy = 0, 1 Allow matching subscriber to access memory file without copying its content in advance (blocking mode)\n" -";\n" -"; share_ttype = 0, 1 Share topic type via registration layer\n" -"; share_tdesc = 0, 1 Share topic description via registration layer (switch off to disable reflection)\n" -"; --------------------------------------------------\n" -"[publisher]\n" -"use_shm = 2\n" -"use_tcp = 0\n" -"use_udp_mc = 2\n" -"\n" -"memfile_minsize = 4096\n" -"memfile_reserve = 50\n" -"memfile_ack_timeout = 0\n" -"memfile_buffer_count = 1\n" -"memfile_zero_copy = 0\n" -"\n" -"share_ttype = 1\n" -"share_tdesc = 1\n" -"\n" -"; --------------------------------------------------\n" -"; SERVICE SETTINGS\n" -"; --------------------------------------------------\n" -"; protocol_v0 = 0, 1 Support service protocol v0, eCAL 5.11 and older (0 = off, 1 = on)\n" -"; protocol_v1 = 0, 1 Support service protocol v1, eCAL 5.12 and newer (0 = off, 1 = on)\n" -"; --------------------------------------------------\n" -"[service]\n" -"protocol_v0 = 1\n" -"protocol_v1 = 1\n" -"\n" -"; --------------------------------------------------\n" -"; MONITORING SETTINGS\n" -"; --------------------------------------------------\n" -"; timeout = 1000 + (x * 1000) Timeout for topic monitoring in ms\n" -"; filter_excl = __.* Topics blacklist as regular expression (will not be monitored)\n" -"; filter_incl = Topics whitelist as regular expression (will be monitored only)\n" -"; filter_log_con = info, warning, error, fatal Log messages logged to console (all, info, warning, error, fatal, debug1, debug2, debug3, debug4)\n" -"; filter_log_file = Log messages to logged into file system\n" -"; filter_log_udp = info, warning, error, fatal Log messages logged via udp network\n" -"; --------------------------------------------------\n" -"[monitoring]\n" -"timeout = 5000\n" -"filter_excl = __.*\n" -"filter_incl =\n" -"filter_log_con = info, warning, error, fatal\n" -"filter_log_file =\n" -"filter_log_udp = info, warning, error, fatal\n" -"\n" -"; --------------------------------------------------\n" -"; SYS SETTINGS\n" -"; --------------------------------------------------\n" -"; filter_excl = App1,App2 Apps blacklist to be excluded when importing tasks from cloud\n" -"; --------------------------------------------------\n" -"[sys]\n" -"filter_excl = ^eCALSysClient$|^eCALSysGUI$|^eCALSys$\n" -"\n" -"; --------------------------------------------------\n" -"; EXPERIMENTAL SETTINGS\n" -"; --------------------------------------------------\n" -"; shm_monitoring_enabled = false Enable distribution of monitoring/registration information via shared memory\n" -"; shm_monitoring_domain = ecal_monitoring Domain name for shared memory based monitoring/registration\n" -"; shm_monitoring_queue_size = 1024 Queue size of monitoring/registration events\n" -"; network_monitoring_enabled = true Enable distribution of monitoring/registration information via network\n" -";\n" -"; drop_out_of_order_messages = false Enable dropping of payload messages that arrive out of order\n" -"; --------------------------------------------------\n" -"[experimental]\n" -"shm_monitoring_enabled = false\n" -"shm_monitoring_domain = ecal_mon\n" -"shm_monitoring_queue_size = 1024\n" -"network_monitoring_enabled = true\n" -"drop_out_of_order_messages = false\n" -; \ No newline at end of file +const std::string ini_file_as_string_yaml = R"(# _____ _ _ ____ _ _ +# | ____|___| (_)_ __ ___ ___ ___ / ___| / \ | | +# | _| / __| | | '_ \/ __|/ _ \ _____ / _ \ | / _ \ | | +# | |__| (__| | | |_) \__ \ __/ |_____| | __/ |___ / ___ \| |___ +# |_____\___|_|_| .__/|___/\___| \___|\____/_/ \_\_____| +# |_| +# _ _ _ __ _ _ _ +# __ _| | ___ | |__ __ _| | ___ ___ _ __ / _(_) __ _ _ _ _ __ __ _| |_(_) ___ _ __ +# / _` | |/ _ \| '_ \ / _` | | / __/ _ \| '_ \| |_| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ +# | (_| | | (_) | |_) | (_| | | | (_| (_) | | | | _| | (_| | |_| | | | (_| | |_| | (_) | | | | +# \__, |_|\___/|_.__/ \__,_|_| \___\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| +# |___/ |___/ + + +# Registration layer configuration +registration: + # Topic registration refresh cylce (has to be smaller then registration timeout! Default: 1000) + registration_refresh: 1000 + # Timeout for topic registration in ms (internal, Default: 60000) + registration_timeout: 60000 + # Enable to receive registration information on the same local machine + loopback: true + # Host group name that enables interprocess mechanisms across (virtual) + # host borders (e.g, Docker); by default equivalent to local host name + host_group_name: "" + # true = all eCAL components communicate over network boundaries + # false = local host only communication (Default: false) + network_enabled: false + + layer: + shm: + enable: false + # Domain name for shared memory based registration + domain: "ecal_mon" + # Queue size of registration events + queue_size: 1024 + + udp: + enable: true + port: 14000 + + +# Monitoring configuration +monitoring: + # Timeout for topic monitoring in ms (Default: 1000), increase in 1000er steps + timeout: 1000 + # Topics blacklist as regular expression (will not be monitored) + filter_excl: "^__.*$" + # Topics whitelist as regular expression (will be monitored only) (Default: "") + filter_incl: "" + + +# Transport layer configuration +transport_layer: + udp: + # UDP configuration version (Since eCAL 5.12.) + # v1: default behavior + # v2: new behavior, comes with a bit more intuitive handling regarding masking of the groups + config_version: "v2" + # Valid modes: local, network (Default: local) + mode: "local" + # Multicast port number + port: 14010 + # v1: Mask maximum number of dynamic multicast group (range 0.0.0.1-0.0.0.255) + # v2: Masks are now considered like routes masking (range 255.0.0.0-255.255.255.255) + mask: "255.255.255.240" + # Send buffer in bytes + send_buffer: 5242880 + # Receive buffer in bytes + receive_buffer: 5242880 + # Linux specific setting to join all network interfaces independend of their link state. + # Enabling ensures that eCAL processes receive data when they are started before the + # network devices are up and running. + join_all_interfaces: false + # Windows specific setting to enable receiving UDP traffic with the Npcap based receiver + npcap_enabled: true + + # In local mode multicast group and ttl are set by default and are not adjustable + local: + # Multicast group base. All registration and logging is sent on this address + # group: "127.0.0.1" + # TTL (hop limit) is used to determine the amount of routers being traversed towards the destination + # ttl: 0 + + network: + # Multicast group base. All registration and logging is sent on this address + group: "239.5.0.1" + # TTL (hop limit) is used to determine the amount of routers being traversed towards the destination + ttl: 3 + + tcp: + # Reader amount of threads that shall execute workload + number_executor_reader: 4 + # Writer amount of threads that shall execute workload + number_executor_writer: 4 + # Reconnection attemps the session will try to reconnect in case of an issue + max_reconnections: 7 + + shm: + # Default memory file size for new publisher + memfile_min_size_bytes: 4096 + # Dynamic file size reserve before recreating memory file if topic size changes + memfile_reserve_percent: 50 + + +# Publisher specific base settings +publisher: + layer: + # Base configuration for shared memory publisher + shm: + # Enable layer + enable: false + # Enable zero copy shared memory transport mode + zero_copy_mode: false + # Force connected subscribers to send acknowledge event after processing the message. + # The publisher send call is blocked on this event with this timeout (0 == no handshake). + acknowledge_timeout_ms: 346 + # Maximum number of used buffers (needs to be greater than 1, default = 1) + memfile_buffer_count: 1 + + # Base configuration for UDP publisher + udp: + # Enable layer + enable: true + + # Base configuration for TCP publisher + tcp: + # Enable layer + enable: true + + # Share topic type via registration + share_topic_type: true + # Share topic description via registration + share_topic_description: true + # Priority list for layer usage in local mode (Default: SHM > UDP > TCP) + priority_local: ["shm", "udp", "tcp"] + # Priority list for layer usage in cloud mode (Default: UDP > TCP) + priority_network: ["udp", "tcp"] + + +# Subscriber specific base configuration +subscriber: + layer: + # Base configuration for shared memory subscriber + shm: + # Enable layer + enable: false + + # Base configuration for UDP subscriber + udp: + # Enabler layer + enable: true + + # Base configuration for TCP subscriber + tcp: + # Enable layer + enable: false + + # Enable dropping of payload messages that arrive out of order + drop_out_of_order_messages: true + + +# Time configuration +time: + # Time synchronisation interface name (dynamic library) + # The name will be extended with platform suffix (32|64), debug suffix (d) and platform extension (.dll|.so) + # Available modules are: + # - ecaltime-localtime local system time without synchronization + # - ecaltime-linuxptp For PTP / gPTP synchronization over ethernet on Linux + # (device configuration in ecaltime.ini) + rt: "ecaltime-localtime" + # Specify the module name for replaying + replay: "" + + +# Service configuration +service: + # Support service protocol v0, eCAL 5.11 and older (0 = off, 1 = on) + protocol_v0: true + # Support service protocol v1, eCAL 5.12 and newer (0 = off, 1 = on) + protocol_v1: false + + +# eCAL Application Configuration +application: + # Configuration for eCAL Sys + sys: + # Apps blacklist to be excluded when importing tasks from cloud + filter_excl: "^eCALSysClient$|^eCALSysGUI$|^eCALSys$*" + # Process specific configuration + terminal: + # Linux only command for starting applications with an external terminal emulator. + # e.g. /usr/bin/x-terminal-emulator -e + # /usr/bin/gnome-terminal -x + # /usr/bin/xterm -e + # If empty, the command will be ignored. + emulator: "myTestTerminal" + + +# Logging configuration +logging: + sinks: + # Console logging configuration + console: + # Enable console logging + enable: false + # Log level for console output + level: ["info", "warning", "error", "fatal"] + # File logging configuration + file: + # Enable file logging + enable: false + # Log level for file output + level: [] + # Log file path + path: "ecal.log" + # UDP logging configuration + udp: + # Enable UDP logging + enable: true + # Log level for UDP output + level: ["info", "warning", "error", "fatal"] + # UDP + port: 14001 + +)"; \ No newline at end of file diff --git a/ecal/tests/cpp/descgate_test/CMakeLists.txt b/ecal/tests/cpp/descgate_test/CMakeLists.txt index 284c224655..618604f280 100644 --- a/ecal/tests/cpp/descgate_test/CMakeLists.txt +++ b/ecal/tests/cpp/descgate_test/CMakeLists.txt @@ -22,7 +22,7 @@ find_package(Threads REQUIRED) find_package(GTest REQUIRED) set(descgate_test_src - src/getpublisher.cpp + src/descgate_getentities.cpp ${ECAL_CORE_PROJECT_ROOT}/core/src/ecal_descgate.cpp ) diff --git a/ecal/tests/cpp/descgate_test/src/getpublisher.cpp b/ecal/tests/cpp/descgate_test/src/descgate_getentities.cpp similarity index 52% rename from ecal/tests/cpp/descgate_test/src/getpublisher.cpp rename to ecal/tests/cpp/descgate_test/src/descgate_getentities.cpp index c631905c54..c17bf5ec4f 100644 --- a/ecal/tests/cpp/descgate_test/src/getpublisher.cpp +++ b/ecal/tests/cpp/descgate_test/src/descgate_getentities.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,32 +32,46 @@ namespace { eCAL::Registration::Sample reg_sample; reg_sample.cmd_type = eCAL::bct_reg_publisher; + reg_sample.identifier.entity_id = std::to_string(topic_id_); reg_sample.topic.tname = topic_name_; - reg_sample.topic.tid = std::to_string(topic_id_); reg_sample.topic.tdatatype.name = topic_name_ + "-tdatatype.name"; reg_sample.topic.tdatatype.encoding = topic_name_ + "-tdatatype.encoding"; reg_sample.topic.tdatatype.descriptor = topic_name_ + "-tdatatype.descriptor"; return reg_sample; } + eCAL::Registration::Sample DestroyPublisher(const std::string& topic_name_, std::uint64_t topic_id_) + { + eCAL::Registration::Sample reg_sample = CreatePublisher(topic_name_, topic_id_); + reg_sample.cmd_type = eCAL::bct_unreg_publisher; + return reg_sample; + } + eCAL::Registration::Sample CreateSubscriber(const std::string& topic_name_, std::uint64_t topic_id_) { eCAL::Registration::Sample reg_sample; reg_sample.cmd_type = eCAL::bct_reg_subscriber; + reg_sample.identifier.entity_id = std::to_string(topic_id_); reg_sample.topic.tname = topic_name_; - reg_sample.topic.tid = std::to_string(topic_id_); reg_sample.topic.tdatatype.name = topic_name_ + "-tdatatype.name"; reg_sample.topic.tdatatype.encoding = topic_name_ + "-tdatatype.encoding"; reg_sample.topic.tdatatype.descriptor = topic_name_ + "-tdatatype.descriptor"; return reg_sample; } + eCAL::Registration::Sample DestroySubscriber(const std::string& topic_name_, std::uint64_t topic_id_) + { + eCAL::Registration::Sample reg_sample = CreateSubscriber(topic_name_, topic_id_); + reg_sample.cmd_type = eCAL::bct_unreg_subscriber; + return reg_sample; + } + eCAL::Registration::Sample CreateService(const std::string& service_name_, std::uint64_t service_id_) { eCAL::Registration::Sample reg_sample; - reg_sample.cmd_type = eCAL::bct_reg_service; - reg_sample.service.sname = service_name_; - reg_sample.service.sid = std::to_string(service_id_); + reg_sample.cmd_type = eCAL::bct_reg_service; + reg_sample.service.sname = service_name_; + reg_sample.identifier.entity_id = std::to_string(service_id_); eCAL::Service::Method method; method.mname = "method_name"; @@ -65,44 +79,56 @@ namespace return reg_sample; } + eCAL::Registration::Sample DestroyService(const std::string& service_name_, std::uint64_t service_id_) + { + eCAL::Registration::Sample reg_sample = CreateService(service_name_, service_id_); + reg_sample.cmd_type = eCAL::bct_unreg_service; + return reg_sample; + } + eCAL::Registration::Sample CreateClient(const std::string& client_name_, std::uint64_t service_id_) { eCAL::Registration::Sample reg_sample; - reg_sample.cmd_type = eCAL::bct_reg_client; - reg_sample.client.sname = client_name_; - reg_sample.client.sid = std::to_string(service_id_); + reg_sample.cmd_type = eCAL::bct_reg_client; + reg_sample.client.sname = client_name_; + reg_sample.identifier.entity_id = std::to_string(service_id_); eCAL::Service::Method method; method.mname = "method_name"; reg_sample.client.methods.push_back(method); return reg_sample; } + + eCAL::Registration::Sample DestroyClient(const std::string& client_name_, std::uint64_t service_id_) + { + eCAL::Registration::Sample reg_sample = CreateClient(client_name_, service_id_); + reg_sample.cmd_type = eCAL::bct_unreg_client; + return reg_sample; + } } TEST(core_cpp_descgate, PublisherExpiration) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; // apply sample 5 times, sample should not expire auto runs(5); while ((runs--) != 0) { desc_gate.ApplySample(CreatePublisher("pub1", 1), eCAL::tl_none); - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS / 2)); - - EXPECT_EQ(1, desc_gate.GetPublishers().size()); + EXPECT_EQ(1, desc_gate.GetPublisherIDs().size()); } // now let the sample expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + desc_gate.ApplySample(DestroyPublisher("pub1", 1), eCAL::tl_none); // sample should be expired - EXPECT_EQ(0, desc_gate.GetPublishers().size()); + EXPECT_EQ(0, desc_gate.GetPublisherIDs().size()); } TEST(core_cpp_descgate, PublisherQualities) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; // create and apply publisher pub1 desc_gate.ApplySample(CreatePublisher("pub1", 1), eCAL::tl_none); @@ -110,46 +136,38 @@ TEST(core_cpp_descgate, PublisherQualities) // create and apply publisher pub2 desc_gate.ApplySample(CreatePublisher("pub2", 2), eCAL::tl_none); - // check gate size - auto sample_map = desc_gate.GetPublishers(); - EXPECT_EQ(2, sample_map.size()); + // check size + auto id_set = desc_gate.GetPublisherIDs(); + EXPECT_EQ(2, id_set.size()); - // check pub1 quality + // check publisher qualities { - auto pub_it = sample_map.find("pub1"); - EXPECT_NE(pub_it, sample_map.end()); - if (pub_it != sample_map.end()) + for (const auto& id : id_set) { - EXPECT_EQ(1, pub_it->second.id); - EXPECT_EQ("pub1-tdatatype.name", pub_it->second.info.name); - EXPECT_EQ("pub1-tdatatype.encoding", pub_it->second.info.encoding); - EXPECT_EQ("pub1-tdatatype.descriptor", pub_it->second.info.descriptor); - } - } - - // check pub2 quality - { - auto pub_it = sample_map.find("pub2"); - EXPECT_NE(pub_it, sample_map.end()); - if (pub_it != sample_map.end()) - { - EXPECT_EQ(2, pub_it->second.id); - EXPECT_EQ("pub2-tdatatype.name", pub_it->second.info.name); - EXPECT_EQ("pub2-tdatatype.encoding", pub_it->second.info.encoding); - EXPECT_EQ("pub2-tdatatype.descriptor", pub_it->second.info.descriptor); + eCAL::Registration::SQualityTopicInfo quality_info; + bool found = desc_gate.GetPublisherInfo(id, quality_info); + EXPECT_TRUE(found); + if (found) + { + std::string tname = id.topic_name; + EXPECT_EQ(tname + "-tdatatype.name", quality_info.info.name); + EXPECT_EQ(tname + "-tdatatype.encoding", quality_info.info.encoding); + EXPECT_EQ(tname + "-tdatatype.descriptor", quality_info.info.descriptor); + } } } // now let the sample expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + desc_gate.ApplySample(DestroyPublisher("pub1", 1), eCAL::tl_none); + desc_gate.ApplySample(DestroyPublisher("pub2", 2), eCAL::tl_none); // sample should be expired - EXPECT_EQ(0, desc_gate.GetPublishers().size()); + EXPECT_EQ(0, desc_gate.GetPublisherIDs().size()); } TEST(core_cpp_descgate, ManyPublisher) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; constexpr int num_pub(1000); for (auto pub = 0; pub < num_pub; ++pub) @@ -159,39 +177,41 @@ TEST(core_cpp_descgate, ManyPublisher) } // map should contain num_pub samples - EXPECT_EQ(num_pub, desc_gate.GetPublishers().size()); + EXPECT_EQ(num_pub, desc_gate.GetPublisherIDs().size()); // now let the samples expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + for (auto pub = 0; pub < num_pub; ++pub) + { + // create registration sample for pub-xx + desc_gate.ApplySample(DestroyPublisher("pub" + std::to_string(pub), pub), eCAL::tl_none); + } // samples should be expired - EXPECT_EQ(0, desc_gate.GetPublishers().size()); + EXPECT_EQ(0, desc_gate.GetPublisherIDs().size()); } TEST(core_cpp_descgate, SubscriberExpiration) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; // apply sample 5 times, sample should not expire auto runs(5); while ((runs--) != 0) { desc_gate.ApplySample(CreateSubscriber("sub1", 1), eCAL::tl_none); - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS / 2)); - - EXPECT_EQ(1, desc_gate.GetSubscribers().size()); + EXPECT_EQ(1, desc_gate.GetSubscriberIDs().size()); } // now let the sample expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + desc_gate.ApplySample(DestroySubscriber("sub1", 1), eCAL::tl_none); // sample should be expired - EXPECT_EQ(0, desc_gate.GetSubscribers().size()); + EXPECT_EQ(0, desc_gate.GetSubscriberIDs().size()); } TEST(core_cpp_descgate, SubscriberQualities) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; // create and apply subscriber sub1 desc_gate.ApplySample(CreateSubscriber("sub1", 1), eCAL::tl_none); @@ -199,46 +219,38 @@ TEST(core_cpp_descgate, SubscriberQualities) // create and apply subscriber sub2 desc_gate.ApplySample(CreateSubscriber("sub2", 2), eCAL::tl_none); - // check gate size - auto sample_map = desc_gate.GetSubscribers(); - EXPECT_EQ(2, sample_map.size()); + // check size + auto id_set = desc_gate.GetSubscriberIDs(); + EXPECT_EQ(2, id_set.size()); - // check sub1 quality + // check subscriber qualities { - auto sub_it = sample_map.find("sub1"); - EXPECT_NE(sub_it, sample_map.end()); - if (sub_it != sample_map.end()) + for (const auto& id : id_set) { - EXPECT_EQ(1, sub_it->second.id); - EXPECT_EQ("sub1-tdatatype.name", sub_it->second.info.name); - EXPECT_EQ("sub1-tdatatype.encoding", sub_it->second.info.encoding); - EXPECT_EQ("sub1-tdatatype.descriptor", sub_it->second.info.descriptor); - } - } - - // check sub2 quality - { - auto sub_it = sample_map.find("sub2"); - EXPECT_NE(sub_it, sample_map.end()); - if (sub_it != sample_map.end()) - { - EXPECT_EQ(2, sub_it->second.id); - EXPECT_EQ("sub2-tdatatype.name", sub_it->second.info.name); - EXPECT_EQ("sub2-tdatatype.encoding", sub_it->second.info.encoding); - EXPECT_EQ("sub2-tdatatype.descriptor", sub_it->second.info.descriptor); + eCAL::Registration::SQualityTopicInfo quality_info; + bool found = desc_gate.GetSubscriberInfo(id, quality_info); + EXPECT_TRUE(found); + if (found) + { + std::string tname = id.topic_name; + EXPECT_EQ(tname + "-tdatatype.name", quality_info.info.name); + EXPECT_EQ(tname + "-tdatatype.encoding", quality_info.info.encoding); + EXPECT_EQ(tname + "-tdatatype.descriptor", quality_info.info.descriptor); + } } } // now let the sample expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + desc_gate.ApplySample(DestroySubscriber("sub1", 1), eCAL::tl_none); + desc_gate.ApplySample(DestroySubscriber("sub2", 2), eCAL::tl_none); // sample should be expired - EXPECT_EQ(0, desc_gate.GetSubscribers().size()); + EXPECT_EQ(0, desc_gate.GetSubscriberIDs().size()); } TEST(core_cpp_descgate, ManySubscriber) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; constexpr int num_sub(1000); for (auto sub = 0; sub < num_sub; ++sub) @@ -248,39 +260,42 @@ TEST(core_cpp_descgate, ManySubscriber) } // map should contain num_sub samples - EXPECT_EQ(num_sub, desc_gate.GetSubscribers().size()); + EXPECT_EQ(num_sub, desc_gate.GetSubscriberIDs().size()); // now let the samples expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + for (auto sub = 0; sub < num_sub; ++sub) + { + // create registration sample for sub-xx + desc_gate.ApplySample(DestroySubscriber("sub" + std::to_string(sub), sub), eCAL::tl_none); + } // samples should be expired - EXPECT_EQ(0, desc_gate.GetSubscribers().size()); + EXPECT_EQ(0, desc_gate.GetSubscriberIDs().size()); } TEST(core_cpp_descgate, ServiceExpiration) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; // apply sample 5 times, sample should not expire auto runs(5); while ((runs--) != 0) { desc_gate.ApplySample(CreateService("service1", 1), eCAL::tl_none); - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS / 2)); - EXPECT_EQ(1, desc_gate.GetServices().size()); + EXPECT_EQ(1, desc_gate.GetServiceIDs().size()); } // now let the sample expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + desc_gate.ApplySample(DestroyService("service1", 1), eCAL::tl_none); // sample should be expired - EXPECT_EQ(0, desc_gate.GetServices().size()); + EXPECT_EQ(0, desc_gate.GetServiceIDs().size()); } TEST(core_cpp_descgate, ManyService) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; constexpr int num_service(1000); for (auto service = 0; service < num_service; ++service) @@ -290,39 +305,41 @@ TEST(core_cpp_descgate, ManyService) } // map should contain num_service samples - EXPECT_EQ(num_service, desc_gate.GetServices().size()); + EXPECT_EQ(num_service, desc_gate.GetServiceIDs().size()); // now let the samples expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + for (auto service = 0; service < num_service; ++service) + { + // create registration sample for service-xx + desc_gate.ApplySample(DestroyService("service" + std::to_string(service), service), eCAL::tl_none); + } // samples should be expired - EXPECT_EQ(0, desc_gate.GetServices().size()); + EXPECT_EQ(0, desc_gate.GetServiceIDs().size()); } TEST(core_cpp_descgate, ClientExpiration) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; // apply sample 5 times, sample should not expire auto runs(5); while ((runs--) != 0) { desc_gate.ApplySample(CreateClient("client1", 1), eCAL::tl_none); - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS / 2)); - - EXPECT_EQ(1, desc_gate.GetClients().size()); + EXPECT_EQ(1, desc_gate.GetClientIDs().size()); } // now let the sample expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + desc_gate.ApplySample(DestroyClient("client1", 1), eCAL::tl_none); // sample should be expired - EXPECT_EQ(0, desc_gate.GetClients().size()); + EXPECT_EQ(0, desc_gate.GetClientIDs().size()); } TEST(core_cpp_descgate, ManyClient) { - eCAL::CDescGate desc_gate(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + eCAL::CDescGate desc_gate; constexpr int num_client(1000); for (auto client = 0; client < num_client; ++client) @@ -332,11 +349,15 @@ TEST(core_cpp_descgate, ManyClient) } // map should contain num_client samples - EXPECT_EQ(num_client, desc_gate.GetClients().size()); + EXPECT_EQ(num_client, desc_gate.GetClientIDs().size()); // now let the samples expire - std::this_thread::sleep_for(std::chrono::milliseconds(DESCGATE_EXPIRATION_MS)); + for (auto client = 0; client < num_client; ++client) + { + // create registration sample for client-xx + desc_gate.ApplySample(DestroyClient("client" + std::to_string(client), client), eCAL::tl_none); + } // samples should be expired - EXPECT_EQ(0, desc_gate.GetClients().size()); + EXPECT_EQ(0, desc_gate.GetClientIDs().size()); } diff --git a/ecal/tests/cpp/expmap_test/src/expmap_test.cpp b/ecal/tests/cpp/expmap_test/src/expmap_test.cpp index 8fbe3993ec..4d6a6b3292 100644 --- a/ecal/tests/cpp/expmap_test/src/expmap_test.cpp +++ b/ecal/tests/cpp/expmap_test/src/expmap_test.cpp @@ -61,7 +61,7 @@ TestingClock::duration TestingClock::current_time{ 0 }; TEST(core_cpp_core, ExpMap_SetGet) { // create the map with 2500 ms expiration - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); // set "A" expmap["A"] = 1; @@ -81,7 +81,7 @@ TEST(core_cpp_core, ExpMap_SetGet) // check size //content = expmap.clone(); - expmap.remove_deprecated(); + expmap.erase_expired(); EXPECT_EQ(1, expmap.size()); // sleep @@ -89,7 +89,7 @@ TEST(core_cpp_core, ExpMap_SetGet) // check size //content = expmap.clone(); - expmap.remove_deprecated(); + expmap.erase_expired(); EXPECT_EQ(1, expmap.size()); // sleep @@ -97,29 +97,86 @@ TEST(core_cpp_core, ExpMap_SetGet) // check size //content = expmap.clone(); - expmap.remove_deprecated(); + expmap.erase_expired(); EXPECT_EQ(0, expmap.size()); expmap["A"] = 1; TestingClock::increment_time(std::chrono::milliseconds(150)); expmap["B"] = 2; expmap["C"] = 3; - expmap.remove_deprecated(); - EXPECT_EQ(3, expmap.size()); + + { + auto erased = expmap.erase_expired(); + EXPECT_EQ(3, expmap.size()); + EXPECT_EQ(0, erased.size()); + } + TestingClock::increment_time(std::chrono::milliseconds(150)); expmap["B"] = 4; - expmap.remove_deprecated(); - EXPECT_EQ(2, expmap.size()); + + { + auto erased = expmap.erase_expired(); + EXPECT_EQ(2, expmap.size()); + EXPECT_EQ(1, erased.size()); + auto a = erased.find("A"); + EXPECT_NE(a, erased.end()); + EXPECT_EQ(a->second, 1); + } + TestingClock::increment_time(std::chrono::milliseconds(150)); - expmap.remove_deprecated(); - EXPECT_EQ(1, expmap.size()); + + { + auto erased = expmap.erase_expired(); + EXPECT_EQ(1, expmap.size()); + EXPECT_EQ(1, erased.size()); + auto c = erased.find("C"); + EXPECT_NE(c, erased.end()); + EXPECT_EQ(c->second, 3); + } + // sleep TestingClock::increment_time(std::chrono::milliseconds(150)); } +TEST(core_cpp_core, ExpMap_EraseMultiple) +{ + // create the map with 2500 ms expiration + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); + + expmap["A"] = 1; + expmap["B"] = 2; + expmap["C"] = 3; + + TestingClock::increment_time(std::chrono::milliseconds(250)); + + auto erased = expmap.erase_expired(); + EXPECT_EQ(0, expmap.size()); + EXPECT_EQ(3, erased.size()); + + auto a = erased.find("A"); + EXPECT_NE(a, erased.end()); + EXPECT_EQ(a->second, 1); + + auto b = erased.find("B"); + EXPECT_NE(b, erased.end()); + EXPECT_EQ(b->second, 2); + + auto c = erased.find("C"); + EXPECT_NE(c, erased.end()); + EXPECT_EQ(c->second, 3); +} + + +TEST(core_cpp_core, ExpMap_EraseEmptyMap) +{ + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); + expmap.erase_expired(); + EXPECT_TRUE(expmap.empty()); +} + TEST(core_cpp_core, ExpMap_Insert) { - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); auto ret = expmap.insert(std::make_pair("A", 1)); auto key = (*ret.first).first; @@ -132,14 +189,14 @@ TEST(core_cpp_core, ExpMap_Insert) EXPECT_EQ(i, 1); TestingClock::increment_time(std::chrono::milliseconds(300)); - expmap.remove_deprecated(); + expmap.erase_expired(); EXPECT_EQ(0, expmap.size()); } // This tests uses find to find an element TEST(core_cpp_core, ExpMap_Find) { - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); auto it = expmap.find("A"); EXPECT_EQ(expmap.end(), it); @@ -151,14 +208,14 @@ TEST(core_cpp_core, ExpMap_Find) EXPECT_EQ(i, 1); TestingClock::increment_time(std::chrono::milliseconds(300)); - expmap.remove_deprecated(); + expmap.erase_expired(); EXPECT_EQ(0, expmap.size()); } -// This test assures that find can be called on a const CExpMap and returns an CExpMap::const_iterator +// This test assures that find can be called on a const CExpirationMap and returns an CExpirationMap::const_iterator TEST(core_cpp_core, ExpMap_FindConst) { - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); auto it = expmap.find("A"); EXPECT_EQ(expmap.end(), it); @@ -168,19 +225,19 @@ TEST(core_cpp_core, ExpMap_FindConst) const auto& const_ref_exmap = expmap; auto const_it = const_ref_exmap.find("A"); // assert that we are actually getting a const_iterator here! - static_assert(std::is_same::const_iterator>::value, "We're not being returned a const_iterator from find."); + static_assert(std::is_same::const_iterator>::value, "We're not being returned a const_iterator from find."); int i = (*const_it).second; EXPECT_EQ(i, 1); TestingClock::increment_time(std::chrono::milliseconds(300)); - expmap.remove_deprecated(); + expmap.erase_expired(); EXPECT_EQ(0, expmap.size()); } TEST(core_cpp_core, ExpMap_Iterate) { // create the map with 2500 ms expiration - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); expmap["A"] = 1; std::string key; @@ -196,7 +253,7 @@ TEST(core_cpp_core, ExpMap_Iterate) EXPECT_EQ(1, value); } -void ConstRefIterate(const eCAL::Util::CExpMap& map) +void ConstRefIterate(const eCAL::Util::CExpirationMap& map) { std::string key; int value; @@ -214,7 +271,7 @@ void ConstRefIterate(const eCAL::Util::CExpMap& TEST(core_cpp_core, ExpMap_ConstExpMapIterate) { // create the map with 2500 ms expiration - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); expmap["A"] = 1; ConstRefIterate(expmap); @@ -222,7 +279,7 @@ TEST(core_cpp_core, ExpMap_ConstExpMapIterate) TEST(core_cpp_core, ExpMap_Empty) { - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); EXPECT_EQ(true, expmap.empty()); expmap["A"] = 1; EXPECT_EQ(false, expmap.empty()); @@ -230,7 +287,7 @@ TEST(core_cpp_core, ExpMap_Empty) TEST(core_cpp_core, ExpMap_Size) { - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); EXPECT_EQ(0, expmap.size()); expmap["A"] = 1; EXPECT_EQ(1, expmap.size()); @@ -238,7 +295,7 @@ TEST(core_cpp_core, ExpMap_Size) TEST(core_cpp_core, ExpMap_Remove) { - eCAL::Util::CExpMap expmap(std::chrono::milliseconds(200)); + eCAL::Util::CExpirationMap expmap(std::chrono::milliseconds(200)); expmap["A"] = 1; EXPECT_EQ(1, expmap.size()); EXPECT_TRUE(expmap.erase("A")); diff --git a/ecal/tests/cpp/pubsub_proto_test/src/proto_subscriber_test.cpp b/ecal/tests/cpp/pubsub_proto_test/src/proto_subscriber_test.cpp index a660046190..6d198b1e6a 100644 --- a/ecal/tests/cpp/pubsub_proto_test/src/proto_subscriber_test.cpp +++ b/ecal/tests/cpp/pubsub_proto_test/src/proto_subscriber_test.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,12 +46,20 @@ class ProtoSubscriberTest : public ::testing::Test { eCAL::Finalize(); } - void SendPerson(eCAL::protobuf::CPublisher& pub) + size_t SendPerson(eCAL::protobuf::CPublisher& pub) { - pb::People::Person p; p.set_id(1); p.set_name("Max"); - pub.Send(p); + return pub.Send(p); + } + + size_t GetPersonSize() + { +#if GOOGLE_PROTOBUF_VERSION >= 3001000 + return static_cast(p.ByteSizeLong()); +#else + return static_cast(p.ByteSize()); +#endif } void OnPerson(const char*, const pb::People::Person&, long long, long long) @@ -60,6 +68,9 @@ class ProtoSubscriberTest : public ::testing::Test { } std::atomic received_callbacks; + +private: + pb::People::Person p; }; using core_cpp_pubsub_proto_sub = ProtoSubscriberTest; @@ -75,11 +86,11 @@ TEST_F(core_cpp_pubsub_proto_sub, ProtoSubscriberTest_SendReceive) std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - SendPerson(person_pub); + auto bytes_send = SendPerson(person_pub); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // assert that the OnPerson callback has been called once. ASSERT_EQ(1, received_callbacks); - + ASSERT_EQ(bytes_send, GetPersonSize()); } TEST_F(core_cpp_pubsub_proto_sub, ProtoSubscriberTest_MoveAssignment) diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_acknowledge.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_acknowledge.cpp index 1530f66e6d..e564457dd7 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_acknowledge.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_acknowledge.cpp @@ -55,7 +55,7 @@ TEST(core_cpp_pubsub, TimeoutAcknowledgment) // create publisher config eCAL::Publisher::Configuration pub_config; - pub_config.shm.acknowledge_timeout_ms = 500; + pub_config.layer.shm.acknowledge_timeout_ms = 500; // create publisher eCAL::string::CPublisher pub("topic", pub_config); diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_multibuffer.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_multibuffer.cpp index 31acdb610d..569736efb3 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_multibuffer.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_multibuffer.cpp @@ -78,13 +78,13 @@ std::vector multibuffer_pub_sub_test(int buffer_count, bool zero_copy, int // create publisher config eCAL::Publisher::Configuration pub_config; // set transport layer - pub_config.shm.enable = true; - pub_config.udp.enable = false; - pub_config.tcp.enable = false; + pub_config.layer.shm.enable = true; + pub_config.layer.udp.enable = false; + pub_config.layer.tcp.enable = false; // set zero copy mode - pub_config.shm.zero_copy_mode = zero_copy; + pub_config.layer.shm.zero_copy_mode = zero_copy; // set number of memory buffer - pub_config.shm.memfile_buffer_count = buffer_count; + pub_config.layer.shm.memfile_buffer_count = buffer_count; // create publisher for topic "A" eCAL::CPublisher pub("A", pub_config); diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp index 4e18f043e6..25a8bc7077 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -216,3 +216,97 @@ TEST(core_cpp_pubsub, SporadicEmptyReceives) // finalize eCAL API EXPECT_EQ(0, eCAL::Finalize()); } + +TEST(PubSub, TestSubscriberSeen) +{ + // initialize eCAL API + EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "subscriber_seen")); + + // enable data loopback + eCAL::Util::EnableLoopback(true); + + std::atomic subscriber_seen_at_publication_start(false); + std::atomic subscriber_seen_at_publication_end(false); + + std::atomic do_start_publication(false); + std::atomic publication_finished(false); + + // publishing thread + auto publisher_thread = [&]() { + eCAL::Publisher::Configuration pub_config; + pub_config.layer.shm.acknowledge_timeout_ms = 500; + eCAL::CPublisher pub("blob", pub_config); + + int cnt(0); + const auto max_runs(1000); + while (eCAL::Ok()) + { + if (do_start_publication && cnt < max_runs) + { + if (cnt == 0) + { + subscriber_seen_at_publication_start = pub.IsSubscribed(); + } + + pub.Send(std::to_string(cnt)); + cnt++; + + if (cnt == max_runs) + { + subscriber_seen_at_publication_end = pub.IsSubscribed(); + publication_finished = true; + break; + } + } + } + }; + + // subscribing thread + auto subscriber_thread = [&]() { + eCAL::CSubscriber sub("blob"); + bool received(false); + auto max_lines(10); + auto receive_lambda = [&received, &max_lines](const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) + { + if (max_lines) + { + // the final log should look like this + // ----------------------------------- + // Receiving 0 + // Receiving 1 + // Receiving 2 + // Receiving 3 + // Receiving 4 + // Receiving 5 + // Receiving 6 + // Receiving 7 + // Receiving 8 + // Receiving 9 + // ----------------------------------- + std::cout << "Receiving " << std::string(static_cast(data_->buf), data_->size) << std::endl; + max_lines--; + } + }; + sub.AddReceiveCallback(receive_lambda); + + while (eCAL::Ok() && !publication_finished) + { + if (sub.IsPublished()) do_start_publication = true; + } + }; + + // create threads for publisher and subscriber + std::thread pub_thread(publisher_thread); + std::thread sub_thread(subscriber_thread); + + // join threads to the main thread + pub_thread.join(); + sub_thread.join(); + + // finalize eCAL API + eCAL::Finalize(); + + // check if the publisher has seen the subscriber + EXPECT_TRUE(subscriber_seen_at_publication_start); + EXPECT_TRUE(subscriber_seen_at_publication_end); +} diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_test_shm.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_test_shm.cpp index fe40695b37..d326c1817f 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_test_shm.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_test_shm.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,12 +60,17 @@ TEST(core_cpp_pubsub, ZeroPayloadMessageSHM) // create publisher config eCAL::Publisher::Configuration pub_config; // set transport layer - pub_config.shm.enable = true; - pub_config.udp.enable = false; - pub_config.tcp.enable = false; + pub_config.layer.shm.enable = true; + pub_config.layer.udp.enable = false; + pub_config.layer.tcp.enable = false; + + // create publisher for topic "A" (no zero copy) + eCAL::CPublisher pub1("A", pub_config); + + // switch on zero copy + pub_config.layer.shm.zero_copy_mode = true; + eCAL::CPublisher pub2("A", pub_config); - // create publisher for topic "A" - eCAL::CPublisher pub("A", pub_config); // add callback EXPECT_EQ(true, sub.AddReceiveCallback(std::bind(OnReceive, std::placeholders::_1, std::placeholders::_2))); @@ -76,21 +81,30 @@ TEST(core_cpp_pubsub, ZeroPayloadMessageSHM) g_callback_received_bytes = 0; g_callback_received_count = 0; - EXPECT_EQ(send_s.size(), pub.Send(send_s)); + // send without zero copy + EXPECT_EQ(send_s.size(), pub1.Send(send_s)); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); + + EXPECT_EQ(send_s.size(), pub1.Send(nullptr, 0)); + eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); + + // send with zero copy + EXPECT_EQ(send_s.size(), pub2.Send(send_s)); eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); - EXPECT_EQ(send_s.size(), pub.Send(nullptr, 0)); + EXPECT_EQ(send_s.size(), pub2.Send(nullptr, 0)); eCAL::Process::SleepMS(DATA_FLOW_TIME_MS); // check callback receive EXPECT_EQ(send_s.size(), g_callback_received_bytes); - EXPECT_EQ(2, g_callback_received_count); + EXPECT_EQ(4, g_callback_received_count); // destroy subscriber sub.Destroy(); // destroy publisher - pub.Destroy(); + pub1.Destroy(); + pub2.Destroy(); // finalize eCAL API eCAL::Finalize(); @@ -115,9 +129,9 @@ TEST(core_cpp_pubsub, MultipleSendsSHM) // create publisher config eCAL::Publisher::Configuration pub_config; // set transport layer - pub_config.shm.enable = true; - pub_config.udp.enable = false; - pub_config.tcp.enable = false; + pub_config.layer.shm.enable = true; + pub_config.layer.udp.enable = false; + pub_config.layer.tcp.enable = false; // create publisher for topic "A" eCAL::string::CPublisher pub("A", pub_config); diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_test_udp.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_test_udp.cpp index 0776132e37..cfdf27b5ac 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_test_udp.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_test_udp.cpp @@ -60,9 +60,9 @@ TEST(core_cpp_pubsub, ZeroPayloadMessageUDP) // create publisher config eCAL::Publisher::Configuration pub_config; // set transport layer - pub_config.shm.enable = false; - pub_config.udp.enable = true; - pub_config.tcp.enable = false; + pub_config.layer.shm.enable = false; + pub_config.layer.udp.enable = true; + pub_config.layer.tcp.enable = false; // create publisher for topic "A" eCAL::CPublisher pub("A", pub_config); @@ -115,9 +115,9 @@ TEST(core_cpp_pubsub, MultipleSendsUDP) // create publisher config eCAL::Publisher::Configuration pub_config; // set transport layer - pub_config.shm.enable = false; - pub_config.udp.enable = true; - pub_config.tcp.enable = false; + pub_config.layer.shm.enable = false; + pub_config.layer.udp.enable = true; + pub_config.layer.tcp.enable = false; // create publisher for topic "A" eCAL::string::CPublisher pub("A", pub_config); diff --git a/ecal/tests/cpp/registration_test/CMakeLists.txt b/ecal/tests/cpp/registration_test/CMakeLists.txt new file mode 100644 index 0000000000..dcb2781346 --- /dev/null +++ b/ecal/tests/cpp/registration_test/CMakeLists.txt @@ -0,0 +1,46 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2024 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +project(test_registration) + +find_package(Threads REQUIRED) +find_package(GTest REQUIRED) + +set(registration_test_src + src/registration_timout_provider_test.cpp + ${ECAL_CORE_PROJECT_ROOT}/core/src/registration/ecal_registration_timeout_provider.cpp +) + +ecal_add_gtest(${PROJECT_NAME} ${registration_test_src}) + +target_include_directories(${PROJECT_NAME} PRIVATE $) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + $<$:dl> + $<$,$>>:rt> + Threads::Threads +) + + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +target_compile_definitions(${PROJECT_NAME} PRIVATE ECAL_CORE_TRANSPORT_SHM) + +ecal_install_gtest(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER tests/cpp/core) diff --git a/ecal/tests/cpp/registration_test/src/registration_timout_provider_test.cpp b/ecal/tests/cpp/registration_test/src/registration_timout_provider_test.cpp new file mode 100644 index 0000000000..ff961d620b --- /dev/null +++ b/ecal/tests/cpp/registration_test/src/registration_timout_provider_test.cpp @@ -0,0 +1,257 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include + +#include "registration/ecal_registration_timeout_provider.h" +#include "serialization/ecal_struct_sample_registration.h" + +eCAL::Registration::Sample pub_foo_process_a_unregister; +eCAL::Registration::Sample pub_foo_process_a_register_1; +eCAL::Registration::Sample pub_foo_process_a_register_2; + +eCAL::Registration::Sample sub_foo_process_a_unregister; +eCAL::Registration::Sample sub_foo_process_a_register_1; +eCAL::Registration::Sample sub_foo_process_a_register_2; + +eCAL::Registration::Sample sub_foo_process_b_unregister; +eCAL::Registration::Sample sub_foo_process_b_register_1; +eCAL::Registration::Sample sub_foo_process_b_register_2; + +// make sure we create unique topic IDs for our testcases +std::string getUniqueId() +{ + static int topic_id = 1; + return std::to_string(topic_id++); +} + +eCAL::Registration::Sample UpdateTopicSample(const eCAL::Registration::Sample& input_) +{ + // vary statistical data + eCAL::Registration::Sample updated = input_; + updated.topic.rclock = input_.topic.rclock + 1; + updated.topic.dclock = input_.topic.dclock + 10; + return updated; +} + +void InitializeAllSamples() +{ + // Publisher 1 + pub_foo_process_a_unregister.cmd_type = eCAL::bct_unreg_publisher; + pub_foo_process_a_unregister.identifier.host_name = "host0"; + pub_foo_process_a_unregister.identifier.process_id = 1000; + pub_foo_process_a_unregister.identifier.entity_id = getUniqueId(); + pub_foo_process_a_unregister.topic.hgname = "host0"; + pub_foo_process_a_unregister.topic.pname = "process_a"; + pub_foo_process_a_unregister.topic.tname = "foo"; + pub_foo_process_a_register_1.topic.uname = "abc"; + + pub_foo_process_a_register_1 = pub_foo_process_a_unregister; + pub_foo_process_a_register_1.cmd_type = eCAL::bct_reg_publisher; + pub_foo_process_a_register_1.topic.rclock = 1; + pub_foo_process_a_register_1.topic.direction = "publisher"; + pub_foo_process_a_register_1.topic.tdatatype = { "a", "b", "c" }; + pub_foo_process_a_register_1.topic.tsize = 100; + pub_foo_process_a_register_1.topic.connections_loc = 2; + pub_foo_process_a_register_1.topic.connections_ext = 2; + pub_foo_process_a_register_1.topic.message_drops = 0; + pub_foo_process_a_register_1.topic.did = 0; + pub_foo_process_a_register_1.topic.dclock = 1; + pub_foo_process_a_register_1.topic.dfreq = 10; + + pub_foo_process_a_register_2 = UpdateTopicSample(pub_foo_process_a_register_1); + + // Subscriber 1 + sub_foo_process_a_unregister.cmd_type = eCAL::bct_unreg_subscriber; + sub_foo_process_a_unregister.identifier.host_name = "host0"; + sub_foo_process_a_unregister.identifier.process_id = 1000; + sub_foo_process_a_unregister.identifier.entity_id = getUniqueId(); + sub_foo_process_a_unregister.topic.hgname = "host0"; + sub_foo_process_a_unregister.topic.pname = "process_a"; + sub_foo_process_a_unregister.topic.tname = "foo"; + sub_foo_process_a_register_1.topic.uname = "abc"; + + sub_foo_process_a_register_1 = sub_foo_process_a_unregister; + sub_foo_process_a_register_1.cmd_type = eCAL::bct_reg_subscriber; + sub_foo_process_a_register_1.topic.rclock = 1; + sub_foo_process_a_register_1.topic.direction = "subscriber"; + sub_foo_process_a_register_1.topic.tdatatype = { "a", "b", "c" }; + sub_foo_process_a_register_1.topic.tsize = 100; + sub_foo_process_a_register_1.topic.connections_loc = 2; + sub_foo_process_a_register_1.topic.connections_ext = 2; + sub_foo_process_a_register_1.topic.message_drops = 0; + sub_foo_process_a_register_1.topic.did = 0; + sub_foo_process_a_register_1.topic.dclock = 1; + sub_foo_process_a_register_1.topic.dfreq = 10; + + sub_foo_process_a_register_2 = UpdateTopicSample(sub_foo_process_a_register_1); + + // Subscriber 2 + sub_foo_process_b_unregister.cmd_type = eCAL::bct_unreg_subscriber; + sub_foo_process_b_unregister.identifier.host_name = "host0"; + sub_foo_process_b_unregister.identifier.process_id = 1000; + sub_foo_process_b_unregister.identifier.entity_id = getUniqueId(); + sub_foo_process_b_unregister.topic.hgname = "host0"; + sub_foo_process_b_unregister.topic.pname = "process_b"; + sub_foo_process_b_unregister.topic.tname = "foo"; + sub_foo_process_b_register_1.topic.uname = "abc"; + + sub_foo_process_b_register_1 = sub_foo_process_b_unregister; + sub_foo_process_b_register_1.cmd_type = eCAL::bct_reg_subscriber; + sub_foo_process_b_register_1.topic.rclock = 1; + sub_foo_process_b_register_1.topic.direction = "subscriber"; + sub_foo_process_b_register_1.topic.tdatatype = { "a", "b", "c" }; + sub_foo_process_b_register_1.topic.tsize = 100; + sub_foo_process_b_register_1.topic.connections_loc = 2; + sub_foo_process_b_register_1.topic.connections_ext = 2; + sub_foo_process_b_register_1.topic.message_drops = 0; + sub_foo_process_b_register_1.topic.did = 0; + sub_foo_process_b_register_1.topic.dclock = 1; + sub_foo_process_b_register_1.topic.dfreq = 10; + + sub_foo_process_b_register_2 = UpdateTopicSample(sub_foo_process_b_register_1); +} + +class TestingClock { +public: + // Define the required types for TrivialClock + using duration = std::chrono::milliseconds; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point; + static const bool is_steady = false; + + // Function to get the current time + static time_point now() noexcept { + return time_point(current_time); + } + + // Function to manually set the current time + static void set_time(const time_point& tp) { + current_time = tp.time_since_epoch(); + } + + // Function to manually increment the current time by a given duration + static void increment_time(const duration& d) { + current_time += d; + } + +private: + static duration current_time; +}; + +// Initialize the static member +TestingClock::duration TestingClock::current_time{ 0 }; + +// Create a test fixture class +class core_cpp_registration : public ::testing::Test { +protected: + // Override the SetUp method to initialize the global variable + void SetUp() override { + InitializeAllSamples(); + TestingClock::set_time(std::chrono::time_point(std::chrono::milliseconds(0))); + } + + // You can also override the TearDown method if needed + void TearDown() override { + // Clean up if necessary + } +}; + +TEST_F(core_cpp_registration, IsUnregistrationSamples) +{ + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(pub_foo_process_a_unregister), true); + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(pub_foo_process_a_register_1), false); + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(pub_foo_process_a_register_2), false); + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(sub_foo_process_a_unregister), true); + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(sub_foo_process_a_register_1), false); + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(sub_foo_process_a_register_2), false); + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(sub_foo_process_b_unregister), true); + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(sub_foo_process_b_register_1), false); + EXPECT_EQ(eCAL::Registration::IsUnregistrationSample(sub_foo_process_b_register_2), false); +} + + +TEST_F(core_cpp_registration, CreateUnregistrationSamples) +{ + EXPECT_EQ(eCAL::Registration::CreateUnregisterSample(pub_foo_process_a_register_1), pub_foo_process_a_unregister); + EXPECT_EQ(eCAL::Registration::CreateUnregisterSample(pub_foo_process_a_register_2), pub_foo_process_a_unregister); + EXPECT_EQ(eCAL::Registration::CreateUnregisterSample(sub_foo_process_a_register_1), sub_foo_process_a_unregister); + EXPECT_EQ(eCAL::Registration::CreateUnregisterSample(sub_foo_process_a_register_2), sub_foo_process_a_unregister); + EXPECT_EQ(eCAL::Registration::CreateUnregisterSample(sub_foo_process_b_register_1), sub_foo_process_b_unregister); + EXPECT_EQ(eCAL::Registration::CreateUnregisterSample(sub_foo_process_b_register_2), sub_foo_process_b_unregister); +} + +// we apply samples and then unregistration samples +// we need to veryfy no callback is called +TEST_F(core_cpp_registration, TimeOutProviderApplyUnregistration) +{ + int callbacks_called = 0; + eCAL::Registration::CTimeoutProvider timout_provider(std::chrono::seconds(5), [&callbacks_called](const eCAL::Registration::Sample&) {callbacks_called++; return true; }); + + timout_provider.ApplySample(pub_foo_process_a_register_1); + TestingClock::increment_time(std::chrono::seconds(1)); + timout_provider.CheckForTimeouts(); + EXPECT_EQ(callbacks_called, 0); + + TestingClock::increment_time(std::chrono::seconds(1)); + timout_provider.ApplySample(pub_foo_process_a_register_2); + TestingClock::increment_time(std::chrono::seconds(1)); + timout_provider.CheckForTimeouts(); + EXPECT_EQ(callbacks_called, 0); + + TestingClock::increment_time(std::chrono::seconds(1)); + timout_provider.ApplySample(pub_foo_process_a_unregister); + TestingClock::increment_time(std::chrono::seconds(1)); + timout_provider.CheckForTimeouts(); + EXPECT_EQ(callbacks_called, 0); +} + +// we apply samples and then unregistration samples +// we need to veryfy no callback is called +TEST_F(core_cpp_registration, TimeOutProviderApplyTimeout) +{ + int callbacks_called = 0; + eCAL::Registration::Sample sample_from_callback; + eCAL::Registration::CTimeoutProvider timout_provider(std::chrono::seconds(5), + [&sample_from_callback, &callbacks_called](const eCAL::Registration::Sample& s) + { + sample_from_callback = s; + ++callbacks_called; + return true; + }); + + timout_provider.ApplySample(pub_foo_process_a_register_1); + TestingClock::increment_time(std::chrono::seconds(6)); + timout_provider.CheckForTimeouts(); + EXPECT_EQ(sample_from_callback, pub_foo_process_a_unregister); + EXPECT_EQ(callbacks_called, 1); + + // reset sample + sample_from_callback = eCAL::Registration::Sample{}; + callbacks_called = 0; + TestingClock::increment_time(std::chrono::seconds(6)); + timout_provider.ApplySample(pub_foo_process_a_register_2); + TestingClock::increment_time(std::chrono::seconds(6)); + timout_provider.CheckForTimeouts(); + EXPECT_EQ(sample_from_callback, pub_foo_process_a_unregister); + EXPECT_EQ(callbacks_called, 1); +} diff --git a/ecal/tests/cpp/registration_test_public/CMakeLists.txt b/ecal/tests/cpp/registration_test_public/CMakeLists.txt new file mode 100644 index 0000000000..c20365afca --- /dev/null +++ b/ecal/tests/cpp/registration_test_public/CMakeLists.txt @@ -0,0 +1,59 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2024 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +project(test_registration_public) + +find_package(Threads REQUIRED) +find_package(GTest REQUIRED) + +if(ECAL_CORE_PUBLISHER AND ECAL_CORE_SUBSCRIBER) + set(registration_test_topics_src + src/registration_gettopics.cpp + ) +endif() + +if(ECAL_CORE_SERVICE) + set(registration_test_service_src + src/registration_getclients.cpp + src/registration_getservices.cpp + ) +endif() + +set(registration_test_src + ${registration_test_topics_src} + ${registration_test_service_src} +) + +ecal_add_gtest(${PROJECT_NAME} ${registration_test_src}) + +target_include_directories(${PROJECT_NAME} PRIVATE $) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + eCAL::core + Threads::Threads) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_gtest(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER tests/cpp/core) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${${PROJECT_NAME}_src} +) diff --git a/ecal/tests/cpp/util_test/src/util_getclients.cpp b/ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp similarity index 60% rename from ecal/tests/cpp/util_test/src/util_getclients.cpp rename to ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp index 28eac2422b..8c3a09b067 100644 --- a/ecal/tests/cpp/util_test/src/util_getclients.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,15 +23,17 @@ enum { CMN_MONITORING_TIMEOUT_MS = (5000 + 100), - CMN_REGISTRATION_REFRESH_MS = (1000 + 100) + CMN_REGISTRATION_REFRESH_MS = (1000) }; - -TEST(core_cpp_util, ClientExpiration) +TEST(core_cpp_registration_public, ClientExpiration) { // initialize eCAL API - eCAL::Initialize(0, nullptr, "core_cpp_util"); + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); - std::map client_info_map; + std::map client_info_map; // create simple client and let it expire { @@ -43,15 +45,18 @@ TEST(core_cpp_util, ClientExpiration) service_method_info.response_type.descriptor = "foo::resp_desc"; const eCAL::CServiceClient client("foo::service", { {"foo::method", service_method_info} }); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // get all clients - eCAL::Util::GetClients(client_info_map); + eCAL::Registration::GetClients(client_info_map); // check size EXPECT_EQ(client_info_map.size(), 1); // check client/method names - std::set client_method_names; - eCAL::Util::GetClientMethodNames(client_method_names); + std::set client_method_names; + eCAL::Registration::GetClientMethodNames(client_method_names); EXPECT_EQ(client_method_names.size(), 1); for (const auto& name : client_method_names) { @@ -63,18 +68,18 @@ TEST(core_cpp_util, ClientExpiration) eCAL::Process::SleepMS(CMN_MONITORING_TIMEOUT_MS); // get all clients again, client should not be expired - eCAL::Util::GetClients(client_info_map); + eCAL::Registration::GetClients(client_info_map); // check size EXPECT_EQ(client_info_map.size(), 1); } // let's unregister - eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // get all clients again, all clients // should be removed from the map - eCAL::Util::GetClients(client_info_map); + eCAL::Registration::GetClients(client_info_map); // check size EXPECT_EQ(client_info_map.size(), 0); @@ -83,12 +88,15 @@ TEST(core_cpp_util, ClientExpiration) eCAL::Finalize(); } -TEST(core_cpp_util, ClientEqualQualities) +TEST(core_cpp_registration_public, ClientEqualQualities) { // initialize eCAL API - eCAL::Initialize(0, nullptr, "core_cpp_util"); + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); - std::map client_info_map; + std::map client_info_map; // create 2 clients with the same quality of data type information { @@ -100,8 +108,11 @@ TEST(core_cpp_util, ClientEqualQualities) service_method_info1.response_type.descriptor = "foo::resp_desc1"; eCAL::CServiceClient client1("foo::service", { {"foo::method", service_method_info1} }); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // get all clients - eCAL::Util::GetClients(client_info_map); + eCAL::Registration::GetClients(client_info_map); // check size EXPECT_EQ(client_info_map.size(), 1); @@ -110,10 +121,10 @@ TEST(core_cpp_util, ClientEqualQualities) std::string req_type, resp_type; std::string req_desc, resp_desc; - eCAL::Util::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type1"); EXPECT_EQ(resp_type, "foo::resp_type1"); - eCAL::Util::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc1"); EXPECT_EQ(resp_desc, "foo::resp_desc1"); @@ -127,10 +138,10 @@ TEST(core_cpp_util, ClientEqualQualities) eCAL::CServiceClient client2("foo::service", { {"foo::method", service_method_info2} }); // check attributes - eCAL::Util::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type1"); EXPECT_EQ(resp_type, "foo::resp_type1"); - eCAL::Util::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc1"); EXPECT_EQ(resp_desc, "foo::resp_desc1"); @@ -141,7 +152,7 @@ TEST(core_cpp_util, ClientEqualQualities) eCAL::Process::SleepMS(CMN_MONITORING_TIMEOUT_MS); // get all clients again, clients should not be expired - eCAL::Util::GetClients(client_info_map); + eCAL::Registration::GetClients(client_info_map); // check size EXPECT_EQ(client_info_map.size(), 1); @@ -149,21 +160,24 @@ TEST(core_cpp_util, ClientEqualQualities) // destroy client 1 client1.Destroy(); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // check attributes, client 1 attributes should be replaced by client 2 attributes now - eCAL::Util::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type2"); EXPECT_EQ(resp_type, "foo::resp_type2"); - eCAL::Util::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc2"); EXPECT_EQ(resp_desc, "foo::resp_desc2"); } // let's unregister - eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // get all clients again, all clients // should be removed from the map - eCAL::Util::GetClients(client_info_map); + eCAL::Registration::GetClients(client_info_map); // check size EXPECT_EQ(client_info_map.size(), 0); @@ -172,12 +186,15 @@ TEST(core_cpp_util, ClientEqualQualities) eCAL::Finalize(); } -TEST(core_cpp_util, ClientDifferentQualities) +TEST(core_cpp_registration_public, ClientDifferentQualities) { // initialize eCAL API - eCAL::Initialize(0, nullptr, "core_cpp_util"); + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); - std::map client_info_map; + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); + + std::map client_info_map; // create 2 clients with different qualities of data type information { @@ -189,8 +206,11 @@ TEST(core_cpp_util, ClientDifferentQualities) service_method_info1.response_type.descriptor = ""; eCAL::CServiceClient client1("foo::service", { {"foo::method", service_method_info1} }); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // get all clients - eCAL::Util::GetClients(client_info_map); + eCAL::Registration::GetClients(client_info_map); // check size EXPECT_EQ(client_info_map.size(), 1); @@ -199,10 +219,10 @@ TEST(core_cpp_util, ClientDifferentQualities) std::string req_type, resp_type; std::string req_desc, resp_desc; - eCAL::Util::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type1"); EXPECT_EQ(resp_type, ""); - eCAL::Util::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc1"); EXPECT_EQ(resp_desc, ""); @@ -214,11 +234,14 @@ TEST(core_cpp_util, ClientDifferentQualities) service_method_info2.response_type.descriptor = "foo::resp_desc2"; eCAL::CServiceClient client2("foo::service", { {"foo::method", service_method_info2} }); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // check attributes, we expect attributes from client 2 here - eCAL::Util::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetClientTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type2"); EXPECT_EQ(resp_type, "foo::resp_type2"); - eCAL::Util::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetClientDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc2"); EXPECT_EQ(resp_desc, "foo::resp_desc2"); @@ -227,11 +250,11 @@ TEST(core_cpp_util, ClientDifferentQualities) } // let's unregister - eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // get all clients again, all clients // should be removed from the map - eCAL::Util::GetClients(client_info_map); + eCAL::Registration::GetClients(client_info_map); // check size EXPECT_EQ(client_info_map.size(), 0); @@ -239,3 +262,41 @@ TEST(core_cpp_util, ClientDifferentQualities) // finalize eCAL API eCAL::Finalize(); } + +TEST(core_cpp_registration_public, GetClientIDs) +{ + // initialize eCAL API + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); + + // create simple client + { + // create client + eCAL::SServiceMethodInformation service_method_info; + service_method_info.request_type.name = "foo::req_type"; + service_method_info.request_type.descriptor = "foo::req_desc"; + service_method_info.response_type.name = "foo::resp_type"; + service_method_info.response_type.descriptor = "foo::resp_desc"; + const eCAL::CServiceClient client("foo::service", { {"foo::method", service_method_info} }); + + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + + // get client + auto id_set = eCAL::Registration::GetClientIDs(); + EXPECT_EQ(1, id_set.size()); + if (id_set.size() > 0) + { + eCAL::Registration::SQualityServiceInfo info; + EXPECT_TRUE(eCAL::Registration::GetClientInfo(*id_set.begin(), info)); + + // check service/method names + EXPECT_EQ(service_method_info, info.info); + } + } + + // finalize eCAL API + eCAL::Finalize(); +} diff --git a/ecal/tests/cpp/util_test/src/util_getservices.cpp b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp similarity index 53% rename from ecal/tests/cpp/util_test/src/util_getservices.cpp rename to ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp index fbf2239d43..195053c40c 100644 --- a/ecal/tests/cpp/util_test/src/util_getservices.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,15 +23,18 @@ enum { CMN_MONITORING_TIMEOUT_MS = (5000 + 100), - CMN_REGISTRATION_REFRESH_MS = (1000 + 100) + CMN_REGISTRATION_REFRESH_MS = (1000) }; -TEST(core_cpp_util, ServiceExpiration) +TEST(core_cpp_registration_public, ServiceExpiration) { // initialize eCAL API - eCAL::Initialize(0, nullptr, "core_cpp_util"); + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); - std::map service_info_map; + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); + + std::map service_info_map; // create simple service and let it expire { @@ -39,15 +42,18 @@ TEST(core_cpp_util, ServiceExpiration) eCAL::CServiceServer service("foo::service"); service.AddDescription("foo::method", "foo::req_type", "foo::req_desc", "foo::resp_type", "foo::resp_desc"); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // get all services - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); // check size EXPECT_EQ(service_info_map.size(), 1); // check service/method names - std::set service_method_names; - eCAL::Util::GetServiceMethodNames(service_method_names); + std::set service_method_names; + eCAL::Registration::GetServiceMethodNames(service_method_names); EXPECT_EQ(service_method_names.size(), 1); for (const auto& name : service_method_names) { @@ -59,18 +65,18 @@ TEST(core_cpp_util, ServiceExpiration) eCAL::Process::SleepMS(CMN_MONITORING_TIMEOUT_MS); // get all services again, service should not be expired - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); // check size EXPECT_EQ(service_info_map.size(), 1); } // let's unregister - eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // get all services again, all services // should be removed from the map - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); // check size EXPECT_EQ(service_info_map.size(), 0); @@ -79,12 +85,15 @@ TEST(core_cpp_util, ServiceExpiration) eCAL::Finalize(); } -TEST(core_cpp_util, ServiceEqualQualities) +TEST(core_cpp_registration_public, ServiceEqualQualities) { // initialize eCAL API - eCAL::Initialize(0, nullptr, "core_cpp_util"); + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); - std::map service_info_map; + std::map service_info_map; // create 2 services with the same quality of data type information { @@ -92,8 +101,11 @@ TEST(core_cpp_util, ServiceEqualQualities) eCAL::CServiceServer service1("foo::service"); service1.AddDescription("foo::method", "foo::req_type1", "foo::req_desc1", "foo::resp_type1", "foo::resp_desc1"); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // get all services - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); // check size EXPECT_EQ(service_info_map.size(), 1); @@ -102,10 +114,10 @@ TEST(core_cpp_util, ServiceEqualQualities) std::string req_type, resp_type; std::string req_desc, resp_desc; - eCAL::Util::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type1"); EXPECT_EQ(resp_type, "foo::resp_type1"); - eCAL::Util::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc1"); EXPECT_EQ(resp_desc, "foo::resp_desc1"); @@ -115,10 +127,10 @@ TEST(core_cpp_util, ServiceEqualQualities) service2.AddDescription("foo::method", "foo::req_type2", "foo::req_desc2", "foo::resp_type2", "foo::resp_desc2"); // check attributes - eCAL::Util::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type1"); EXPECT_EQ(resp_type, "foo::resp_type1"); - eCAL::Util::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc1"); EXPECT_EQ(resp_desc, "foo::resp_desc1"); @@ -129,7 +141,7 @@ TEST(core_cpp_util, ServiceEqualQualities) eCAL::Process::SleepMS(CMN_MONITORING_TIMEOUT_MS); // get all services again, services should not be expired - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); // check size EXPECT_EQ(service_info_map.size(), 1); @@ -137,21 +149,24 @@ TEST(core_cpp_util, ServiceEqualQualities) // destroy service 1 service1.Destroy(); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // check attributes, service 1 attributes should be replaced by service 2 attributes now - eCAL::Util::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type2"); EXPECT_EQ(resp_type, "foo::resp_type2"); - eCAL::Util::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc2"); EXPECT_EQ(resp_desc, "foo::resp_desc2"); } // let's unregister - eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // get all services again, all services // should be removed from the map - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); // check size EXPECT_EQ(service_info_map.size(), 0); @@ -160,12 +175,15 @@ TEST(core_cpp_util, ServiceEqualQualities) eCAL::Finalize(); } -TEST(core_cpp_util, ServiceDifferentQualities) +TEST(core_cpp_registration_public, ServiceDifferentQualities) { // initialize eCAL API - eCAL::Initialize(0, nullptr, "core_cpp_util"); + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); - std::map service_info_map; + std::map service_info_map; // create 2 services with different qualities of data type information { @@ -173,8 +191,11 @@ TEST(core_cpp_util, ServiceDifferentQualities) eCAL::CServiceServer service1("foo::service"); service1.AddDescription("foo::method", "foo::req_type1", "foo::req_desc1", "", ""); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // get all services - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); // check size EXPECT_EQ(service_info_map.size(), 1); @@ -183,10 +204,10 @@ TEST(core_cpp_util, ServiceDifferentQualities) std::string req_type, resp_type; std::string req_desc, resp_desc; - eCAL::Util::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type1"); EXPECT_EQ(resp_type, ""); - eCAL::Util::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc1"); EXPECT_EQ(resp_desc, ""); @@ -194,11 +215,14 @@ TEST(core_cpp_util, ServiceDifferentQualities) eCAL::CServiceServer service2("foo::service"); service2.AddDescription("foo::method", "foo::req_type2", "foo::req_desc2", "foo::resp_type2", "foo::resp_desc2"); + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + // check attributes, we expect attributes from service 2 here - eCAL::Util::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); + eCAL::Registration::GetServiceTypeNames("foo::service", "foo::method", req_type, resp_type); EXPECT_EQ(req_type, "foo::req_type2"); EXPECT_EQ(resp_type, "foo::resp_type2"); - eCAL::Util::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); + eCAL::Registration::GetServiceDescription("foo::service", "foo::method", req_desc, resp_desc); EXPECT_EQ(req_desc, "foo::req_desc2"); EXPECT_EQ(resp_desc, "foo::resp_desc2"); @@ -207,11 +231,11 @@ TEST(core_cpp_util, ServiceDifferentQualities) } // let's unregister - eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // get all services again, all services // should be removed from the map - eCAL::Util::GetServices(service_info_map); + eCAL::Registration::GetServices(service_info_map); // check size EXPECT_EQ(service_info_map.size(), 0); @@ -219,3 +243,49 @@ TEST(core_cpp_util, ServiceDifferentQualities) // finalize eCAL API eCAL::Finalize(); } + +TEST(core_cpp_registration_public, GetServiceIDs) +{ + // initialize eCAL API + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); + + // create simple server + { + // create server + eCAL::CServiceServer service("foo::service"); + + // add description + eCAL::SServiceMethodInformation service_method_info; + service_method_info.request_type.name = "foo::req_type"; + service_method_info.request_type.descriptor = "foo::req_desc"; + service_method_info.response_type.name = "foo::resp_type"; + service_method_info.response_type.descriptor = "foo::resp_desc"; + + service.AddDescription("foo::method", + service_method_info.request_type.name, + service_method_info.request_type.descriptor, + service_method_info.response_type.name, + service_method_info.response_type.descriptor); + + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + + // get server + auto id_set = eCAL::Registration::GetServiceIDs(); + EXPECT_EQ(1, id_set.size()); + if (id_set.size() > 0) + { + eCAL::Registration::SQualityServiceInfo info; + EXPECT_TRUE(eCAL::Registration::GetServiceInfo(*id_set.begin(), info)); + + // check service/method names + EXPECT_EQ(service_method_info, info.info); + } + } + + // finalize eCAL API + eCAL::Finalize(); +} diff --git a/ecal/tests/cpp/registration_test_public/src/registration_gettopics.cpp b/ecal/tests/cpp/registration_test_public/src/registration_gettopics.cpp new file mode 100644 index 0000000000..2f65bc9143 --- /dev/null +++ b/ecal/tests/cpp/registration_test_public/src/registration_gettopics.cpp @@ -0,0 +1,269 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include + +#include +#include +#include +#include + +enum { + CMN_MONITORING_TIMEOUT_MS = (5000 + 100), + CMN_REGISTRATION_REFRESH_MS = (1000) +}; + +TEST(core_cpp_registration_public, GetTopics) +{ + // initialize eCAL API + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); + + std::map topic_info_map; + + // create and check a few pub/sub entities + { + eCAL::SDataTypeInformation info_A1 { "typeA1" ,"", "descA1" }; + eCAL::SDataTypeInformation info_A1_2{ "typeA1.2","", "descA1.2" }; + eCAL::SDataTypeInformation info_A2 { "typeA2" ,"", "descA2" }; + eCAL::SDataTypeInformation info_A3 { "typeA3" ,"", "descA3" }; + + eCAL::SDataTypeInformation info_B1 { "typeB1" ,"", "descB1" }; + eCAL::SDataTypeInformation info_B1_2{ "typeB1.2","", "descB1.2" }; + eCAL::SDataTypeInformation info_B2 { "typeB2" ,"", "descB2" }; + + // create 3 publisher + eCAL::CPublisher pub1("A1", info_A1); + eCAL::CPublisher pub2("A2", info_A2); + eCAL::CPublisher pub3("A3", info_A3); + + // create a missmatching publisher + // this should trigger a warning but not increase map size + eCAL::CPublisher pub12("A1", info_A1_2); + + // create 2 subscriber + eCAL::CSubscriber sub1("B1", info_B1); + eCAL::CSubscriber sub2("B2", info_B2); + + // create a missmatching subscriber + // this should trigger a warning but not increase map size + eCAL::CSubscriber sub12("B1", info_B1_2); + + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + + // get all topics + eCAL::Registration::GetTopics(topic_info_map); + + // check size + EXPECT_EQ(topic_info_map.size(), 5); + + // check types and descriptions + for (auto& topic_info : topic_info_map) + { + eCAL::SDataTypeInformation registration_topic_info; + eCAL::Registration::GetTopicDataTypeInformation(topic_info.first, registration_topic_info); + eCAL::SDataTypeInformation expected_topic_info{ "type" + topic_info.first, "", "desc" + topic_info.first }; + EXPECT_EQ(registration_topic_info, expected_topic_info); + } + + // wait a monitoring timeout long, + eCAL::Process::SleepMS(CMN_MONITORING_TIMEOUT_MS); + + // the topics should not be expired + eCAL::Registration::GetTopics(topic_info_map); + + // check size + EXPECT_EQ(topic_info_map.size(), 5); + + // now destroy publisher pub1 and subscriber sub1 + // the entities pub12 and sub12 should replace them + // by overwriting their type names and descriptions + pub1.Destroy(); + sub1.Destroy(); + + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + + // size should be 5 again (because of pub12 and sub12 should have replaced pub1 and sub1 attributes now) + EXPECT_EQ(topic_info_map.size(), 5); + + // check overwritten attributes + { + eCAL::SDataTypeInformation registration_topic_info; + EXPECT_EQ(true, eCAL::Registration::GetTopicDataTypeInformation("A1", registration_topic_info)); + EXPECT_EQ(registration_topic_info, info_A1_2); + } + { + eCAL::SDataTypeInformation registration_topic_info; + EXPECT_EQ(true, eCAL::Registration::GetTopicDataTypeInformation("B1", registration_topic_info)); + EXPECT_EQ(registration_topic_info, info_B1_2); + } + } + + // let's unregister + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + + // get all topics again, now all topics + // should be removed from the map + eCAL::Registration::GetTopics(topic_info_map); + + // check size + EXPECT_EQ(topic_info_map.size(), 0); + + // finalize eCAL API + eCAL::Finalize(); +} + +// This test creates a reall big number of publishers. +// It then checks, if they have all been seen using GetTopics() +// And the count is back to 0 upon completion. +TEST(core_cpp_registration_public, GetTopicsParallel) +{ + constexpr const int max_publisher_count(2000); + constexpr const int waiting_time_thread(4000); + constexpr const int parallel_threads(1); + + std::atomic testing_completed{ false }; + + // initialize eCAL API + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); + + auto create_publishers = [&]() { + std::string topic_name = "Test.ParallelUtilFunctions"; + std::atomic call_back_count{ 0 }; + { + std::vector> publishers; + for (int pub_count = 0; pub_count < max_publisher_count; pub_count++) { + std::unique_ptr publisher = std::make_unique(topic_name + std::to_string(pub_count)); + publishers.push_back(std::move(publisher)); + } + std::this_thread::sleep_for(std::chrono::milliseconds(waiting_time_thread)); + } + std::this_thread::sleep_for(std::chrono::milliseconds(waiting_time_thread)); + testing_completed = true; + }; + + auto get_topics_from_ecal = [&]() { + size_t number_publishers_seen = 0; + size_t max_number_publishers_seen = 0; + + std::set tmp_topic_names; + std::map topics; + + do { + eCAL::Registration::GetTopicNames(tmp_topic_names); + eCAL::Registration::GetTopics(topics); + + number_publishers_seen = tmp_topic_names.size(); + max_number_publishers_seen = std::max(max_number_publishers_seen, number_publishers_seen); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } while (!testing_completed); + + EXPECT_EQ(number_publishers_seen, 0); + EXPECT_EQ(max_number_publishers_seen, max_publisher_count); + }; + + std::vector threads_container; + threads_container.push_back(std::thread(create_publishers)); + + for (size_t i = 0; i < parallel_threads; i++) { + threads_container.push_back(std::thread(get_topics_from_ecal)); + } + + for (auto& th : threads_container) { + th.join(); + } + + // finalize eCAL API + eCAL::Finalize(); +} + +TEST(core_cpp_registration_public, GetTopicIDs) +{ + // initialize eCAL API + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); + + // enable loop back communication in the same process + eCAL::Util::EnableLoopback(true); + + // create and check a few pub/sub entities + { + eCAL::SDataTypeInformation info_A1{ "typeA1" ,"", "descA1" }; + eCAL::SDataTypeInformation info_A2{ "typeA2" ,"", "descA2" }; + eCAL::SDataTypeInformation info_A3{ "typeA3" ,"", "descA3" }; + + eCAL::SDataTypeInformation info_B1{ "typeB1" ,"", "descB1" }; + eCAL::SDataTypeInformation info_B2{ "typeB2" ,"", "descB2" }; + + // create 3 publisher + eCAL::CPublisher pub1("A1", info_A1); + eCAL::CPublisher pub2("A2", info_A2); + eCAL::CPublisher pub3("A3", info_A3); + + // create 2 subscriber + eCAL::CSubscriber sub1("B1", info_B1); + eCAL::CSubscriber sub2("B2", info_B2); + + // let's register + eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); + + // get publisher + { + auto id_set = eCAL::Registration::GetPublisherIDs(); + EXPECT_EQ(3, id_set.size()); + + // check publisher datatype information + for (const auto& id : id_set) + { + eCAL::Registration::SQualityTopicInfo info; + EXPECT_TRUE(eCAL::Registration::GetPublisherInfo(id, info)); + EXPECT_EQ(std::string("type") + id.topic_name, info.info.name); + EXPECT_EQ("", info.info.encoding); + EXPECT_EQ(std::string("desc") + id.topic_name, info.info.descriptor); + } + } + + // get subscriber + { + auto id_set = eCAL::Registration::GetSubscriberIDs(); + EXPECT_EQ(2, id_set.size()); + + // check subscriber datatype information + for (const auto& id : id_set) + { + eCAL::Registration::SQualityTopicInfo info; + EXPECT_TRUE(eCAL::Registration::GetSubscriberInfo(id, info)); + EXPECT_EQ(std::string("type") + id.topic_name, info.info.name); + EXPECT_EQ("", info.info.encoding); + EXPECT_EQ(std::string("desc") + id.topic_name, info.info.descriptor); + } + } + } + + // finalize eCAL API + eCAL::Finalize(); +} diff --git a/ecal/tests/cpp/serialization_test/src/registration_compare.cpp b/ecal/tests/cpp/serialization_test/src/registration_compare.cpp index f4eb4f8ecc..39682f868e 100644 --- a/ecal/tests/cpp/serialization_test/src/registration_compare.cpp +++ b/ecal/tests/cpp/serialization_test/src/registration_compare.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,9 +37,7 @@ namespace eCAL bool CompareProcess(const Process& process1, const Process& process2) { return (process1.rclock == process2.rclock) && - (process1.hname == process2.hname) && (process1.hgname == process2.hgname) && - (process1.pid == process2.pid) && (process1.pname == process2.pname) && (process1.uname == process2.uname) && (process1.pparam == process2.pparam) && @@ -76,12 +74,9 @@ namespace eCAL bool CompareService(const Service::Service& service1, const Service::Service& service2) { return (service1.rclock == service2.rclock) && - (service1.hname == service2.hname) && (service1.pname == service2.pname) && (service1.uname == service2.uname) && - (service1.pid == service2.pid) && (service1.sname == service2.sname) && - (service1.sid == service2.sid) && CompareMethods(service1.methods, service2.methods) && (service1.version == service2.version) && (service1.tcp_port_v0 == service2.tcp_port_v0) && @@ -92,12 +87,9 @@ namespace eCAL bool CompareClient(const Service::Client& client1, const Service::Client& client2) { return (client1.rclock == client2.rclock) && - (client1.hname == client2.hname) && (client1.pname == client2.pname) && (client1.uname == client2.uname) && - (client1.pid == client2.pid) && (client1.sname == client2.sname) && - (client1.sid == client2.sid) && CompareMethods(client1.methods, client2.methods) && (client1.version == client2.version); } @@ -148,7 +140,8 @@ namespace eCAL // compare TLayer objects for equality return (layer1.type == layer2.type) && (layer1.version == layer2.version) && - (layer1.confirmed == layer2.confirmed) && + (layer1.enabled == layer2.enabled) && + (layer1.active == layer2.active) && CompareConnectionPar(layer1.par_layer, layer2.par_layer); }); } @@ -157,12 +150,9 @@ namespace eCAL bool CompareTopic(const Topic& topic1, const Topic& topic2) { return (topic1.rclock == topic2.rclock) && - (topic1.hname == topic2.hname) && (topic1.hgname == topic2.hgname) && - (topic1.pid == topic2.pid) && (topic1.pname == topic2.pname) && (topic1.uname == topic2.uname) && - (topic1.tid == topic2.tid) && (topic1.tname == topic2.tname) && (topic1.direction == topic2.direction) && CompareDataTypeInformation(topic1.tdatatype, topic2.tdatatype) && @@ -177,11 +167,19 @@ namespace eCAL (topic1.attr == topic2.attr); } + bool CompareIdentifier(const SampleIdentifier& identifier1, const SampleIdentifier& identifier2) + { + return (identifier1.entity_id == identifier2.entity_id) && + (identifier1.process_id == identifier2.process_id) && + (identifier1.host_name == identifier2.host_name); + } + // compare two Registration Sample objects bool CompareRegistrationSamples(const Sample& sample1, const Sample& sample2) { return (sample1.cmd_type == sample2.cmd_type) && (sample1.host.hname == sample2.host.hname) && + CompareIdentifier(sample1.identifier, sample2.identifier) && CompareProcess(sample1.process, sample2.process) && CompareService(sample1.service, sample2.service) && CompareClient(sample1.client, sample2.client) && diff --git a/ecal/tests/cpp/serialization_test/src/registration_generate.cpp b/ecal/tests/cpp/serialization_test/src/registration_generate.cpp index e29a95f6f2..1fd33ece09 100644 --- a/ecal/tests/cpp/serialization_test/src/registration_generate.cpp +++ b/ecal/tests/cpp/serialization_test/src/registration_generate.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,9 @@ * * ========================= eCAL LICENSE ================================= */ +#include "registration_generate.h" -#include "../../serialization/ecal_struct_sample_registration.h" - +#include #include namespace eCAL @@ -46,12 +46,9 @@ namespace eCAL { Service::Service service; service.rclock = rand() % 1000; - service.hname = GenerateString(8); service.pname = GenerateString(10); service.uname = GenerateString(5); - service.pid = rand() % 100; service.sname = GenerateString(8); - service.sid = GenerateString(7); service.methods.push_back(GenerateMethod()); service.methods.push_back(GenerateMethod()); service.version = rand() % 10; @@ -66,12 +63,9 @@ namespace eCAL { Service::Client client; client.rclock = rand() % 1000; - client.hname = GenerateString(8); client.pname = GenerateString(10); client.uname = GenerateString(5); - client.pid = rand() % 100; client.sname = GenerateString(8); - client.sid = GenerateString(7); client.methods.push_back(GenerateMethod()); client.methods.push_back(GenerateMethod()); client.version = rand() % 10; @@ -95,7 +89,8 @@ namespace eCAL TLayer layer; layer.type = static_cast(rand() % (tl_all + 1)); layer.version = rand() % 100; - layer.confirmed = rand() % 2 == 1; + layer.enabled = rand() % 2 == 1; + layer.active = rand() % 2 == 1; return layer; } @@ -104,12 +99,9 @@ namespace eCAL { Topic topic; topic.rclock = rand() % 1000; - topic.hname = GenerateString(8); topic.hgname = GenerateString(6); - topic.pid = rand() % 100; topic.pname = GenerateString(10); topic.uname = GenerateString(5); - topic.tid = GenerateString(7); topic.tname = GenerateString(8); topic.direction = GenerateString(5); topic.tdatatype = GenerateDataTypeInformation(); @@ -125,31 +117,73 @@ namespace eCAL return topic; } - // generate Registration Sample - Sample GenerateRegistrationSample() + Process GenerateProcess() + { + Process process; + process.rclock = rand() % 1000; + process.hgname = GenerateString(6); + process.pname = GenerateString(10); + process.uname = GenerateString(5); + process.pparam = GenerateString(12); + process.state.severity = static_cast(rand() % (proc_sev_failed + 1)); + process.state.severity_level = static_cast(rand() % (proc_sev_level5 + 1)); + process.state.info = GenerateString(10); + process.tsync_state = static_cast(rand() % (tsync_replay + 1)); + process.tsync_mod_name = GenerateString(6); + process.component_init_state = rand() % 5; + process.component_init_info = GenerateString(8); + process.ecal_runtime_version = GenerateString(5); + return process; + } + + SampleIdentifier GenerateIdentifier() + { + SampleIdentifier identifier; + identifier.entity_id = GenerateString(7); + identifier.process_id = rand() % 100; + identifier.host_name = GenerateString(8); + return identifier; + } + + Sample GenerateProcessSample() { Sample sample; - sample.cmd_type = static_cast(rand() % (bct_unreg_client + 1)); - sample.host.hname = GenerateString(8); - sample.process.rclock = rand() % 1000; - sample.process.hname = GenerateString(8); - sample.process.hgname = GenerateString(6); - sample.process.pid = rand() % 100; - sample.process.pname = GenerateString(10); - sample.process.uname = GenerateString(5); - sample.process.pparam = GenerateString(12); - sample.process.state.severity = static_cast(rand() % (proc_sev_failed + 1)); - sample.process.state.severity_level = static_cast(rand() % (proc_sev_level5 + 1)); - sample.process.state.info = GenerateString(10); - sample.process.tsync_state = static_cast(rand() % (tsync_replay + 1)); - sample.process.tsync_mod_name = GenerateString(6); - sample.process.component_init_state = rand() % 5; - sample.process.component_init_info = GenerateString(8); - sample.process.ecal_runtime_version = GenerateString(5); - sample.service = GenerateService(); - sample.client = GenerateClient(); - sample.topic = GenerateTopic(); + sample.cmd_type = bct_reg_process; + sample.host.hname = GenerateString(8); + sample.identifier = GenerateIdentifier(); + // Process samples don't have an id internally, hence it must be 0. + sample.identifier.entity_id = ""; + sample.process = GenerateProcess(); + return sample; + } + Sample GenerateTopicSample() + { + Sample sample; + sample.cmd_type = bct_reg_publisher; + sample.host.hname = GenerateString(8); + sample.identifier = GenerateIdentifier(); + sample.topic = GenerateTopic(); + return sample; + } + + Sample GenerateServiceSample() + { + Sample sample; + sample.cmd_type = bct_reg_service; + sample.host.hname = GenerateString(8); + sample.identifier = GenerateIdentifier(); + sample.service = GenerateService(); + return sample; + } + + Sample GenerateClientSample() + { + Sample sample; + sample.cmd_type = bct_reg_client; + sample.host.hname = GenerateString(8); + sample.identifier = GenerateIdentifier(); + sample.client = GenerateClient(); return sample; } } diff --git a/ecal/tests/cpp/serialization_test/src/registration_generate.h b/ecal/tests/cpp/serialization_test/src/registration_generate.h new file mode 100644 index 0000000000..64d1a765cf --- /dev/null +++ b/ecal/tests/cpp/serialization_test/src/registration_generate.h @@ -0,0 +1,30 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ +#include "serialization/ecal_struct_sample_registration.h" + +namespace eCAL +{ + namespace Registration + { + Sample GenerateProcessSample(); + Sample GenerateTopicSample(); + Sample GenerateServiceSample(); + Sample GenerateClientSample(); + } +} \ No newline at end of file diff --git a/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp b/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp index b279884ada..82c9c1bd38 100644 --- a/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp +++ b/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ */ #include "../../serialization/ecal_serialize_sample_registration.h" +#include "registration_generate.h" #include @@ -25,67 +26,86 @@ namespace eCAL { namespace Registration { - Sample GenerateRegistrationSample(); bool CompareRegistrationSamples(const Sample& sample1, const Sample& sample2); - TEST(core_cpp_serialization, Registration2String) - { - Sample sample_in = GenerateRegistrationSample(); + class RegistrationSampleSerializationTest : public ::testing::Test { + public: - std::string sample_buffer; - ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + RegistrationSampleSerializationTest() { + samples.push_back(GenerateProcessSample()); + samples.push_back(GenerateTopicSample()); + samples.push_back(GenerateServiceSample()); + samples.push_back(GenerateClientSample()); + } - Sample sample_out; - ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + protected: + std::vector samples; + }; + using core_cpp_registration_serialization = RegistrationSampleSerializationTest; - ASSERT_TRUE(CompareRegistrationSamples(sample_in, sample_out)); - } - TEST(core_cpp_serialization, Registration2Vector) + TEST_F(core_cpp_registration_serialization, Registration2String) { - Sample sample_in = GenerateRegistrationSample(); + for (const auto& sample_in : samples) + { + std::string sample_buffer; + EXPECT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); - std::vector sample_buffer; - ASSERT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); + Sample sample_out; + EXPECT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + + EXPECT_TRUE(CompareRegistrationSamples(sample_in, sample_out)); + } + } + + TEST_F(core_cpp_registration_serialization, Registration2Vector) + { + for (const auto& sample_in : samples) + { + std::vector sample_buffer; + EXPECT_TRUE(SerializeToBuffer(sample_in, sample_buffer)); - Sample sample_out; - ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); + Sample sample_out; + EXPECT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_out)); - ASSERT_TRUE(CompareRegistrationSamples(sample_in, sample_out)); + EXPECT_TRUE(CompareRegistrationSamples(sample_in, sample_out)); + } } - TEST(core_cpp_serialization, RegistrationList2String) + TEST_F(core_cpp_registration_serialization, RegistrationList2String) { SampleList sample_list_in; - sample_list_in.samples.push_back(GenerateRegistrationSample()); - sample_list_in.samples.push_back(GenerateRegistrationSample()); - sample_list_in.samples.push_back(GenerateRegistrationSample()); + for (const auto& sample_in : samples) + { + sample_list_in.samples.push_back(sample_in); + } std::string sample_buffer; - ASSERT_TRUE(SerializeToBuffer(sample_list_in, sample_buffer)); + EXPECT_TRUE(SerializeToBuffer(sample_list_in, sample_buffer)); SampleList sample_list_out; - ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); + EXPECT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); - ASSERT_TRUE(sample_list_in.samples.size() == sample_list_out.samples.size()); - ASSERT_TRUE(std::equal(sample_list_in.samples.begin(), sample_list_in.samples.end(), sample_list_out.samples.begin(), CompareRegistrationSamples)); + EXPECT_TRUE(sample_list_in.samples.size() == sample_list_out.samples.size()); + EXPECT_TRUE(std::equal(sample_list_in.samples.begin(), sample_list_in.samples.end(), sample_list_out.samples.begin(), CompareRegistrationSamples)); } - TEST(core_cpp_serialization, RegistrationList2Vector) + TEST_F(core_cpp_registration_serialization, RegistrationList2Vector) { SampleList sample_list_in; - sample_list_in.samples.push_back(GenerateRegistrationSample()); - sample_list_in.samples.push_back(GenerateRegistrationSample()); - sample_list_in.samples.push_back(GenerateRegistrationSample()); + for (const auto& sample_in : samples) + { + sample_list_in.samples.push_back(sample_in); + } std::vector sample_buffer; - ASSERT_TRUE(SerializeToBuffer(sample_list_in, sample_buffer)); + EXPECT_TRUE(SerializeToBuffer(sample_list_in, sample_buffer)); SampleList sample_list_out; - ASSERT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); + EXPECT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); - ASSERT_TRUE(sample_list_in.samples.size() == sample_list_out.samples.size()); - ASSERT_TRUE(std::equal(sample_list_in.samples.begin(), sample_list_in.samples.end(), sample_list_out.samples.begin(), CompareRegistrationSamples)); + EXPECT_TRUE(sample_list_in.samples.size() == sample_list_out.samples.size()); + EXPECT_TRUE(std::equal(sample_list_in.samples.begin(), sample_list_in.samples.end(), sample_list_out.samples.begin(), CompareRegistrationSamples)); } } } diff --git a/ecal/tests/cpp/util_test/CMakeLists.txt b/ecal/tests/cpp/util_test/CMakeLists.txt index 4d39f66d1d..d4e8b901b9 100644 --- a/ecal/tests/cpp/util_test/CMakeLists.txt +++ b/ecal/tests/cpp/util_test/CMakeLists.txt @@ -1,6 +1,6 @@ # ========================= eCAL LICENSE ================================= # -# Copyright (C) 2016 - 2019 Continental Corporation +# Copyright (C) 2016 - 2024 Continental Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,22 +21,7 @@ project(test_util) find_package(Threads REQUIRED) find_package(GTest REQUIRED) -if(ECAL_CORE_PUBLISHER AND ECAL_CORE_SUBSCRIBER) - set(util_test_topics_src - src/util_gettopics.cpp - ) -endif() - -if(ECAL_CORE_SERVICE) - set(util_test_service_src - src/util_getclients.cpp - src/util_getservices.cpp - ) -endif() - set(util_test_src - ${util_test_topics_src} - ${util_test_service_src} src/util_test.cpp ) diff --git a/ecal/tests/cpp/util_test/src/util_gettopics.cpp b/ecal/tests/cpp/util_test/src/util_gettopics.cpp deleted file mode 100644 index 112234d018..0000000000 --- a/ecal/tests/cpp/util_test/src/util_gettopics.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* ========================= eCAL LICENSE ================================= - * - * Copyright (C) 2016 - 2019 Continental Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ========================= eCAL LICENSE ================================= -*/ - -#include - -#include - -#include -#include -#include -#include - -enum { - CMN_MONITORING_TIMEOUT_MS = (5000 + 100), - CMN_REGISTRATION_REFRESH_MS = (1000 + 100) -}; - -TEST(core_cpp_util, GetTopics) -{ - // initialize eCAL API - eCAL::Initialize(0, nullptr, "core_cpp_util"); - - std::map topic_info_map; - - // create and check a few pub/sub entities - { - eCAL::SDataTypeInformation info_A1 { "typeA1" ,"", "descA1" }; - eCAL::SDataTypeInformation info_A1_2{ "typeA1.2","", "descA1.2" }; - eCAL::SDataTypeInformation info_A2 { "typeA2" ,"", "descA2" }; - eCAL::SDataTypeInformation info_A3 { "typeA3" ,"", "descA3" }; - - eCAL::SDataTypeInformation info_B1 { "typeB1" ,"", "descB1" }; - eCAL::SDataTypeInformation info_B1_2{ "typeB1.2","", "descB1.2" }; - eCAL::SDataTypeInformation info_B2 { "typeB2" ,"", "descB2" }; - - // create 3 publisher - eCAL::CPublisher pub1("A1", info_A1); - eCAL::CPublisher pub2("A2", info_A2); - eCAL::CPublisher pub3("A3", info_A3); - - // create a missmatching publisher - // this should trigger a warning but not increase map size - eCAL::CPublisher pub12("A1", info_A1_2); - - // create 2 subscriber - eCAL::CSubscriber sub1("B1", info_B1); - eCAL::CSubscriber sub2("B2", info_B2); - - // create a missmatching subscriber - // this should trigger a warning but not increase map size - eCAL::CSubscriber sub12("B1", info_B1_2); - - // get all topics - eCAL::Util::GetTopics(topic_info_map); - - // check size - EXPECT_EQ(topic_info_map.size(), 5); - - // check types and descriptions - for (auto& topic_info : topic_info_map) - { - eCAL::SDataTypeInformation utils_topic_info; - eCAL::Util::GetTopicDataTypeInformation(topic_info.first, utils_topic_info); - eCAL::SDataTypeInformation expected_topic_info{ "type" + topic_info.first, "", "desc" + topic_info.first }; - EXPECT_EQ(utils_topic_info, expected_topic_info); - } - - // wait a monitoring timeout long, - eCAL::Process::SleepMS(CMN_MONITORING_TIMEOUT_MS); - - // the topics should not be expired - eCAL::Util::GetTopics(topic_info_map); - - // check size - EXPECT_EQ(topic_info_map.size(), 5); - - // now destroy publisher pub1 and subscriber sub1 - // the entities pub12 and sub12 should replace them - // by overwriting their type names and descriptions - pub1.Destroy(); - sub1.Destroy(); - - // size should be 5 again (because of pub12 and sub12 should have replaced pub1 and sub1 attributes now) - EXPECT_EQ(topic_info_map.size(), 5); - - // check overwritten attributes - { - eCAL::SDataTypeInformation utils_topic_info; - EXPECT_EQ(true, eCAL::Util::GetTopicDataTypeInformation("A1", utils_topic_info)); - EXPECT_EQ(utils_topic_info, info_A1_2); - } - { - eCAL::SDataTypeInformation utils_topic_info; - EXPECT_EQ(true, eCAL::Util::GetTopicDataTypeInformation("B1", utils_topic_info)); - EXPECT_EQ(utils_topic_info, info_B1_2); - } - } - - // let's unregister - eCAL::Process::SleepMS(CMN_REGISTRATION_REFRESH_MS); - - // get all topics again, now all topics - // should be removed from the map - eCAL::Util::GetTopics(topic_info_map); - - // check size - EXPECT_EQ(topic_info_map.size(), 0); - - // finalize eCAL API - eCAL::Finalize(); -} - -TEST(core_cpp_util, GetTopicsParallel) -{ - constexpr const int max_publisher_count(2000); - constexpr const int waiting_time_thread(1000); - constexpr const int parallel_threads(1); - - // initialize eCAL API - eCAL::Initialize(0, nullptr, "core_cpp_util"); - - auto create_publishers = [&]() { - std::string topic_name = "Test.ParallelUtilFunctions"; - std::atomic call_back_count{ 0 }; - - std::vector> publishers; - for (int pub_count = 0; pub_count < max_publisher_count; pub_count++) { - std::unique_ptr publisher = std::make_unique(topic_name + std::to_string(pub_count)); - publishers.push_back(std::move(publisher)); - } - std::this_thread::sleep_for(std::chrono::milliseconds(waiting_time_thread)); - }; - - auto get_topics_from_ecal = [&]() { - size_t found_topics = 0; - std::set tmp_topic_names; - std::map topics; - do { - eCAL::Util::GetTopicNames(tmp_topic_names); - eCAL::Util::GetTopics(topics); - - found_topics = tmp_topic_names.size(); - std::cout << "Number of topics found by ecal: " << found_topics << "\n"; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } while (found_topics < max_publisher_count); - - // do it again until all publishers are deleted - do { - eCAL::Util::GetTopicNames(tmp_topic_names); - eCAL::Util::GetTopics(topics); - - found_topics = tmp_topic_names.size(); - std::cout << "Number of topics found by ecal: " << found_topics << "\n"; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } while (found_topics != 0); - }; - - std::vector threads_container; - threads_container.push_back(std::thread(create_publishers)); - - for (size_t i = 0; i < parallel_threads; i++) { - threads_container.push_back(std::thread(get_topics_from_ecal)); - } - - for (auto& th : threads_container) { - th.join(); - } - - std::set final_topic_names; - std::map final_topics; - eCAL::Util::GetTopicNames(final_topic_names); - eCAL::Util::GetTopics(final_topics); - - EXPECT_EQ(final_topic_names.size(), 0); - EXPECT_EQ(final_topics.size(), 0); - - // finalize eCAL API - eCAL::Finalize(); -} diff --git a/ecal/tests/cpp/util_test/src/util_test.cpp b/ecal/tests/cpp/util_test/src/util_test.cpp index abaf84d48f..5936dbf459 100644 --- a/ecal/tests/cpp/util_test/src/util_test.cpp +++ b/ecal/tests/cpp/util_test/src/util_test.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -194,3 +194,56 @@ TEST(core_cpp_util, Freq_ResettableFrequencyCalculator) } } } + + +struct command +{ + std::string command_type; + long long time_point; +}; + +std::vector commands = +{ +command{"getting frequency", 0 }, +command{"ticking", 500 }, +command{"getting frequency", 1000 }, +command{"ticking", 1500 }, +command{"getting frequency", 2000 }, +command{"ticking", 2500 }, +command{"getting frequency", 3000 }, +command{"ticking", 3998 }, +command{"getting frequency", 4000 }, +command{"ticking", 4002 }, +command{"getting frequency", 5000 }, +command{"ticking", 5998 }, +command{"getting frequency", 6000 }, +}; + +TEST(core_cpp_util, Freq_NoZeroFrequency) +{ + eCAL::ResettableFrequencyCalculator calculator(3.0f); + int i = 0; + + for (const auto& command : commands) + { + std::chrono::steady_clock::time_point time(std::chrono::milliseconds(command.time_point)); + + if (command.command_type == "ticking") + { + calculator.addTick(time); + } + else if (command.command_type == "getting frequency") + { + auto frequency = calculator.getFrequency(time); + if (i > 1) + { + ASSERT_GT(frequency, 0.0) << "Iteration " << i; + } + + ++i; + } + else + { + } + } +} diff --git a/lang/python/core/src/ecal_clang.cpp b/lang/python/core/src/ecal_clang.cpp index 420246dc07..98d681344d 100644 --- a/lang/python/core/src/ecal_clang.cpp +++ b/lang/python/core/src/ecal_clang.cpp @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -176,7 +176,7 @@ void ecal_enable_loopback(const int state_) bool ecal_get_type_name(const char* topic_name_, const char** topic_type_, int* topic_type_len_) { eCAL::SDataTypeInformation topic_info; - bool ret = eCAL::Util::GetTopicDataTypeInformation(topic_name_, topic_info); + bool ret = eCAL::Registration::GetTopicDataTypeInformation(topic_name_, topic_info); if(ret) { std::string topic_type_s = topic_info.name; @@ -204,7 +204,7 @@ bool ecal_get_type_name(const char* topic_name_, const char** topic_type_, int* bool ecal_get_type_encoding(const char* topic_name_, const char** topic_encoding_, int* topic_encoding_len_) { eCAL::SDataTypeInformation topic_info; - bool ret = eCAL::Util::GetTopicDataTypeInformation(topic_name_, topic_info); + bool ret = eCAL::Registration::GetTopicDataTypeInformation(topic_name_, topic_info); if (ret) { std::string topic_encoding_s = topic_info.encoding; @@ -232,7 +232,7 @@ bool ecal_get_type_encoding(const char* topic_name_, const char** topic_encoding bool ecal_get_description(const char* topic_name_, const char** topic_desc_, int* topic_desc_len_) { eCAL::SDataTypeInformation topic_info; - bool ret = eCAL::Util::GetTopicDataTypeInformation(topic_name_, topic_info); + bool ret = eCAL::Registration::GetTopicDataTypeInformation(topic_name_, topic_info); if(ret) { std::string topic_desc_s = topic_info.descriptor; diff --git a/lang/python/core/src/ecal_wrap.cxx b/lang/python/core/src/ecal_wrap.cxx index 4066735b66..f94cb983d7 100644 --- a/lang/python/core/src/ecal_wrap.cxx +++ b/lang/python/core/src/ecal_wrap.cxx @@ -1,6 +1,6 @@ /* ========================= eCAL LICENSE ================================= * - * Copyright (C) 2016 - 2019 Continental Corporation + * Copyright (C) 2016 - 2024 Continental Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1051,6 +1051,9 @@ namespace val = Py_BuildValue("s", topic.hname.c_str()); PyDict_SetItemString(topicDict, "hname", val); Py_DECREF(val); + val = Py_BuildValue("s", topic.hgname.c_str()); + PyDict_SetItemString(topicDict, "hgname", val); Py_DECREF(val); + val = Py_BuildValue("i", topic.pid); PyDict_SetItemString(topicDict, "pid", val); Py_DECREF(val); @@ -1069,14 +1072,32 @@ namespace val = Py_BuildValue("s", topic.direction.c_str()); PyDict_SetItemString(topicDict, "direction", val); Py_DECREF(val); + // TODO: SDataTypeInformation tdatatype + + // TODO: std::vector tlayer + val = Py_BuildValue("i", topic.tsize); PyDict_SetItemString(topicDict, "tsize", val); Py_DECREF(val); + val = Py_BuildValue("connections_loc", topic.tsize); + PyDict_SetItemString(topicDict, "connections_loc", val); Py_DECREF(val); + + val = Py_BuildValue("connections_ext", topic.tsize); + PyDict_SetItemString(topicDict, "connections_ext", val); Py_DECREF(val); + + val = Py_BuildValue("message_drops", topic.tsize); + PyDict_SetItemString(topicDict, "message_drops", val); Py_DECREF(val); + + val = Py_BuildValue("i", topic.did); + PyDict_SetItemString(topicDict, "did", val); Py_DECREF(val); + val = Py_BuildValue("i", topic.dclock); PyDict_SetItemString(topicDict, "dclock", val); Py_DECREF(val); val = Py_BuildValue("i", topic.dfreq); PyDict_SetItemString(topicDict, "dfreq", val); Py_DECREF(val); + + // TODO: std::map attr } } } @@ -1106,6 +1127,9 @@ PyObject* mon_monitoring(PyObject* /*self*/, PyObject* /*args*/) val = Py_BuildValue("s", process.hname.c_str()); PyDict_SetItemString(processDict, "hname", val); Py_DECREF(val); + val = Py_BuildValue("s", process.hgname.c_str()); + PyDict_SetItemString(processDict, "hgname", val); Py_DECREF(val); + val = Py_BuildValue("i", process.pid); PyDict_SetItemString(processDict, "pid", val); Py_DECREF(val); @@ -1138,6 +1162,9 @@ PyObject* mon_monitoring(PyObject* /*self*/, PyObject* /*args*/) val = Py_BuildValue("s", process.component_init_info.c_str()); PyDict_SetItemString(processDict, "component_init_info", val); Py_DECREF(val); + + val = Py_BuildValue("s", process.ecal_runtime_version.c_str()); + PyDict_SetItemString(processDict, "ecal_runtime_version", val); Py_DECREF(val); } } @@ -1172,6 +1199,15 @@ PyObject* mon_monitoring(PyObject* /*self*/, PyObject* /*args*/) val = Py_BuildValue("s", service.sid.c_str()); PyDict_SetItemString(serviceDict, "sid", val); Py_DECREF(val); + val = Py_BuildValue("i", service.version); + PyDict_SetItemString(serviceDict, "version", val); Py_DECREF(val); + + val = Py_BuildValue("i", service.tcp_port_v0); + PyDict_SetItemString(serviceDict, "tcp_port_v0", val); Py_DECREF(val); + + val = Py_BuildValue("i", service.tcp_port_v1); + PyDict_SetItemString(serviceDict, "tcp_port_v1", val); Py_DECREF(val); + PyObject* methodsDict = PyDict_New(); PyDict_SetItemString(serviceDict, "methods", methodsDict); Py_DECREF(methodsDict); @@ -1183,9 +1219,13 @@ PyObject* mon_monitoring(PyObject* /*self*/, PyObject* /*args*/) val = Py_BuildValue("s", method.req_type.c_str()); PyDict_SetItemString(methodsDict, "req_type", val); Py_DECREF(val); + // TODO: std::string req_desc + val = Py_BuildValue("s", method.resp_type.c_str()); PyDict_SetItemString(methodsDict, "resp_type", val); Py_DECREF(val); + // TODO: std::string resp_desc + val = Py_BuildValue("i", method.call_count); PyDict_SetItemString(methodsDict, "call_count", val); Py_DECREF(val); } @@ -1222,6 +1262,31 @@ PyObject* mon_monitoring(PyObject* /*self*/, PyObject* /*args*/) val = Py_BuildValue("s", client.sid.c_str()); PyDict_SetItemString(clientDict, "sid", val); Py_DECREF(val); + + PyObject* methodsDict = PyDict_New(); + PyDict_SetItemString(clientDict, "methods", methodsDict); Py_DECREF(methodsDict); + + for (const auto method : client.methods) + { + val = Py_BuildValue("s", method.mname.c_str()); + PyDict_SetItemString(methodsDict, "mname", val); Py_DECREF(val); + + val = Py_BuildValue("s", method.req_type.c_str()); + PyDict_SetItemString(methodsDict, "req_type", val); Py_DECREF(val); + + // TODO: std::string req_desc + + val = Py_BuildValue("s", method.resp_type.c_str()); + PyDict_SetItemString(methodsDict, "resp_type", val); Py_DECREF(val); + + // TODO: std::string resp_desc + + val = Py_BuildValue("i", method.call_count); + PyDict_SetItemString(methodsDict, "call_count", val); Py_DECREF(val); + } + + val = Py_BuildValue("i", client.version); + PyDict_SetItemString(clientDict, "version", val); Py_DECREF(val); } } diff --git a/lang/python/nanobind_core/samples/minimal_server.py b/lang/python/nanobind_core/samples/minimal_server.py index e6f90011ce..a2ca2e9720 100644 --- a/lang/python/nanobind_core/samples/minimal_server.py +++ b/lang/python/nanobind_core/samples/minimal_server.py @@ -24,8 +24,7 @@ # define the server method "foo" function def foo_req_callback(method_name, req_type, resp_type, request): print("'DemoService' method '{}' called with {}".format(method_name, request)) - #return True #, bytes("thank you for calling foo :-)", "ascii") - return 0, "pong" + return True, bytes("thank you for calling foo :-)", "ascii") # define the server method "ping" function def ping_req_callback(method_name, req_type, resp_type, request): @@ -49,8 +48,7 @@ def main(): server = ecal_core.ServiceServer("DemoService") # define the server methods and connect them to the callbacks - server.add_method_callback("foo", "string", "string", foo_req_callback) - server.add_method_callback("ping", "ping_type", "pong_type", ping_req_callback) + server.add_method_callback("ping", "ping_type", "pong_type", "ping_req_callback", ping_req_callback) # idle while(ecal_core.ok()): diff --git a/lang/python/nanobind_core/src/CMakeLists.txt b/lang/python/nanobind_core/src/CMakeLists.txt index 18f5591d9a..0044be3e72 100644 --- a/lang/python/nanobind_core/src/CMakeLists.txt +++ b/lang/python/nanobind_core/src/CMakeLists.txt @@ -31,6 +31,8 @@ nanobind_add_module( modules/module_server.h modules/module_subscriber.cpp modules/module_subscriber.h + modules/module_subscriber_config.cpp + modules/module_subscriber_config.h modules/module_util.cpp modules/module_util.h @@ -46,6 +48,8 @@ nanobind_add_module( wrappers/wrapper_server.h wrappers/wrapper_subscriber.cpp wrappers/wrapper_subscriber.h + wrappers/wrapper_subscriber_config.cpp + wrappers/wrapper_subscriber_config.h nanobind_core.cpp ) diff --git a/lang/python/nanobind_core/src/modules/module_publisher_config.cpp b/lang/python/nanobind_core/src/modules/module_publisher_config.cpp index a52ac453b0..5e5fc6b574 100644 --- a/lang/python/nanobind_core/src/modules/module_publisher_config.cpp +++ b/lang/python/nanobind_core/src/modules/module_publisher_config.cpp @@ -18,7 +18,7 @@ */ /** - * @brief Add ServiceClient class to nanobind module + * @brief Add Publisher config structs to nanobind module **/ @@ -27,10 +27,30 @@ void AddPublisherConfigStructToModule(nanobind::module_& module) { - // Struct eCAL::SDataTypeInformation - nanobind::class_(module, "PublisherConfiguration"); - // .def(nanobind::init<>()) - // .def_rw("name", &eCAL::CNBDataTypeInformation::name) - // .def_rw("encoding", &eCAL::CNBDataTypeInformation::encoding) - // .def_rw("descriptor", &eCAL::CNBDataTypeInformation::descriptor); -} + nanobind::class_(module, "SHMConfiguration") + .def(nanobind::init<>()) + .def_rw("enable", &eCAL::Publisher::SHM::CNBSHMConfiguration::enable) + .def_rw("zero_copy_mode", &eCAL::Publisher::SHM::CNBSHMConfiguration::zero_copy_mode) + .def_rw("acknowledge_timeout_ms", &eCAL::Publisher::SHM::CNBSHMConfiguration::acknowledge_timeout_ms) + .def_rw("memfile_min_size_bytes", &eCAL::Publisher::SHM::CNBSHMConfiguration::memfile_min_size_bytes) + .def_rw("memfile_reserve_percent", &eCAL::Publisher::SHM::CNBSHMConfiguration::memfile_reserve_percent) + .def_rw("memfile_buffer_count", &eCAL::Publisher::SHM::CNBSHMConfiguration::memfile_buffer_count); + + nanobind::class_(module, "UDPConfiguration") + .def(nanobind::init<>()) + .def_rw("enable", &eCAL::Publisher::UDP::CNBUDPConfiguration::enable) + .def_rw("loopback", &eCAL::Publisher::UDP::CNBUDPConfiguration::loopback) + .def_rw("sndbuf_size_bytes", &eCAL::Publisher::UDP::CNBUDPConfiguration::sndbuf_size_bytes); + + nanobind::class_(module, "TCPConfiguration") + .def(nanobind::init<>()) + .def_rw("enable", &eCAL::Publisher::TCP::CNBTCPConfiguration::enable); + + nanobind::class_(module, "PublisherConfiguration") + .def(nanobind::init<>()) + .def_rw("shm", &eCAL::Publisher::CNBPublisherConfiguration::shm) + .def_rw("udp", &eCAL::Publisher::CNBPublisherConfiguration::udp) + .def_rw("tcp", &eCAL::Publisher::CNBPublisherConfiguration::tcp) + .def_rw("share_topic_type", &eCAL::Publisher::CNBPublisherConfiguration::share_topic_type) + .def_rw("share_topic_description", &eCAL::Publisher::CNBPublisherConfiguration::share_topic_description); +} \ No newline at end of file diff --git a/lang/python/nanobind_core/src/modules/module_publisher_config.h b/lang/python/nanobind_core/src/modules/module_publisher_config.h index 5fbaeac902..48d195c641 100644 --- a/lang/python/nanobind_core/src/modules/module_publisher_config.h +++ b/lang/python/nanobind_core/src/modules/module_publisher_config.h @@ -43,4 +43,4 @@ * * @param module The nanobind module variable **/ -void AddDataTypeInfoStructToModule(nanobind::module_& module); +void AddPublisherConfigStructToModule(nanobind::module_& module); diff --git a/lang/python/nanobind_core/src/modules/module_subscriber_config.cpp b/lang/python/nanobind_core/src/modules/module_subscriber_config.cpp new file mode 100644 index 0000000000..a190a736fb --- /dev/null +++ b/lang/python/nanobind_core/src/modules/module_subscriber_config.cpp @@ -0,0 +1,47 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief Add ServiceClient class to nanobind module +**/ + + +#include +#include + +void AddSubscriberConfigStructToModule(nanobind::module_& module) +{ + nanobind::class_(module, "SHMConfiguration") + .def(nanobind::init<>()) + .def_rw("enable", &eCAL::Subscriber::SHM::CNBSHMConfiguration::enable); + + nanobind::class_(module, "UDPConfiguration") + .def(nanobind::init<>()) + .def_rw("enable", &eCAL::Subscriber::UDP::CNBUDPConfiguration::enable); + + nanobind::class_(module, "TCPConfiguration") + .def(nanobind::init<>()) + .def_rw("enable", &eCAL::Subscriber::TCP::CNBTCPConfiguration::enable); + + nanobind::class_(module, "SubscriberConfiguration") + .def(nanobind::init<>()) + .def_rw("shm", &eCAL::Subscriber::CNBSubscriberConfiguration::shm) + .def_rw("udp", &eCAL::Subscriber::CNBSubscriberConfiguration::udp) + .def_rw("tcp", &eCAL::Subscriber::CNBSubscriberConfiguration::tcp); +} diff --git a/lang/python/nanobind_core/src/modules/module_subscriber_config.h b/lang/python/nanobind_core/src/modules/module_subscriber_config.h new file mode 100644 index 0000000000..a37c858485 --- /dev/null +++ b/lang/python/nanobind_core/src/modules/module_subscriber_config.h @@ -0,0 +1,46 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @file wrapper_subscriber_config.h + * @brief Nanobind wrapper for structs of Subscriber config +**/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * @brief Function to Add Nanobind module + * + * @param module The nanobind module variable +**/ +void AddSubscriberConfigStructToModule(nanobind::module_& module); diff --git a/lang/python/nanobind_core/src/nanobind_core.cpp b/lang/python/nanobind_core/src/nanobind_core.cpp index ef81bd9ca0..ddcc176aa7 100644 --- a/lang/python/nanobind_core/src/nanobind_core.cpp +++ b/lang/python/nanobind_core/src/nanobind_core.cpp @@ -39,8 +39,10 @@ #include #include #include +#include #include #include +#include #include @@ -51,6 +53,8 @@ NB_MODULE(nanobind_core, m) { AddPublisherClassToModule(m); AddClientClassToModule(m); AddServerClassToModule(m); + AddPublisherConfigStructToModule(m); + AddSubscriberConfigStructToModule(m); AddCoreFuncToModule(m); AddUtilFuncToModule(m); diff --git a/lang/python/nanobind_core/src/wrappers/wrapper_datatypeinfo.cpp b/lang/python/nanobind_core/src/wrappers/wrapper_datatypeinfo.cpp index f38f7331d6..3cc3ce51f4 100644 --- a/lang/python/nanobind_core/src/wrappers/wrapper_datatypeinfo.cpp +++ b/lang/python/nanobind_core/src/wrappers/wrapper_datatypeinfo.cpp @@ -37,5 +37,7 @@ namespace eCAL info.name = nb_info.name; info.encoding = nb_info.encoding; info.descriptor = std::string(nb_info.descriptor.c_str(), nb_info.descriptor.size()); + + return info; } } \ No newline at end of file diff --git a/lang/python/nanobind_core/src/wrappers/wrapper_publisher_config.cpp b/lang/python/nanobind_core/src/wrappers/wrapper_publisher_config.cpp index b83f17ec93..5afc555096 100644 --- a/lang/python/nanobind_core/src/wrappers/wrapper_publisher_config.cpp +++ b/lang/python/nanobind_core/src/wrappers/wrapper_publisher_config.cpp @@ -25,9 +25,14 @@ namespace eCAL { - // TODO @Ariff - Publisher::Configuration convert(const CNBPublisherConfigStruct& nb_config) - { - return Publisher::Configuration{}; + Publisher::CNBPublisherConfiguration::CNBPublisherConfiguration() + { + Publisher::Configuration::Configuration(); } + + // TODO @Ariff + // Publisher::Configuration convert(const CNBPublisherConfigStruct& nb_config) + // { + // return Publisher::Configuration{}; + //} } \ No newline at end of file diff --git a/lang/python/nanobind_core/src/wrappers/wrapper_publisher_config.h b/lang/python/nanobind_core/src/wrappers/wrapper_publisher_config.h index 75a56d534b..e59ab13da2 100644 --- a/lang/python/nanobind_core/src/wrappers/wrapper_publisher_config.h +++ b/lang/python/nanobind_core/src/wrappers/wrapper_publisher_config.h @@ -18,8 +18,8 @@ */ /** - * @file wrapper_datatypeinfo.h - * @brief Nanobind wrapper SDataTypeInformation struct + * @file wrapper_publisher_config.h + * @brief Nanobind wrapper for structs of Publisher config **/ #pragma once @@ -28,23 +28,58 @@ #include #include #include +#include +#include #include #include namespace eCAL { - // TODO @Ariff - class CNBPublisherConfigStruct + namespace Publisher { - }; - - /** - * @brief Convert function for SDataTypeInformation - * - * @param nb_info CNBDataTypeInformation struct - * - * @return SDataTypeInformation struct after convertion - **/ - Publisher::Configuration convert(const CNBPublisherConfigStruct& nb_config); -} \ No newline at end of file + namespace SHM + { + struct CNBSHMConfiguration + { + bool enable; //!< enable layer + bool zero_copy_mode; //!< enable zero copy shared memory transport mode + unsigned int acknowledge_timeout_ms; /*!< force connected subscribers to send acknowledge event after processing the message + the publisher send call is blocked on this event with this timeout (0 == no handshake) */ + Types::ConstrainedInteger<4096, 4096> memfile_min_size_bytes; //!< default memory file size for new publisher + Types::ConstrainedInteger<50, 1, 100> memfile_reserve_percent; //!< dynamic file size reserve before recreating memory file if topic size changes + Types::ConstrainedInteger<1, 1> memfile_buffer_count; //!< maximum number of used buffers (needs to be greater than 1, default = 1) + }; + } + + namespace UDP + { + struct CNBUDPConfiguration + { + bool enable; //!< enable layer + bool loopback; //!< enable to receive udp messages on the same local machine + Types::ConstrainedInteger<5242880, 1024> sndbuf_size_bytes; //!< udp send buffer size in bytes (default 5MB) + }; + } + + namespace TCP + { + struct CNBTCPConfiguration + { + bool enable; //!< enable layer + }; + } + + struct CNBPublisherConfiguration + { + CNBPublisherConfiguration(); + + SHM::Configuration shm; + UDP::Configuration udp; + TCP::Configuration tcp; + + bool share_topic_type; //!< share topic type via registration + bool share_topic_description; //!< share topic description via registration + }; + } +} diff --git a/lang/python/nanobind_core/src/wrappers/wrapper_server.cpp b/lang/python/nanobind_core/src/wrappers/wrapper_server.cpp index 1d5b985535..d18e409846 100644 --- a/lang/python/nanobind_core/src/wrappers/wrapper_server.cpp +++ b/lang/python/nanobind_core/src/wrappers/wrapper_server.cpp @@ -36,14 +36,14 @@ namespace eCAL CNBSrvServer::CNBSrvServer(const std::string& service_name) : CServiceServer(service_name) { } - bool CNBSrvServer::WrapAddMethodCB(const std::string& nb_method, const std::string& nb_req_type, const std::string& nb_resp_type, nanobind::callable callback_) + bool CNBSrvServer::WrapAddMethodCB(const std::string& nb_method, const std::string& nb_req_type, const std::string& nb_resp_type, const std::string& nb_request, nanobind::callable callback_) { assert(IsConnected()); { std::lock_guard callback_lock(m_python_method_callback_mutex); m_python_method_callback = callback_; } - auto Servercallback = std::bind(&CNBSrvServer::MethodCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5); + auto const Servercallback = std::bind(&CNBSrvServer::MethodCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5); return(CNBSrvServer::AddMethodCallback(nb_method, nb_req_type, nb_resp_type, Servercallback)); } @@ -68,11 +68,13 @@ namespace eCAL try { nanobind::gil_scoped_acquire g2; - auto result = fn_callback(method_, req_type_, resp_type_, request); + nanobind::callable func_call; + auto result = fn_callback(method_, req_type_, resp_type_, func_call); // do some check if object holds a tuple if (!result.is_type) nanobind::tuple result_tuple = nanobind::cast(result); - response = nanobind::cast(result[1]); - int nb_int = nanobind::cast(result[0]); + response = nanobind::cast(result_tuple[1]); + // request = nanobind::cast(result_tuple[2]); + int nb_int = nanobind::cast(result_tuple[0]); return nb_int; } catch (const nanobind::python_error& e) { diff --git a/lang/python/nanobind_core/src/wrappers/wrapper_server.h b/lang/python/nanobind_core/src/wrappers/wrapper_server.h index 9f8117abe1..f879c12f70 100644 --- a/lang/python/nanobind_core/src/wrappers/wrapper_server.h +++ b/lang/python/nanobind_core/src/wrappers/wrapper_server.h @@ -57,11 +57,12 @@ namespace eCAL * @param nb_method Service method name * @param nb_req_type Service method request type. * @param nb_resp_type Service method response type. + * @param nb_request Name of request * @param callback_ The callback function to add. * * @return True if succeeded, false if not. **/ - bool WrapAddMethodCB(const std::string& nb_method, const std::string& nb_req_type, const std::string& nb_resp_type, nanobind::callable callback_); + bool WrapAddMethodCB(const std::string& nb_method, const std::string& nb_req_type, const std::string& nb_resp_type, const std::string& nb_request, nanobind::callable callback_); /** * @brief Wrapper for Add callback function for service client events. diff --git a/lang/python/nanobind_core/src/wrappers/wrapper_subscriber_config.cpp b/lang/python/nanobind_core/src/wrappers/wrapper_subscriber_config.cpp new file mode 100644 index 0000000000..cdd66228d5 --- /dev/null +++ b/lang/python/nanobind_core/src/wrappers/wrapper_subscriber_config.cpp @@ -0,0 +1,32 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @brief subscriber configuration, adapted for Nanobind +**/ + +#include + +namespace eCAL +{ + Subscriber::CNBSubscriberConfiguration::CNBSubscriberConfiguration() + { + Subscriber::Configuration::Configuration(); + } +} diff --git a/lang/python/nanobind_core/src/wrappers/wrapper_subscriber_config.h b/lang/python/nanobind_core/src/wrappers/wrapper_subscriber_config.h new file mode 100644 index 0000000000..453c9efa44 --- /dev/null +++ b/lang/python/nanobind_core/src/wrappers/wrapper_subscriber_config.h @@ -0,0 +1,74 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2024 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +/** + * @file wrapper_publisher_config.h + * @brief Nanobind wrapper for structs of Publisher config +**/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace eCAL +{ + namespace Subscriber + { + namespace SHM + { + struct CNBSHMConfiguration + { + bool enable; //!< enable layer + }; + } + + namespace UDP + { + struct CNBUDPConfiguration + { + bool enable; //!< enable layer + }; + } + + namespace TCP + { + struct CNBTCPConfiguration + { + bool enable; //!< enable layer + }; + } + + struct CNBSubscriberConfiguration + { + CNBSubscriberConfiguration(); + + SHM::Configuration shm; + UDP::Configuration udp; + TCP::Configuration tcp; + }; + } +} diff --git a/pyproject.toml b/pyproject.toml index fd6a1605c3..4f69e2e26d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,7 +100,6 @@ ECAL_THIRDPARTY_BUILD_FTXUI = "OFF" ECAL_THIRDPARTY_BUILD_SPDLOG = "OFF" ECAL_THIRDPARTY_BUILD_TERMCOLOR = "OFF" ECAL_THIRDPARTY_BUILD_TINYXML2 = "OFF" -ECAL_THIRDPARTY_BUILD_YAML-CPP = "OFF" ECAL_THIRDPARTY_BUILD_CURL = "OFF" ECAL_THIRDPARTY_BUILD_HDF5 = "ON" # Stop HDF5 trying to export eCALCoreTargets due to the hack diff --git a/thirdparty/yaml-cpp/compatibility-yaml-cpp.cmake b/thirdparty/yaml-cpp/compatibility-yaml-cpp.cmake new file mode 100644 index 0000000000..4716ad5226 --- /dev/null +++ b/thirdparty/yaml-cpp/compatibility-yaml-cpp.cmake @@ -0,0 +1,8 @@ +# Create targets to be compatible with yaml-cpp < 0.8.0 +macro(yaml_cpp_create_compatibility_targets) + if (NOT TARGET yaml-cpp::yaml-cpp AND TARGET yaml-cpp) + # ALIASing a imported non-global library requires CMake 3.18 so we do this + add_library(yaml-cpp::yaml-cpp INTERFACE IMPORTED) + target_link_libraries(yaml-cpp::yaml-cpp INTERFACE yaml-cpp) + endif() +endmacro() \ No newline at end of file diff --git a/thirdparty/yaml-cpp/yaml-cpp b/thirdparty/yaml-cpp/yaml-cpp index c73ee34704..f732014112 160000 --- a/thirdparty/yaml-cpp/yaml-cpp +++ b/thirdparty/yaml-cpp/yaml-cpp @@ -1 +1 @@ -Subproject commit c73ee34704c512ebe915b283645aefa9f424a22f +Subproject commit f7320141120f720aecc4c32be25586e7da9eb978