diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index e39bac55..41f9427d 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -110,7 +110,7 @@ jobs: ECAL_CORE_BUILD_TESTS: "ON" ECAL_CORE_CONFIGURATION: "OFF" ECAL_CORE_COMMAND_LINE: "OFF" - ECAL_CORE_REGISTRATION: "OFF" + ECAL_CORE_REGISTRATION: "ON" ECAL_CORE_REGISTRATION_SHM: "OFF" ECAL_CORE_MONITORING: "OFF" ECAL_CORE_PUBLISHER: "ON" @@ -212,28 +212,6 @@ jobs: ECAL_THIRDPARTY_BUILD_PROTOBUF: "OFF" BUILD_SHARED_LIBS: "OFF" CMAKE_BUILD_TYPE: "Release" - ######################################## - - name: "monitoring_only" - ######################################## - ECAL_CORE_HAS_PROTOBUF: "ON" - ECAL_CORE_BUILD_SAMPLES: "ON" - ECAL_CORE_BUILD_TESTS: "ON" - ECAL_CORE_CONFIGURATION: "OFF" - ECAL_CORE_COMMAND_LINE: "OFF" - ECAL_CORE_REGISTRATION: "ON" - ECAL_CORE_REGISTRATION_SHM: "OFF" - ECAL_CORE_MONITORING: "ON" - ECAL_CORE_PUBLISHER: "OFF" - ECAL_CORE_SUBSCRIBER: "OFF" - ECAL_CORE_SERVICE: "OFF" - ECAL_CORE_TIMEPLUGIN: "OFF" - ECAL_CORE_TRANSPORT_UDP: "OFF" - ECAL_CORE_TRANSPORT_TCP: "OFF" - ECAL_CORE_TRANSPORT_SHM: "OFF" - ECAL_CORE_NPCAP_SUPPORT: "OFF" - ECAL_THIRDPARTY_BUILD_PROTOBUF: "OFF" - BUILD_SHARED_LIBS: "OFF" - CMAKE_BUILD_TYPE: "Release" steps: - name: Install Dependencies diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 7ee9e5ca..d56a1092 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -110,7 +110,7 @@ jobs: ECAL_CORE_BUILD_TESTS: "ON" ECAL_CORE_CONFIGURATION: "OFF" ECAL_CORE_COMMAND_LINE: "OFF" - ECAL_CORE_REGISTRATION: "OFF" + ECAL_CORE_REGISTRATION: "ON" ECAL_CORE_REGISTRATION_SHM: "OFF" ECAL_CORE_MONITORING: "OFF" ECAL_CORE_PUBLISHER: "ON" @@ -212,28 +212,6 @@ jobs: ECAL_THIRDPARTY_BUILD_PROTOBUF: "OFF" BUILD_SHARED_LIBS: "OFF" CMAKE_BUILD_TYPE: "Release" - ######################################## - - name: "monitoring_only" - ######################################## - ECAL_CORE_HAS_PROTOBUF: "ON" - ECAL_CORE_BUILD_SAMPLES: "ON" - ECAL_CORE_BUILD_TESTS: "ON" - ECAL_CORE_CONFIGURATION: "OFF" - ECAL_CORE_COMMAND_LINE: "OFF" - ECAL_CORE_REGISTRATION: "ON" - ECAL_CORE_REGISTRATION_SHM: "OFF" - ECAL_CORE_MONITORING: "ON" - ECAL_CORE_PUBLISHER: "OFF" - ECAL_CORE_SUBSCRIBER: "OFF" - ECAL_CORE_SERVICE: "OFF" - ECAL_CORE_TIMEPLUGIN: "OFF" - ECAL_CORE_TRANSPORT_UDP: "OFF" - ECAL_CORE_TRANSPORT_TCP: "OFF" - ECAL_CORE_TRANSPORT_SHM: "OFF" - ECAL_CORE_NPCAP_SUPPORT: "OFF" - ECAL_THIRDPARTY_BUILD_PROTOBUF: "OFF" - BUILD_SHARED_LIBS: "OFF" - CMAKE_BUILD_TYPE: "Release" runs-on: ${{ matrix.os }} diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 763f13cf..9b98dd43 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -106,7 +106,7 @@ jobs: ECAL_CORE_BUILD_TESTS: "ON" ECAL_CORE_CONFIGURATION: "OFF" ECAL_CORE_COMMAND_LINE: "OFF" - ECAL_CORE_REGISTRATION: "OFF" + ECAL_CORE_REGISTRATION: "ON" ECAL_CORE_REGISTRATION_SHM: "OFF" ECAL_CORE_MONITORING: "OFF" ECAL_CORE_PUBLISHER: "ON" @@ -203,27 +203,6 @@ jobs: ECAL_CORE_NPCAP_SUPPORT: "OFF" BUILD_SHARED_LIBS: "OFF" CMAKE_BUILD_TYPE: "Release" - ######################################## - - name: "monitoring_only" - ######################################## - ECAL_CORE_HAS_PROTOBUF: "ON" - ECAL_CORE_BUILD_SAMPLES: "ON" - ECAL_CORE_BUILD_TESTS: "ON" - ECAL_CORE_CONFIGURATION: "OFF" - ECAL_CORE_COMMAND_LINE: "OFF" - ECAL_CORE_REGISTRATION: "ON" - ECAL_CORE_REGISTRATION_SHM: "OFF" - ECAL_CORE_MONITORING: "ON" - ECAL_CORE_PUBLISHER: "OFF" - ECAL_CORE_SUBSCRIBER: "OFF" - ECAL_CORE_SERVICE: "OFF" - ECAL_CORE_TIMEPLUGIN: "OFF" - ECAL_CORE_TRANSPORT_UDP: "OFF" - ECAL_CORE_TRANSPORT_TCP: "OFF" - ECAL_CORE_TRANSPORT_SHM: "OFF" - ECAL_CORE_NPCAP_SUPPORT: "OFF" - BUILD_SHARED_LIBS: "OFF" - CMAKE_BUILD_TYPE: "Release" steps: - name: Checkout diff --git a/build/linux/05_build_pubsub_udp_only.sh b/build/linux/05_build_pubsub_udp_only.sh index f672de4c..371e2606 100644 --- a/build/linux/05_build_pubsub_udp_only.sh +++ b/build/linux/05_build_pubsub_udp_only.sh @@ -9,7 +9,7 @@ CMAKE_OPTIONS="-DCMAKE_INSTALL_PREFIX=_install \ -DECAL_CORE_BUILD_TESTS=ON \ -DECAL_CORE_CONFIGURATION=OFF \ -DECAL_CORE_COMMAND_LINE=OFF \ --DECAL_CORE_REGISTRATION=OFF \ +-DECAL_CORE_REGISTRATION=ON \ -DECAL_CORE_REGISTRATION_SHM=OFF \ -DECAL_CORE_MONITORING=OFF \ -DECAL_CORE_PUBLISHER=ON \ diff --git a/build/windows/nanopb/compile-nanopb.bat b/build/windows/nanopb/compile-nanopb.bat new file mode 100644 index 00000000..f6cce262 --- /dev/null +++ b/build/windows/nanopb/compile-nanopb.bat @@ -0,0 +1 @@ +python compile-nanopb.py --nano_pb_path c:\nanopb\nanopb-0.4.9 --ecal_repository "..\..\.." \ No newline at end of file diff --git a/build/windows/nanopb/compile-nanopb.py b/build/windows/nanopb/compile-nanopb.py new file mode 100644 index 00000000..5632fc90 --- /dev/null +++ b/build/windows/nanopb/compile-nanopb.py @@ -0,0 +1,112 @@ +import sys +import re +import subprocess +import logging +from pathlib import Path +import shutil +import argparse + +def setup_logging(): + """Sets up the logging configuration.""" + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + + +def list_files(directory: Path, extension: str) -> list[Path]: + """Returns a list of files with specific extension in the given directory.""" + if not directory.exists(): + logging.error(f"Directory {directory} does not exist.") + return [] + return list(directory.glob(f"*{extension}")) + + +def copy_files(source_dir: Path, target_dir: Path, file_types: list[str]): + """Copies files of specific types from source to target directory.""" + target_dir.mkdir(parents=True, exist_ok=True) + for file_type in file_types: + for file in source_dir.glob(file_type): + try: + shutil.copy2(file, target_dir) + logging.info(f"Copied {file.name} to {target_dir}") + except Exception as e: + logging.error(f"Error copying {file.name} to {target_dir}: {e}") + + +def check_nanopb_compiler_exists(compiler_path: Path): + """Checks if the nanopb_generator exists.""" + if not compiler_path.exists(): + logging.error(f"nanopb generator not found at {compiler_path}") + sys.exit(1) + + +def run_nanopb_generator(proto_files: list[Path], compiler: Path, ecal_pb_base_path: Path, ecal_pb_sub_path: Path, target_dir: Path): + """Runs the nanopb generator for each proto file.""" + for proto_file_path in proto_files: + proto_file_name = proto_file_path.name + + command = [ + str(compiler), + "-I" + str(ecal_pb_base_path), + "-I" + str(ecal_pb_base_path / ecal_pb_sub_path), + "-D" + str(target_dir / ecal_pb_sub_path), + "-e" + ".npb", + proto_file_name + ] + + logging.info(f"Running: {' '.join(command)}") + + try: + result = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, cwd=proto_file_path.parent) + logging.info(f"Success: {proto_file_name} processed.") + logging.debug(result.stdout) + except subprocess.CalledProcessError as e: + logging.error(f"Error running nanopb_generator for {proto_file_name}: {e.stderr}") + + +def main(nano_pb_path: Path, ecal_repository: Path): + setup_logging() + + # Define paths based on the provided arguments + ecal_pb_base_path = Path(ecal_repository / "ecal/core_pb/src") + ecal_pb_sub_path = Path("ecal/core/pb") + ecal_target_path = Path("../../core/src/serialization/nanopb") + + # Combine paths and list .proto files + proto_files_dir = ecal_pb_base_path / ecal_pb_sub_path + ecal_pb_files = list_files(proto_files_dir, ".proto") + + # Check if nanopb generator exists + nano_pb_compiler = nano_pb_path / "generator-bin/nanopb_generator.exe" + check_nanopb_compiler_exists(nano_pb_compiler) + + # Check if any .proto files are found + if not ecal_pb_files: + logging.error(f"No .proto files found in {proto_files_dir}") + sys.exit(1) + + # Prepare target directory + absolute_target_path = (ecal_pb_base_path / ecal_target_path).resolve() + absolute_target_path.mkdir(parents=True, exist_ok=True) + + # Copy nanopb common decoder and encoder to target nanopb directory + target_nanopb_dir = absolute_target_path + copy_files(nano_pb_path, target_nanopb_dir, ["*.c", "*.h"]) + + # Run nanopb_generator for each .proto file + run_nanopb_generator(ecal_pb_files, nano_pb_compiler, ecal_pb_base_path, ecal_pb_sub_path, absolute_target_path) + + +if __name__ == "__main__": + # Set up argument parsing + parser = argparse.ArgumentParser(description="Process nanopb and eCAL protobuf files.") + parser.add_argument("--nano_pb_path", type=Path, required=True, help="Path to the nanopb directory") + parser.add_argument("--ecal_repository", type=Path, required=True, help="Path to the eCAL repository") + + # Parse the arguments + args = parser.parse_args() + + # Resolve paths to absolute paths to support relative paths + nano_pb_path = Path(args.nano_pb_path).resolve() + ecal_repository = Path(args.ecal_repository).resolve() + + # Call the main function with resolved absolute paths + main(nano_pb_path, ecal_repository) \ No newline at end of file diff --git a/ecal/core/CMakeLists.txt b/ecal/core/CMakeLists.txt index 09caa9e9..4f9e7eee 100644 --- a/ecal/core/CMakeLists.txt +++ b/ecal/core/CMakeLists.txt @@ -69,6 +69,7 @@ set(ecal_config_src src/config/ecal_cmd_parser.cpp src/config/ecal_config.cpp src/config/ecal_config_initializer.cpp + src/config/transport_layer.cpp src/types/ecal_custom_data_types.cpp ) if (ECAL_CORE_CONFIGURATION) @@ -192,6 +193,8 @@ if(ECAL_CORE_MONITORING) set(ecal_monitoring_src src/monitoring/ecal_monitoring_def.cpp src/monitoring/ecal_monitoring_def.h + src/monitoring/ecal_monitoring_filter.cpp + src/monitoring/ecal_monitoring_filter.h src/monitoring/ecal_monitoring_impl.cpp src/monitoring/ecal_monitoring_impl.h ) @@ -286,6 +289,7 @@ 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 @@ -296,6 +300,8 @@ if (ECAL_CORE_REGISTRATION) 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 @@ -323,29 +329,29 @@ endif() # serialization ###################################### set(ecal_serialization_src - src/serialization/nanopb/nanopb/pb.h - src/serialization/nanopb/nanopb/pb_common.c - src/serialization/nanopb/nanopb/pb_common.h - src/serialization/nanopb/nanopb/pb_decode.c - src/serialization/nanopb/nanopb/pb_decode.h - src/serialization/nanopb/nanopb/pb_encode.c - src/serialization/nanopb/nanopb/pb_encode.h - src/serialization/nanopb/ecal.pb.c - src/serialization/nanopb/ecal.pb.h - src/serialization/nanopb/host.pb.c - src/serialization/nanopb/host.pb.h - src/serialization/nanopb/layer.pb.c - src/serialization/nanopb/layer.pb.h - src/serialization/nanopb/logging.pb.c - src/serialization/nanopb/logging.pb.h - src/serialization/nanopb/monitoring.pb.c - src/serialization/nanopb/monitoring.pb.h - src/serialization/nanopb/process.pb.c - src/serialization/nanopb/process.pb.h - src/serialization/nanopb/service.pb.c - src/serialization/nanopb/service.pb.h - src/serialization/nanopb/topic.pb.c - src/serialization/nanopb/topic.pb.h + src/serialization/nanopb/pb.h + src/serialization/nanopb/pb_common.c + src/serialization/nanopb/pb_common.h + src/serialization/nanopb/pb_decode.c + src/serialization/nanopb/pb_decode.h + src/serialization/nanopb/pb_encode.c + src/serialization/nanopb/pb_encode.h + src/serialization/nanopb/ecal/core/pb/ecal.npb.c + src/serialization/nanopb/ecal/core/pb/ecal.npb.h + src/serialization/nanopb/ecal/core/pb/host.npb.c + src/serialization/nanopb/ecal/core/pb/host.npb.h + src/serialization/nanopb/ecal/core/pb/layer.npb.c + src/serialization/nanopb/ecal/core/pb/layer.npb.h + src/serialization/nanopb/ecal/core/pb/logging.npb.c + src/serialization/nanopb/ecal/core/pb/logging.npb.h + src/serialization/nanopb/ecal/core/pb/monitoring.npb.c + src/serialization/nanopb/ecal/core/pb/monitoring.npb.h + src/serialization/nanopb/ecal/core/pb/process.npb.c + src/serialization/nanopb/ecal/core/pb/process.npb.h + src/serialization/nanopb/ecal/core/pb/service.npb.c + src/serialization/nanopb/ecal/core/pb/service.npb.h + src/serialization/nanopb/ecal/core/pb/topic.npb.c + src/serialization/nanopb/ecal/core/pb/topic.npb.h src/serialization/ecal_serialize_common.cpp src/serialization/ecal_serialize_common.h src/serialization/ecal_serialize_logging.cpp @@ -404,6 +410,7 @@ endif() set(ecal_util_src src/util/ecal_expmap.h src/util/ecal_thread.h + src/util/expanding_vector.h src/util/frequency_calculator.h src/util/getenvvar.h ) @@ -440,6 +447,27 @@ if (WIN32) ) endif() +###################################### +# builder +###################################### +set (ecal_builder_src + src/config/builder/logging_attribute_builder.cpp + src/config/builder/monitoring_attribute_builder.cpp + src/config/builder/registration_attribute_builder.cpp + src/logging/config/builder/udp_attribute_builder.cpp + src/pubsub/config/builder/reader_attribute_builder.cpp + src/pubsub/config/builder/writer_attribute_builder.cpp + src/readwrite/config/builder/shm_attribute_builder.cpp + src/readwrite/config/builder/tcp_attribute_builder.cpp + src/readwrite/config/builder/udp_attribute_builder.cpp + src/readwrite/tcp/config/builder/data_reader_tcp_attribute_builder.cpp + src/readwrite/udp/config/builder/udp_attribute_builder.cpp + src/registration/config/builder/udp_shm_attribute_builder.cpp + src/registration/config/builder/sample_applier_attribute_builder.cpp + src/registration/udp/config/builder/udp_attribute_builder.cpp +) + + ###################################### # c interface ###################################### @@ -452,6 +480,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 @@ -480,6 +509,7 @@ set(ecal_header_cmn include/ecal/config/subscriber.h include/ecal/ecal.h include/ecal/ecal_callback.h + include/ecal/ecal_config.h include/ecal/ecal_client.h include/ecal/ecal_core.h include/ecal/ecal_deprecate.h @@ -491,6 +521,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 @@ -511,6 +542,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 @@ -558,6 +590,7 @@ set(ecal_sources ${ecal_time_src} ${ecal_util_src} ${ecal_cmn_src} + ${ecal_builder_src} ${ecal_header_cmn} ${ecal_header_msg} ) @@ -673,8 +706,8 @@ target_link_libraries(${PROJECT_NAME} target_include_directories(${PROJECT_NAME} PRIVATE $ + $ $ - $ PUBLIC $ $ 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 00000000..7b11172c --- /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 d5d0cb2f..02cbd273 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/logging.h b/ecal/core/include/ecal/config/logging.h index d5a82f32..0ade27b3 100644 --- a/ecal/core/include/ecal/config/logging.h +++ b/ecal/core/include/ecal/config/logging.h @@ -32,7 +32,7 @@ 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; + static const eCAL_Logging_Filter log_filter_default = log_level_info | log_level_warning | log_level_error | log_level_fatal; } namespace eCAL @@ -68,7 +68,7 @@ namespace eCAL { 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) + eCAL_Logging_Filter filter_log_udp { log_filter_default }; //!< Log messages logged via udp network (Default: info, warning, error, fatal) }; } diff --git a/ecal/core/include/ecal/config/monitoring.h b/ecal/core/include/ecal/config/monitoring.h index 5a905a80..3a085ce7 100644 --- a/ecal/core/include/ecal/config/monitoring.h +++ b/ecal/core/include/ecal/config/monitoring.h @@ -32,7 +32,6 @@ namespace eCAL { struct Configuration { - eCAL::Types::ConstrainedInteger<1000, 1000> timeout { 5000U }; //!< Timeout for topic monitoring in ms (Default: 5000) 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: "") }; diff --git a/ecal/core/include/ecal/config/publisher.h b/ecal/core/include/ecal/config/publisher.h index 802c9329..7ab4316a 100644 --- a/ecal/core/include/ecal/config/publisher.h +++ b/ecal/core/include/ecal/config/publisher.h @@ -105,12 +105,14 @@ namespace eCAL { struct Configuration { - bool enable { true }; //!< enable layer + bool enable { true }; //!< enable layer - 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) */ + 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) */ + 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) }; } diff --git a/ecal/core/include/ecal/config/registration.h b/ecal/core/include/ecal/config/registration.h index d01421b5..79f9798a 100644 --- a/ecal/core/include/ecal/config/registration.h +++ b/ecal/core/include/ecal/config/registration.h @@ -63,7 +63,7 @@ namespace eCAL struct Configuration { - unsigned int registration_timeout { 60000U }; //!< Timeout for topic registration in ms (internal) (Default: 60000) + 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) bool network_enabled { false }; /*!< true = all eCAL components communicate over network boundaries diff --git a/ecal/core/include/ecal/config/transport_layer.h b/ecal/core/include/ecal/config/transport_layer.h index eb89062e..b2286605 100644 --- a/ecal/core/include/ecal/config/transport_layer.h +++ b/ecal/core/include/ecal/config/transport_layer.h @@ -25,6 +25,7 @@ #pragma once #include +#include namespace eCAL { @@ -42,6 +43,16 @@ namespace eCAL }; } + 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 { Types::UdpConfigVersion config_version { Types::UdpConfigVersion::V2 }; /*!< UDP configuration version (Since eCAL 5.12.) @@ -61,6 +72,9 @@ namespace eCAL 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); }; } @@ -70,16 +84,7 @@ namespace eCAL { 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 SHM - { - struct Configuration - { - 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) + int max_reconnections { 5 }; //!< Reconnection attemps the session will try to reconnect in (Default: 5) }; } @@ -87,7 +92,6 @@ namespace eCAL { UDP::Configuration udp; TCP::Configuration tcp; - SHM::Configuration shm; }; } } \ No newline at end of file diff --git a/ecal/core/include/ecal/ecal.h b/ecal/core/include/ecal/ecal.h index 0bfbc138..8cd5beda 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_callback.h b/ecal/core/include/ecal/ecal_callback.h index 00dbd5d3..0b7dd508 100644 --- a/ecal/core/include/ecal/ecal_callback.h +++ b/ecal/core/include/ecal/ecal_callback.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. @@ -124,13 +124,22 @@ namespace eCAL }; /** - * @brief Raw data receive callback function type. + * @brief Receive callback function type with topic name and data struct. * * @param topic_name_ The topic name of the received message. * @param data_ Data struct containing payload, timestamp and publication clock. **/ using ReceiveCallbackT = std::function; + /** + * @brief Receive callback function type with topic id and data struct. The topic id contains the topic name, the process + * name, the host name and a uniques topic identifier. + * + * @param topic_id_ The topic id struct of the received message. + * @param data_ Data struct containing payload, timestamp and publication clock. + **/ + using ReceiveIDCallbackT = std::function; + /** * @brief Timer callback function type. **/ diff --git a/ecal/core/include/ecal/ecal_config.h b/ecal/core/include/ecal/ecal_config.h index 19c5ec0e..21096a53 100644 --- a/ecal/core/include/ecal/ecal_config.h +++ b/ecal/core/include/ecal/ecal_config.h @@ -30,8 +30,17 @@ //@{ namespace eCAL { - ECAL_API Configuration& GetConfiguration(); - + ECAL_API Configuration& GetConfiguration (); + ECAL_API TransportLayer::Configuration& GetTransportLayerConfiguration (); + 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 { ///////////////////////////////////// @@ -67,14 +76,7 @@ namespace eCAL ECAL_API size_t GetTcpPubsubReaderThreadpoolSize (); ECAL_API size_t GetTcpPubsubWriterThreadpoolSize (); - ECAL_API size_t GetTcpPubsubMaxReconnectionAttemps (); - - ECAL_API int GetTcpPubReaderThreadpoolSize (); - ECAL_API int GetTcpPubWriterThreadpoolSize (); - - ECAL_API int GetTcpSubReaderThreadpoolSize (); - ECAL_API int GetTcpSubWriterThreadpoolSize (); - ECAL_API int GetTcpSubMaxReconnectionAttemps (); + ECAL_API int GetTcpPubsubMaxReconnectionAttemps (); ECAL_API std::string GetHostGroupName (); @@ -95,7 +97,6 @@ namespace eCAL // monitoring ///////////////////////////////////// - ECAL_API int GetMonitoringTimeoutMs (); ECAL_API std::string GetMonitoringFilterExcludeList (); ECAL_API std::string GetMonitoringFilterIncludeList (); ECAL_API eCAL_Logging_Filter GetConsoleLogFilter (); diff --git a/ecal/core/include/ecal/ecal_log.h b/ecal/core/include/ecal/ecal_log.h index 70fd0b67..39158b75 100644 --- a/ecal/core/include/ecal/ecal_log.h +++ b/ecal/core/include/ecal/ecal_log.h @@ -42,6 +42,27 @@ namespace eCAL **/ ECAL_API void SetLogLevel(eCAL_Logging_eLogLevel level_); + /** + * @brief Sets the file log filter. + * + * @param filter_ The filter; + */ + ECAL_API void SetFileLogFilter(eCAL_Logging_Filter filter_); + + /** + * @brief Sets the udp log filter. + * + * @param filter_ The filter; + */ + ECAL_API void SetUDPLogFilter(eCAL_Logging_Filter filter_); + + /** + * @brief Sets the console log filter. + * + * @param filter_ The filter; + */ + ECAL_API void SetConsoleLogFilter(eCAL_Logging_Filter filter_); + /** * @brief Get the current log level. * diff --git a/ecal/core/include/ecal/ecal_log_level.h b/ecal/core/include/ecal/ecal_log_level.h index 532844ef..9387d00b 100644 --- a/ecal/core/include/ecal/ecal_log_level.h +++ b/ecal/core/include/ecal/ecal_log_level.h @@ -41,4 +41,4 @@ enum eCAL_Logging_eLogLevel log_level_debug4 = 128, }; -typedef char eCAL_Logging_Filter; //!< This type is to be used as a bitmask for the activated logging levels \ No newline at end of file +typedef unsigned char eCAL_Logging_Filter; //!< This type is to be used as a bitmask for the activated logging levels \ No newline at end of file diff --git a/ecal/core/include/ecal/ecal_publisher.h b/ecal/core/include/ecal/ecal_publisher.h index 7d5c9d4a..fb662a1a 100644 --- a/ecal/core/include/ecal/ecal_publisher.h +++ b/ecal/core/include/ecal/ecal_publisher.h @@ -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. @@ -263,6 +263,13 @@ namespace eCAL **/ ECAL_API std::string GetTopicName() const; + /** + * @brief Gets a unique ID of this Publisher + * + * @return The topic id. + **/ + ECAL_API Registration::STopicId GetId() const; + /** * @brief Gets description of the connected topic. * diff --git a/ecal/core/include/ecal/ecal_registration.h b/ecal/core/include/ecal/ecal_registration.h new file mode 100644 index 00000000..4222efa8 --- /dev/null +++ b/ecal/core/include/ecal/ecal_registration.h @@ -0,0 +1,392 @@ +/* ========================= 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 +#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; + + using CallbackToken = std::size_t; + + enum class RegistrationEventType + { + new_entity, //!< Represents a new entity registration + deleted_entity //!< Represents a deletion of an entity + }; + + using TopicIDCallbackT = std::function; + using ServiceIDCallbackT = std::function; + + /** + * @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 Register a callback function to be notified when a new publisher becomes available. + * + * @param callback_ The callback function to be called with the STopicId of the new publisher. + * The callback function must not be blocked for a longer period of time, + * otherwise timeout mechanisms of the eCAL registration would be triggered. + * + * @return CallbackToken Token that can be used to unregister the callback. + */ + ECAL_API CallbackToken AddPublisherEventCallback(const TopicIDCallbackT& callback_); + + /** + * @brief Unregister the publisher callback using the provided token. + * + * @param token The token returned by AddPublisherCallback. + */ + ECAL_API void RemPublisherEventCallback(CallbackToken token_); + + /** + * @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 Register a callback function to be notified when a new subscriber becomes available. + * + * @param callback_ The callback function to be called with the STopicId of the new subscriber. + * The callback function must not be blocked for a longer period of time, + * otherwise timeout mechanisms of the eCAL registration would be triggered. + * + * @return CallbackToken Token that can be used to unregister the callback. + */ + ECAL_API CallbackToken AddSubscriberEventCallback(const TopicIDCallbackT& callback_); + + /** + * @brief Unregister the subscriber callback using the provided token. + * + * @param token The token returned by AddSubscriberCallback. + */ + ECAL_API void RemSubscriberEventCallback(CallbackToken token_); + + /** + * @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 5ba5610d..c947d4bf 100644 --- a/ecal/core/include/ecal/ecal_subscriber.h +++ b/ecal/core/include/ecal/ecal_subscriber.h @@ -97,7 +97,7 @@ 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. @@ -105,7 +105,7 @@ namespace eCAL * @param topic_name_ Unique topic name. * @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. @@ -208,6 +208,15 @@ namespace eCAL **/ ECAL_API bool AddReceiveCallback(ReceiveCallbackT callback_); + /** + * @brief Add callback function for incoming receives. + * + * @param callback_ The callback function to add. + * + * @return True if succeeded, false if not. + **/ + ECAL_API bool AddReceiveCallback(ReceiveIDCallbackT callback_); + /** * @brief Remove callback function for incoming receives. * @@ -262,6 +271,13 @@ namespace eCAL **/ ECAL_API std::string GetTopicName() const; + /** + * @brief Gets a unique ID of this Subscriber + * + * @return The topic id. + **/ + ECAL_API Registration::STopicId GetId() const; + /** * @brief Gets description of the connected topic. * diff --git a/ecal/core/include/ecal/ecal_types.h b/ecal/core/include/ecal/ecal_types.h index 5e32297c..e446c25b 100644 --- a/ecal/core/include/ecal/ecal_types.h +++ b/ecal/core/include/ecal/ecal_types.h @@ -25,6 +25,7 @@ #pragma once #include #include +#include namespace eCAL { @@ -53,6 +54,13 @@ namespace eCAL { return std::tie(name, encoding, descriptor) < std::tie(rhs.name, rhs.encoding, rhs.descriptor); } + + void clear() + { + name.clear(); + encoding.clear(); + descriptor.clear(); + } //!< @endcond }; @@ -82,4 +90,72 @@ namespace eCAL } //!< @endcond }; + + namespace Registration + { + struct SEntityId + { + std::string entity_id; // unique id within that process (it should already be unique within the whole system) + 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; + } + + bool operator<(const SEntityId& other) const + { + return entity_id < other.entity_id; + } + }; + + // Overload the << operator for SEntityId + inline std::ostream& operator<<(std::ostream& os, const SEntityId& id) + { + os << "SEntityId(entity_id: " << id.entity_id + << ", process_id: " << id.process_id + << ", host_name: " << id.host_name << ")"; + return os; + } + + struct STopicId + { + SEntityId topic_id; + std::string topic_name; + + bool operator==(const STopicId& other) const + { + return topic_id == other.topic_id && topic_name == other.topic_name; + } + + bool operator<(const STopicId& other) const + { + return std::tie(topic_id, topic_name) < std::tie(other.topic_id, other.topic_name); + } + }; + + inline std::ostream& operator<<(std::ostream& os, const STopicId& id) + { + os << "STopicId(topic_id: " << id.topic_id + << ", topic_name: " << id.topic_name << ")"; + return os; + } + + struct SServiceId + { + SEntityId service_id; + std::string service_name; + std::string method_name; + + bool operator==(const SServiceId& other) const + { + return service_id == other.service_id && service_name == other.service_name && method_name == other.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 6bb6471f..ba4fa07c 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,78 +25,14 @@ #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 @@ -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 fb74dd87..b9ff42d0 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 78603f59..1d09b6df 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 717ab81f..2ff702f8 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 78cd42ca..daab06f0 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_)); } diff --git a/ecal/core/include/ecal/msg/publisher.h b/ecal/core/include/ecal/msg/publisher.h index 74e993c6..5497c9bf 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 03cbf886..6d280618 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 b293f676..a0e09bad 100644 --- a/ecal/core/include/ecal/msg/subscriber.h +++ b/ecal/core/include/ecal/msg/subscriber.h @@ -256,7 +256,7 @@ namespace eCAL * @param topic_name_ Unique topic name. * @param config_ Optional configuration parameters. **/ - CMessageSubscriber(const std::string& topic_name_, const Subscriber::Configuration& config_ = {}) : CSubscriber() + CMessageSubscriber(const std::string& topic_name_, const Subscriber::Configuration& config_ = GetSubscriberConfiguration()) : CSubscriber() , m_deserializer() { SDataTypeInformation topic_info = m_deserializer.GetDataTypeInformation(); 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 12218f12..00ab7b7c 100644 --- a/ecal/core/include/ecal/types/ecal_custom_data_types.h +++ b/ecal/core/include/ecal/types/ecal_custom_data_types.h @@ -52,7 +52,7 @@ namespace eCAL ECAL_API IpAddressV4& operator=(const std::string& ip_string_); ECAL_API IpAddressV4& operator=(const char* ip_string_); - ECAL_API operator std::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); 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 00000000..b01590e9 --- /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 da01819a..cdf16d0d 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/builder/logging_attribute_builder.cpp b/ecal/core/src/config/builder/logging_attribute_builder.cpp new file mode 100644 index 00000000..52cf0b6f --- /dev/null +++ b/ecal/core/src/config/builder/logging_attribute_builder.cpp @@ -0,0 +1,79 @@ +#include "logging_attribute_builder.h" +#include "ecal/ecal_process.h" +#include "ecal/ecal_util.h" + +namespace eCAL +{ + namespace Logging + { + SAttributes BuildLoggingAttributes(const Logging::Configuration& log_config_, const Registration::Configuration& reg_config_, const TransportLayer::Configuration& tl_config_) + { + SAttributes attributes; + + attributes.network_enabled = reg_config_.network_enabled; + attributes.host_name = Process::GetHostName(); + attributes.process_id = Process::GetProcessID(); + attributes.process_name = Process::GetProcessName(); + attributes.unit_name = Process::GetUnitName(); + attributes.level = log_level_info; + + attributes.udp.enabled = log_config_.sinks.udp.enable; + attributes.udp.port = log_config_.sinks.udp.port; + attributes.udp.filter_log = log_config_.sinks.udp.filter_log_udp; + + attributes.file.enabled = log_config_.sinks.file.enable; + attributes.file.filter_log = log_config_.sinks.file.filter_log_file; + attributes.file.path = log_config_.sinks.file.path; + if (attributes.file.path.empty()) + { + // check ECAL_DATA + // Creates path if not exists + attributes.file.path = Util::GeteCALLogPath(); + } + + attributes.console.enabled = log_config_.sinks.console.enable; + attributes.console.filter_log = log_config_.sinks.console.filter_log_con; + + // UDP related configuration part + attributes.udp_sender.broadcast = !reg_config_.network_enabled; + attributes.udp_sender.loopback = reg_config_.loopback; + + attributes.udp_sender.sndbuf = tl_config_.udp.send_buffer; + attributes.udp_sender.port = tl_config_.udp.port; + + switch (tl_config_.udp.mode) + { + case Types::UDPMode::NETWORK: + attributes.udp_sender.address = tl_config_.udp.network.group; + attributes.udp_sender.ttl = tl_config_.udp.network.ttl; + break; + case Types::UDPMode::LOCAL: + attributes.udp_sender.address = tl_config_.udp.local.group; + attributes.udp_sender.ttl = tl_config_.udp.local.ttl; + break; + default: + break; + } + + attributes.udp_receiver.broadcast = !reg_config_.network_enabled; + attributes.udp_receiver.loopback = true; + + attributes.udp_receiver.rcvbuf = tl_config_.udp.receive_buffer; + attributes.udp_receiver.port = tl_config_.udp.port; + + switch (tl_config_.udp.mode) + { + case Types::UDPMode::NETWORK: + attributes.udp_receiver.address = tl_config_.udp.network.group; + break; + case Types::UDPMode::LOCAL: + attributes.udp_receiver.address = tl_config_.udp.local.group; + break; + default: + break; + } + + return attributes; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/config/builder/logging_attribute_builder.h b/ecal/core/src/config/builder/logging_attribute_builder.h new file mode 100644 index 00000000..122e0ecc --- /dev/null +++ b/ecal/core/src/config/builder/logging_attribute_builder.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 "logging/config/attributes/logging_attributes.h" +#include "ecal/config/logging.h" +#include "ecal/config/registration.h" +#include "ecal/config/transport_layer.h" + +namespace eCAL +{ + namespace Logging + { + SAttributes BuildLoggingAttributes(const Logging::Configuration& log_config_, const Registration::Configuration& reg_config_, const TransportLayer::Configuration& tl_config_); + } +} \ No newline at end of file diff --git a/ecal/core/src/config/builder/monitoring_attribute_builder.cpp b/ecal/core/src/config/builder/monitoring_attribute_builder.cpp new file mode 100644 index 00000000..f3d21a4c --- /dev/null +++ b/ecal/core/src/config/builder/monitoring_attribute_builder.cpp @@ -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 ================================= +*/ + +#include "monitoring_attribute_builder.h" + +namespace eCAL +{ + namespace Monitoring + { + SAttributes BuildMonitoringAttributes(const Monitoring::Configuration& config_) + { + SAttributes attributes; + + attributes.filter_excl = config_.filter_excl; + attributes.filter_incl = config_.filter_incl; + + return attributes; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/config/builder/monitoring_attribute_builder.h b/ecal/core/src/config/builder/monitoring_attribute_builder.h new file mode 100644 index 00000000..2d286500 --- /dev/null +++ b/ecal/core/src/config/builder/monitoring_attribute_builder.h @@ -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 ================================= +*/ + +#pragma once + +#include "monitoring/config/attributes/monitoring_attributes.h" + +#include + +namespace eCAL +{ + namespace Monitoring + { + SAttributes BuildMonitoringAttributes(const Monitoring::Configuration& config_); + } +} \ No newline at end of file diff --git a/ecal/core/src/config/builder/registration_attribute_builder.cpp b/ecal/core/src/config/builder/registration_attribute_builder.cpp new file mode 100644 index 00000000..d9070474 --- /dev/null +++ b/ecal/core/src/config/builder/registration_attribute_builder.cpp @@ -0,0 +1,64 @@ +/* ========================= 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; + + 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/config/builder/registration_attribute_builder.h b/ecal/core/src/config/builder/registration_attribute_builder.h new file mode 100644 index 00000000..92d821f9 --- /dev/null +++ b/ecal/core/src/config/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/config/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/config/configuration_reader.cpp b/ecal/core/src/config/configuration_reader.cpp index 13559d7c..1173c091 100644 --- a/ecal/core/src/config/configuration_reader.cpp +++ b/ecal/core/src/config/configuration_reader.cpp @@ -26,14 +26,14 @@ namespace eCAL 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_) { @@ -47,7 +47,7 @@ namespace eCAL } return false; - }; + } void MergeYamlNodes(YAML::Node& base, const YAML::Node& other) { @@ -96,7 +96,7 @@ namespace eCAL } } } - }; + } bool MergeYamlIntoConfiguration(const std::string& file_name_ , eCAL::Configuration& config_) { diff --git a/ecal/core/src/config/configuration_to_yaml.cpp b/ecal/core/src/config/configuration_to_yaml.cpp index 94ff0189..8cf3f94e 100644 --- a/ecal/core/src/config/configuration_to_yaml.cpp +++ b/ecal/core/src/config/configuration_to_yaml.cpp @@ -36,7 +36,7 @@ namespace YAML } return(filter_mask); - }; + } std::vector LogLevelToVector(eCAL_Logging_Filter filter_mask) { @@ -165,9 +165,15 @@ namespace YAML 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_.host_group_name, node_, "host_group_name"); + 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; } @@ -183,7 +189,6 @@ namespace YAML Node convert::encode(const eCAL::Monitoring::Configuration& config_) { Node node; - node["timeout"] << config_.timeout; node["filter_excl"] = config_.filter_excl; node["filter_incl"] = config_.filter_incl; @@ -192,7 +197,6 @@ namespace YAML bool convert::decode(const Node& node_, eCAL::Monitoring::Configuration& config_) { - AssignValue(config_.timeout, node_, "timeout"); AssignValue(config_.filter_excl, node_, "filter_excl"); AssignValue(config_.filter_incl, node_, "filter_incl"); return true; @@ -206,22 +210,6 @@ namespace YAML /_/ /_/ \_,_/_//_/___/ .__/\___/_/ \__/____/\_,_/\_, /\__/_/ /_/ /___/ */ - - Node convert::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; @@ -292,7 +280,6 @@ namespace YAML Node convert::encode(const eCAL::TransportLayer::Configuration& config_) { Node node; - node["shm"] = config_.shm; node["udp"] = config_.udp; node["tcp"] = config_.tcp; @@ -301,7 +288,6 @@ namespace YAML 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; @@ -318,10 +304,12 @@ namespace YAML Node convert::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; + 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; + node["memfile_min_size_bytes"] << config_.memfile_min_size_bytes; + node["memfile_reserve_percent"] << config_.memfile_reserve_percent; return node; } @@ -331,6 +319,8 @@ namespace YAML 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"); + AssignValue(config_.memfile_min_size_bytes, node_, "memfile_min_size_bytes"); + AssignValue(config_.memfile_reserve_percent, node_, "memfile_reserve_percent"); return true; } diff --git a/ecal/core/src/config/configuration_to_yaml.h b/ecal/core/src/config/configuration_to_yaml.h index d6930d7a..b983f350 100644 --- a/ecal/core/src/config/configuration_to_yaml.h +++ b/ecal/core/src/config/configuration_to_yaml.h @@ -101,14 +101,6 @@ namespace YAML /_/ /_/ \_,_/_//_/___/ .__/\___/_/ \__/____/\_,_/\_, /\__/_/ /_/ /___/ */ - template<> - 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 { diff --git a/ecal/core/src/config/default_configuration.cpp b/ecal/core/src/config/default_configuration.cpp index f8cbff2f..5ca03722 100644 --- a/ecal/core/src/config/default_configuration.cpp +++ b/ecal/core/src/config/default_configuration.cpp @@ -7,10 +7,6 @@ namespace { - std::string quoteString(const char* str_) { - return std::string("\"") + std::string(str_) + std::string("\""); - } - std::string quoteString(const std::string& str_) { return std::string("\"") + str_ + std::string("\""); } @@ -166,8 +162,6 @@ namespace eCAL ss << R"()" << "\n"; ss << R"(# Monitoring configuration)" << "\n"; ss << R"(monitoring:)" << "\n"; - ss << R"( # Timeout for topic monitoring in ms (Default: 5000), increase in 1000er steps)" << "\n"; - ss << R"( timeout: )" << config_.monitoring.timeout << "\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"; @@ -220,12 +214,6 @@ namespace eCAL 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"; @@ -241,6 +229,10 @@ namespace eCAL 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"( # Default memory file size for new publisher)" << "\n"; + ss << R"( memfile_min_size_bytes: )" << config_.publisher.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_.publisher.layer.shm.memfile_reserve_percent << "\n"; ss << R"()" << "\n"; ss << R"( # Base configuration for UDP publisher)" << "\n"; ss << R"( udp:)" << "\n"; diff --git a/ecal/core/src/config/ecal_cmd_parser.cpp b/ecal/core/src/config/ecal_cmd_parser.cpp index babfef68..0d9c26af 100644 --- a/ecal/core/src/config/ecal_cmd_parser.cpp +++ b/ecal/core/src/config/ecal_cmd_parser.cpp @@ -82,7 +82,7 @@ namespace eCAL #endif } - bool CmdParser::getDumpConfig() const { return m_dump_config; }; - std::string& CmdParser::getUserIni() { return m_user_ini; }; + bool CmdParser::getDumpConfig() const { return m_dump_config; } + std::string& CmdParser::getUserIni() { return m_user_ini; } } } diff --git a/ecal/core/src/config/ecal_config.cpp b/ecal/core/src/config/ecal_config.cpp index 81c8a650..8b565036 100644 --- a/ecal/core/src/config/ecal_config.cpp +++ b/ecal/core/src/config/ecal_config.cpp @@ -58,9 +58,9 @@ namespace eCAL ECAL_API bool IsNpcapEnabled () { return GetConfiguration().transport_layer.udp.npcap_enabled; } - 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 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 int GetTcpPubsubMaxReconnectionAttemps () { return GetConfiguration().transport_layer.tcp.max_reconnections;} ECAL_API std::string GetHostGroupName () { return GetConfiguration().registration.host_group_name; } @@ -81,7 +81,6 @@ namespace eCAL // monitoring ///////////////////////////////////// - ECAL_API int GetMonitoringTimeoutMs () { return GetConfiguration().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.sinks.console.filter_log_con; } diff --git a/ecal/core/src/config/ecal_config_initializer.cpp b/ecal/core/src/config/ecal_config_initializer.cpp index 2718470b..a3b154c2 100644 --- a/ecal/core/src/config/ecal_config_initializer.cpp +++ b/ecal/core/src/config/ecal_config_initializer.cpp @@ -225,14 +225,14 @@ namespace eCAL 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."); + std::cout << "Yaml file found at \"" << yaml_path << "\" but eCAL core configuration is not enabled." << "\n"; #endif } else { - eCAL::Logging::Log(log_level_warning, "Specified yaml configuration path not valid:\"" + yaml_path_ + "\". Using default configuration."); + std::cout << "Specified yaml configuration path not valid:\"" << yaml_path_ << "\". Using default configuration." << "\n"; } - }; + } Configuration::Configuration(int argc_ , char **argv_) : Configuration(ConvertArgcArgvToVector(argc_, argv_)) @@ -240,7 +240,6 @@ namespace eCAL } Configuration::Configuration(const std::vector& args_) - : Configuration() { Config::CmdParser parser(args_); @@ -264,7 +263,6 @@ namespace eCAL Configuration::Configuration() { - eCAL::InitGlobals(); } std::string Configuration::GetYamlFilePath() @@ -275,19 +273,58 @@ namespace eCAL Configuration& GetConfiguration() { return g_ecal_configuration; - }; + } + + TransportLayer::Configuration& GetTransportLayerConfiguration() + { + return GetConfiguration().transport_layer; + } + + Registration::Configuration& GetRegistrationConfiguration() + { + return GetConfiguration().registration; + } + + Monitoring::Configuration& GetMonitoringConfiguration() + { + return GetConfiguration().monitoring; + } + + Logging::Configuration& GetLoggingConfiguration() + { + return GetConfiguration().logging; + } + + Subscriber::Configuration& GetSubscriberConfiguration() + { + return GetConfiguration().subscriber; + } + + Publisher::Configuration& GetPublisherConfiguration() + { + return GetConfiguration().publisher; + } + + Time::Configuration& GetTimesyncConfiguration() + { + return GetConfiguration().timesync; + } + + Service::Configuration& GetServiceConfiguration() + { + return GetConfiguration().service; + } + + 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(); - } - bool direxists(const std::string& path_) { const EcalUtils::Filesystem::FileStatus status(path_, EcalUtils::Filesystem::Current); diff --git a/ecal/core/src/config/transport_layer.cpp b/ecal/core/src/config/transport_layer.cpp new file mode 100644 index 00000000..7661d47e --- /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 54a5cf90..5fbd3296 100644 --- a/ecal/core/src/ecal.cpp +++ b/ecal/core/src/ecal.cpp @@ -132,6 +132,8 @@ namespace eCAL **/ int Initialize(eCAL::Configuration& config_, const char *unit_name_ /*= nullptr*/, unsigned int components_ /*= Init::Default*/) { + InitGlobals(); + g_ecal_configuration = config_; if (unit_name_ != nullptr) diff --git a/ecal/core/src/ecal_descgate.cpp b/ecal/core/src/ecal_descgate.cpp index 52f82f5a..786f28ea 100644 --- a/ecal/core/src/ecal_descgate.cpp +++ b/ecal/core/src/ecal_descgate.cpp @@ -27,81 +27,157 @@ 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() + Registration::CallbackToken CDescGate::AddPublisherEventCallback(const Registration::TopicIDCallbackT& callback_) { - return GetTopics(m_publisher_info_map); + const std::lock_guard lock(m_publisher_callback_map.mtx); + + const Registration::CallbackToken new_token = CreateToken(); + m_publisher_callback_map.map[new_token] = callback_; + + return new_token; } - Util::QualityTopicInfoMultiMap CDescGate::GetSubscribers() + void CDescGate::RemPublisherEventCallback(Registration::CallbackToken token_) { - return GetTopics(m_subscriber_info_map); + const std::lock_guard lock(m_publisher_callback_map.mtx); + m_publisher_callback_map.map.erase(token_); } - Util::QualityServiceInfoMultimap CDescGate::GetServices() + std::set CDescGate::GetSubscriberIDs() const { - return GetServices(m_service_info_map); + return GetTopicIDs(m_subscriber_info_map); } - Util::QualityServiceInfoMultimap CDescGate::GetClients() + bool CDescGate::GetSubscriberInfo(const Registration::STopicId& id_, Registration::SQualityTopicInfo& topic_info_) const { - return GetServices(m_client_info_map); + return GetTopic(id_, m_subscriber_info_map, topic_info_); } - Util::QualityTopicInfoMultiMap CDescGate::GetTopics(SQualityTopicIdMap& topic_info_map_) + Registration::CallbackToken CDescGate::AddSubscriberEventCallback(const Registration::TopicIDCallbackT& callback_) { - Util::QualityTopicInfoMultiMap multi_map; + const std::lock_guard lock(m_subscriber_callback_map.mtx); - const std::lock_guard lock(topic_info_map_.mtx); - topic_info_map_.map.erase_expired(); + const Registration::CallbackToken new_token = CreateToken(); + m_subscriber_callback_map.map[new_token] = callback_; + + return new_token; + } + + void CDescGate::RemSubscriberEventCallback(Registration::CallbackToken token_) + { + const std::lock_guard lock(m_subscriber_callback_map.mtx); + m_subscriber_callback_map.map.erase(token_); + } + + std::set CDescGate::GetServiceIDs() const + { + return GetServiceIDs(m_service_info_map); + } + + bool CDescGate::GetServiceInfo(const Registration::SServiceId& id_, Registration::SQualityServiceInfo& service_info_) const + { + return GetService(id_, m_service_info_map, service_info_); + } + + std::set CDescGate::GetClientIDs() const + { + return GetServiceIDs(m_client_info_map); + } + 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.erase_expired(); + for (const auto& service_method_info_map_it : service_method_info_map_.id_map) + { + service_id_set.insert(service_method_info_map_it.first); + } + return service_id_set; + } - for (const auto& service_method_info_map_it : service_method_info_map_.map) + 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_.id_map.find(id_); + if (iter == service_method_info_map_.id_map.end()) + { + return false; + } + else { - 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_method_info_ = iter->second; + return true; } - return multi_map; } void CDescGate::ApplySample(const Registration::Sample& sample_, eTLayerType /*layer_*/) @@ -125,12 +201,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_.identifier.entity_id), 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_.identifier.entity_id)); + RemServiceDescription(m_service_info_map, sample_.identifier, sample_.service.sname); break; case bct_reg_client: for (const auto& method : sample_.client.methods) @@ -143,23 +219,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_.identifier.entity_id), 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_.identifier.entity_id)); + 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_.identifier.entity_id), sample_.topic.tdatatype, GetDataTypeInfoQuality(sample_.topic.tdatatype, true)); + ApplyTopicDescription(m_publisher_info_map, m_publisher_callback_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_.identifier.entity_id)); + RemTopicDescription(m_publisher_info_map, m_publisher_callback_map, sample_.identifier, sample_.topic.tname); break; case bct_reg_subscriber: - ApplyTopicDescription(m_subscriber_info_map, sample_.topic.tname, std::stoull(sample_.identifier.entity_id), sample_.topic.tdatatype, GetDataTypeInfoQuality(sample_.topic.tdatatype, false)); + ApplyTopicDescription(m_subscriber_info_map, m_subscriber_callback_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_.identifier.entity_id)); + RemTopicDescription(m_subscriber_info_map, m_subscriber_callback_map, sample_.identifier, sample_.topic.tname); break; default: { @@ -170,73 +246,122 @@ 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 STopicIdCallbackMap& topic_callback_map_, + 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_ }; + + // update topic info + bool new_topic_info(false); + { + const std::unique_lock lock(topic_info_map_.mtx); + QualityTopicIdMap::iterator topic_info_quality_iter = topic_info_map_.map.find(topic_info_key); + new_topic_info = topic_info_quality_iter == topic_info_map_.map.end(); - Util::SQualityTopicInfo topic_quality_info; - topic_quality_info.id = topic_id_; - topic_quality_info.info = topic_info_; - topic_quality_info.quality = topic_quality_; + if (new_topic_info) + { + std::tie(topic_info_quality_iter, std::ignore) = topic_info_map_.map.emplace(topic_info_key, Registration::SQualityTopicInfo{}); + } + + topic_info_quality_iter->second.info = topic_info_; + topic_info_quality_iter->second.quality = topic_quality_; + } - const std::unique_lock lock(topic_info_map_.mtx); - topic_info_map_.map.erase_expired(); - topic_info_map_.map[topic_info_key] = topic_quality_info; + // notify publisher / subscriber registration callbacks about new entity + if(new_topic_info) + { + const std::unique_lock lock(topic_callback_map_.mtx); + for (const auto& callback_iter : topic_callback_map_.map) + { + if (callback_iter.second) + { + callback_iter.second(topic_info_key, Registration::RegistrationEventType::new_entity); + } + } + } } - 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 STopicIdCallbackMap& topic_callback_map_, + const Registration::SampleIdentifier& topic_id_, + const std::string& topic_name_) { - const std::unique_lock lock(topic_info_map_.mtx); - topic_info_map_.map.erase_expired(); - topic_info_map_.map.erase(STopicIdKey{ topic_name_, topic_id_ }); + const auto topic_info_key = Registration::STopicId{ ConvertToEntityId(topic_id_), topic_name_ }; + + // delete topic info + bool deleted_topic_info(false); + { + const std::unique_lock lock(topic_info_map_.mtx); + deleted_topic_info = topic_info_map_.map.erase(topic_info_key) > 0; + } + + // notify publisher / subscriber registration callbacks about deleted entity + if (deleted_topic_info) + { + const std::unique_lock lock(topic_callback_map_.mtx); + for (const auto& callback_iter : topic_callback_map_.map) + { + if (callback_iter.second) + { + callback_iter.second(topic_info_key, Registration::RegistrationEventType::deleted_entity); + } + } + } } 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.erase_expired(); - service_method_info_map_.map[service_method_info_key] = service_quality_info; + service_method_info_map_.id_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.erase_expired(); - for (auto&& service_it : service_method_info_map_.map) + for (auto&& service_it : service_method_info_map_.id_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_.id_map.erase(service_method_info_key); } } + + Registration::CallbackToken CDescGate::CreateToken() + { + // Atomically increment m_callback_token using fetch_add to ensure thread safety. + // fetch_add returns the value before increment, so we add 1 to get the new token value. + // memory_order_relaxed is used to optimize performance without additional synchronization. + return m_callback_token.fetch_add(1, std::memory_order_relaxed) + 1; + } } diff --git a/ecal/core/src/ecal_descgate.h b/ecal/core/src/ecal_descgate.h index fbc10653..76262e45 100644 --- a/ecal/core/src/ecal_descgate.h +++ b/ecal/core/src/ecal_descgate.h @@ -23,12 +23,13 @@ #pragma once +#include #include #include #include "serialization/ecal_struct_sample_registration.h" -#include "util/ecal_expmap.h" +#include #include #include #include @@ -37,48 +38,34 @@ 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; + Registration::CallbackToken AddPublisherEventCallback(const Registration::TopicIDCallbackT& callback_); + void RemPublisherEventCallback(Registration::CallbackToken token_); + + // get subscriber information + std::set GetSubscriberIDs() const; + bool GetSubscriberInfo(const Registration::STopicId& id_, Registration::SQualityTopicInfo& topic_info_) const; + Registration::CallbackToken AddSubscriberEventCallback(const Registration::TopicIDCallbackT& callback_); + void RemSubscriberEventCallback(Registration::CallbackToken token_); + + // get service information + std::set GetServiceIDs() const; + bool GetServiceInfo(const Registration::SServiceId& id_, Registration::SQualityServiceInfo& service_info_) const; - // get service/clients maps - Util::QualityServiceInfoMultimap GetServices(); - Util::QualityServiceInfoMultimap GetClients(); + // 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,54 +76,72 @@ namespace eCAL CDescGate& operator=(CDescGate&&) = delete; protected: - using QualityTopicIdExpMap = eCAL::Util::CExpirationMap; + 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::CExpirationMap; + using TopicIdCallbackMap = std::map; + struct STopicIdCallbackMap + { + mutable std::mutex mtx; + TopicIdCallbackMap map; + }; + + 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 id_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 STopicIdCallbackMap& topic_callback_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 STopicIdCallbackMap& topic_callback_map_, + 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_); + Registration::CallbackToken CreateToken(); + // internal quality topic info publisher/subscriber maps SQualityTopicIdMap m_publisher_info_map; + STopicIdCallbackMap m_publisher_callback_map; + SQualityTopicIdMap m_subscriber_info_map; + STopicIdCallbackMap m_subscriber_callback_map; // internal quality service info service/client maps SQualityServiceIdMap m_service_info_map; SQualityServiceIdMap m_client_info_map; + + mutable std::mutex m_callback_token_mtx; + std::atomic m_callback_token{ 0 }; }; } diff --git a/ecal/core/src/ecal_global_accessors.cpp b/ecal/core/src/ecal_global_accessors.cpp index f2befc53..85024b1e 100644 --- a/ecal/core/src/ecal_global_accessors.cpp +++ b/ecal/core/src/ecal_global_accessors.cpp @@ -86,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 56634748..70eeeb3e 100644 --- a/ecal/core/src/ecal_globals.cpp +++ b/ecal/core/src/ecal_globals.cpp @@ -33,6 +33,10 @@ #include "service/ecal_service_singleton_manager.h" #endif +#include "config/builder/registration_attribute_builder.h" +#include "config/builder/monitoring_attribute_builder.h" +#include "config/builder/logging_attribute_builder.h" + namespace eCAL { CGlobals::CGlobals() : initialized(false), components(0) @@ -43,18 +47,19 @@ namespace eCAL Finalize(); } - int CGlobals::Initialize(unsigned int components_, std::vector* config_keys_ /*= nullptr*/) + int CGlobals::Initialize(unsigned int components_) { // will be set if any new module was initialized bool new_initialization(false); #if ECAL_CORE_REGISTRATION + const Registration::SAttributes registration_attr = BuildRegistrationAttributes(GetRegistrationConfiguration(), GetTransportLayerConfiguration().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; } @@ -63,7 +68,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 @@ -74,7 +79,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; } @@ -174,7 +179,7 @@ namespace eCAL { if (monitoring_instance == nullptr) { - monitoring_instance = std::make_unique(); + monitoring_instance = std::make_unique(eCAL::Monitoring::BuildMonitoringAttributes(GetMonitoringConfiguration())); new_initialization = true; } } @@ -187,7 +192,7 @@ namespace eCAL { if (log_instance == nullptr) { - log_instance = std::make_unique(); + log_instance = std::make_unique(eCAL::Logging::BuildLoggingAttributes(GetLoggingConfiguration(), GetRegistrationConfiguration(), GetTransportLayerConfiguration())); new_initialization = true; } } @@ -345,6 +350,9 @@ namespace eCAL log_instance = nullptr; initialized = false; + // reset configuration to default values + g_ecal_configuration = Configuration(); + return(0); } } diff --git a/ecal/core/src/ecal_globals.h b/ecal/core/src/ecal_globals.h index dc5eaed7..11044529 100644 --- a/ecal/core/src/ecal_globals.h +++ b/ecal/core/src/ecal_globals.h @@ -61,7 +61,7 @@ namespace eCAL CGlobals(); ~CGlobals(); - int Initialize ( unsigned int components_, std::vector* config_keys_ = nullptr); + int Initialize ( unsigned int components_); bool IsInitialized ( unsigned int component_ ); unsigned int GetComponents() const { return(components); }; diff --git a/ecal/core/src/ecal_util.cpp b/ecal/core/src/ecal_util.cpp index 4df41fb1..6e11e4ec 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 e2a1ed81..9a982e48 100644 --- a/ecal/core/src/io/shm/ecal_memfile_pool.cpp +++ b/ecal/core/src/io/shm/ecal_memfile_pool.cpp @@ -96,7 +96,7 @@ namespace eCAL return true; } - bool CMemFileObserver::Start(const std::string& topic_name_, const std::string& topic_id_, const int timeout_, const MemFileDataCallbackT& callback_) + bool CMemFileObserver::Start(const int timeout_, const MemFileDataCallbackT& callback_) { if (!m_created) return false; if (m_is_observing) return false; @@ -108,11 +108,11 @@ namespace eCAL m_is_observing = true; // start observer thread - m_thread = std::thread(&CMemFileObserver::Observe, this, topic_name_, topic_id_, timeout_); + m_thread = std::thread(&CMemFileObserver::Observe, this, timeout_); #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, std::string("CMemFileObserver started (" + topic_name_ + ", " + topic_id_ + ")")); + Logging::Log(log_level_debug2, std::string("CMemFileObserver started.")); #endif return true; @@ -147,7 +147,7 @@ namespace eCAL return true; } - void CMemFileObserver::Observe(const std::string& topic_name_, const std::string& topic_id_, const int timeout_) + void CMemFileObserver::Observe(const int timeout_) { // internal clock sample update checking uint64_t last_sample_clock(0); @@ -223,13 +223,13 @@ namespace eCAL // 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); + m_data_callback(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); + m_data_callback(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); } } } @@ -264,7 +264,7 @@ namespace eCAL if (post_process_buffer) { // add sample to data reader (and call user callback function) - if (m_data_callback) m_data_callback(topic_name_, topic_id_, receive_buffer.data(), receive_buffer.size(), (long long)mfile_hdr.id, (long long)mfile_hdr.clock, (long long)mfile_hdr.time, (size_t)mfile_hdr.hash); + if (m_data_callback) m_data_callback(receive_buffer.data(), receive_buffer.size(), (long long)mfile_hdr.id, (long long)mfile_hdr.clock, (long long)mfile_hdr.time, (size_t)mfile_hdr.hash); } // send acknowledge event @@ -365,7 +365,7 @@ namespace eCAL m_created = false; } - bool CMemFileThreadPool::ObserveFile(const std::string& memfile_name_, const std::string& memfile_event_, const std::string& topic_name_, const std::string& topic_id_, int timeout_observation_ms, const MemFileDataCallbackT& callback_) + bool CMemFileThreadPool::ObserveFile(const std::string& memfile_name_, const std::string& memfile_event_, int timeout_observation_ms, const MemFileDataCallbackT& callback_) { if(!m_created) return(false); if(memfile_name_.empty()) return(false); @@ -388,7 +388,7 @@ namespace eCAL else { observer->Stop(); - observer->Start(topic_name_, topic_id_, timeout_observation_ms, callback_); + observer->Start(timeout_observation_ms, callback_); } return(true); @@ -398,7 +398,7 @@ namespace eCAL { auto observer = std::make_shared(); observer->Create(memfile_name_, memfile_event_); - observer->Start(topic_name_, topic_id_, timeout_observation_ms, callback_); + observer->Start(timeout_observation_ms, callback_); m_observer_pool[memfile_name_] = observer; #ifndef NDEBUG // log it diff --git a/ecal/core/src/io/shm/ecal_memfile_pool.h b/ecal/core/src/io/shm/ecal_memfile_pool.h index 7d83e757..cf64438d 100644 --- a/ecal/core/src/io/shm/ecal_memfile_pool.h +++ b/ecal/core/src/io/shm/ecal_memfile_pool.h @@ -42,7 +42,7 @@ namespace eCAL { - using MemFileDataCallbackT = std::function; + using MemFileDataCallbackT = std::function; //////////////////////////////////////// // CMemFileObserver @@ -61,14 +61,14 @@ namespace eCAL bool Create(const std::string& memfile_name_, const std::string& memfile_event_); bool Destroy(); - bool Start(const std::string& topic_name_, const std::string& topic_id_, int timeout_, const MemFileDataCallbackT& callback_); + bool Start(int timeout_, const MemFileDataCallbackT& callback_); bool Stop(); bool IsObserving() {return(m_is_observing);}; bool ResetTimeout(); protected: - void Observe(const std::string& topic_name_, const std::string& topic_id_, int timeout_); + void Observe(int timeout_); bool ReadFileHeader(SMemFileHeader& memfile_hdr); std::atomic m_created; @@ -97,7 +97,7 @@ namespace eCAL void Start(); void Stop(); - bool ObserveFile(const std::string& memfile_name_, const std::string& memfile_event_, const std::string& topic_name_, const std::string& topic_id_, int timeout_observation_ms, const MemFileDataCallbackT& callback_); + bool ObserveFile(const std::string& memfile_name_, const std::string& memfile_event_, int timeout_observation_ms, const MemFileDataCallbackT& callback_); protected: void CleanupPoolThread(); diff --git a/ecal/core/src/logging/config/attributes/logging_attributes.h b/ecal/core/src/logging/config/attributes/logging_attributes.h new file mode 100644 index 00000000..9105bc77 --- /dev/null +++ b/ecal/core/src/logging/config/attributes/logging_attributes.h @@ -0,0 +1,86 @@ +/* ========================= 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 +{ + namespace Logging + { + struct SUDP + { + bool enabled; + unsigned int port; + eCAL_Logging_Filter filter_log; + }; + + struct SFile + { + bool enabled; + std::string path; + eCAL_Logging_Filter filter_log; + }; + + struct SConsole + { + bool enabled; + eCAL_Logging_Filter filter_log; + }; + + struct SUDPSender + { + std::string address; + int port; + int ttl; + bool broadcast; + bool loopback; + int sndbuf; + }; + + struct SUDPReceiver + { + std::string address; + int port; + bool broadcast; + bool loopback; + int rcvbuf; + }; + + struct SAttributes + { + SUDP udp; + SFile file; + SConsole console; + + SUDPSender udp_sender; + SUDPReceiver udp_receiver; + + int process_id; + bool network_enabled; + std::string host_name; + std::string process_name; + std::string unit_name; + eCAL_Logging_eLogLevel level; + }; + } +} \ No newline at end of file diff --git a/ecal/core/src/logging/config/builder/udp_attribute_builder.cpp b/ecal/core/src/logging/config/builder/udp_attribute_builder.cpp new file mode 100644 index 00000000..ff193f87 --- /dev/null +++ b/ecal/core/src/logging/config/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 Logging + { + namespace UDP + { + eCAL::UDP::SSenderAttr ConvertToIOUDPSenderAttributes(const Logging::SUDPSender& 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 Logging::SUDPReceiver& 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/logging/config/builder/udp_attribute_builder.h b/ecal/core/src/logging/config/builder/udp_attribute_builder.h new file mode 100644 index 00000000..bead7d64 --- /dev/null +++ b/ecal/core/src/logging/config/builder/udp_attribute_builder.h @@ -0,0 +1,38 @@ +/* ========================= 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 "logging/config/attributes/logging_attributes.h" + +namespace eCAL +{ + namespace Logging + { + namespace UDP + { + eCAL::UDP::SSenderAttr ConvertToIOUDPSenderAttributes (const Logging::SUDPSender& sender_attr_); + eCAL::UDP::SReceiverAttr ConvertToIOUDPReceiverAttributes (const Logging::SUDPReceiver& receiver_attr_); + } + } +} \ No newline at end of file diff --git a/ecal/core/src/logging/ecal_log.cpp b/ecal/core/src/logging/ecal_log.cpp index a8bb3f77..b3e49bb4 100644 --- a/ecal/core/src/logging/ecal_log.cpp +++ b/ecal/core/src/logging/ecal_log.cpp @@ -40,6 +40,36 @@ namespace eCAL if(g_log() != nullptr) g_log()->SetLogLevel(level_); } + /** + * @brief Sets the file log filter. + * + * @param filter_ The filter; + */ + void SetFileLogFilter(eCAL_Logging_Filter filter_) + { + if(g_log() != nullptr) g_log()->SetFileLogFilter(filter_); + } + + /** + * @brief Sets the udp log filter. + * + * @param filter_ The filter; + */ + void SetUDPLogFilter(eCAL_Logging_Filter filter_) + { + if(g_log() != nullptr) g_log()->SetUDPLogFilter(filter_); + } + + /** + * @brief Sets the console log filter. + * + * @param filter_ The filter; + */ + void SetConsoleLogFilter(eCAL_Logging_Filter filter_) + { + if(g_log() != nullptr) g_log()->SetConsoleLogFilter(filter_); + } + /** * @brief Get the current log level. * diff --git a/ecal/core/src/logging/ecal_log_impl.cpp b/ecal/core/src/logging/ecal_log_impl.cpp index 17da3e85..76f1d5b1 100644 --- a/ecal/core/src/logging/ecal_log_impl.cpp +++ b/ecal/core/src/logging/ecal_log_impl.cpp @@ -26,9 +26,8 @@ #include #include "ecal_log_impl.h" -#include "io/udp/ecal_udp_configurations.h" #include "serialization/ecal_serialize_logging.h" - +#include "config/builder/udp_attribute_builder.h" #include #include @@ -45,7 +44,7 @@ #include namespace -{ +{ bool isDirectory(const std::string& path_) { if (path_.empty()) return false; @@ -93,16 +92,64 @@ static std::string get_time_str() } #endif +namespace +{ + void logWarningToConsole(const std::string& msg_) + { + std::cout << "[eCAL][Logging][Warning] " << msg_ << "\n"; + } + + void createLogHeader(std::stringstream& msg_stream, const eCAL_Logging_eLogLevel level_, const eCAL::Logging::SAttributes& attr_, const eCAL::Time::ecal_clock::time_point& log_time_) + { + msg_stream << std::chrono::duration_cast(log_time_.time_since_epoch()).count(); + msg_stream << " ms"; + msg_stream << " | "; + msg_stream << attr_.host_name; + msg_stream << " | "; + msg_stream << attr_.unit_name; + msg_stream << " | "; + msg_stream << attr_.process_id; + msg_stream << " | "; + switch(level_) + { + case log_level_none: + case log_level_all: + break; + case log_level_info: + msg_stream << "info"; + break; + case log_level_warning: + msg_stream << "warning"; + break; + case log_level_error: + msg_stream << "error"; + break; + case log_level_fatal: + msg_stream << "fatal"; + break; + case log_level_debug1: + msg_stream << "debug1"; + break; + case log_level_debug2: + msg_stream << "debug2"; + break; + case log_level_debug3: + msg_stream << "debug3"; + break; + case log_level_debug4: + msg_stream << "debug4"; + break; + } + msg_stream << " | "; + } +} + namespace eCAL { - CLog::CLog() : + CLog::CLog(const Logging::SAttributes& attr_) : m_created(false), - m_pid(0), - m_logfile(nullptr), - m_level(log_level_none), - m_filter_mask_con(log_level_info | log_level_warning | log_level_error | log_level_fatal), - m_filter_mask_file(log_level_info | log_level_warning | log_level_error | log_level_fatal | log_level_debug1 | log_level_debug2), - m_filter_mask_udp(log_level_info | log_level_warning | log_level_error | log_level_fatal | log_level_debug1 | log_level_debug2) + m_attributes(attr_), + m_logfile(nullptr) { } @@ -113,55 +160,53 @@ namespace eCAL void CLog::Start() { - m_hname = Process::GetHostName(); - m_pid = Process::GetProcessID(); - m_pname = Process::GetProcessName(); - m_level = log_level_info; - - // parse logging filter strings - m_filter_mask_con = Config::GetConsoleLogFilter(); - m_filter_mask_file = Config::GetFileLogFilter(); - m_filter_mask_udp = Config::GetUdpLogFilter(); - - // create log file - if(m_filter_mask_file != 0) + // create log file if file logging is enabled + if(m_attributes.file.enabled) { - // check ECAL_DATA - const std::string ecal_log_path = Util::GeteCALLogPath(); - if (!isDirectory(ecal_log_path)) return; - - const std::string tstring = get_time_str(); + if (isDirectory(m_attributes.file.path)) + { + const std::string tstring = get_time_str(); + + m_logfile_name = m_attributes.file.path + tstring + "_" + m_attributes.unit_name + "_" + std::to_string(m_attributes.process_id) + ".log"; + m_logfile = fopen(m_logfile_name.c_str(), "w"); + } + else + { + logWarningToConsole("Logging for file enabled, but specified path to log is not valid: " + m_attributes.file.path); + } - m_logfile_name = ecal_log_path + tstring + "_" + eCAL::Process::GetUnitName() + "_" + std::to_string(m_pid) + ".log"; - m_logfile = fopen(m_logfile_name.c_str(), "w"); + if (m_logfile == nullptr) + { + logWarningToConsole("Logging for file enabled, but file could not be created."); + } + } - if(m_filter_mask_udp != 0) + if(m_attributes.udp.enabled) { // set logging send network attributes - eCAL::UDP::SSenderAttr attr; - attr.address = UDP::GetLoggingAddress(); - attr.port = UDP::GetLoggingPort(); - attr.ttl = UDP::GetMulticastTtl(); - attr.broadcast = UDP::IsBroadcast(); - attr.loopback = true; - attr.sndbuf = UDP::GetSendBufferSize(); + const eCAL::UDP::SSenderAttr attr = Logging::UDP::ConvertToIOUDPSenderAttributes(m_attributes.udp_sender); // create udp logging sender m_udp_logging_sender = std::make_unique(attr); + + if(m_udp_logging_sender == nullptr) + { + logWarningToConsole("Logging for UDP enabled, but sender could not be created."); + } } // set logging receive network attributes - eCAL::UDP::SReceiverAttr attr; - attr.address = UDP::GetLoggingAddress(); - attr.port = UDP::GetLoggingPort(); - attr.broadcast = UDP::IsBroadcast(); - attr.loopback = true; - attr.rcvbuf = UDP::GetReceiveBufferSize(); + const eCAL::UDP::SReceiverAttr attr = Logging::UDP::ConvertToIOUDPReceiverAttributes(m_attributes.udp_receiver); // 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)); + if(m_log_receiver == nullptr) + { + logWarningToConsole("Logging receiver could not be created."); + } + m_created = true; } @@ -182,13 +227,28 @@ namespace eCAL void CLog::SetLogLevel(const eCAL_Logging_eLogLevel level_) { const std::lock_guard lock(m_log_mtx); - m_level = level_; + m_attributes.level = level_; + } + + void CLog::SetFileLogFilter(eCAL_Logging_Filter filter_) + { + m_attributes.file.filter_log = filter_; + } + + void CLog::SetUDPLogFilter(eCAL_Logging_Filter filter_) + { + m_attributes.udp.filter_log = filter_; + } + + void CLog::SetConsoleLogFilter(eCAL_Logging_Filter filter_) + { + m_attributes.console.filter_log = filter_; } eCAL_Logging_eLogLevel CLog::GetLogLevel() { const std::lock_guard lock(m_log_mtx); - return(m_level); + return(m_attributes.level); } void CLog::Log(const eCAL_Logging_eLogLevel level_, const std::string& msg_) @@ -198,89 +258,56 @@ namespace eCAL if(!m_created) return; if(msg_.empty()) return; - const eCAL_Logging_Filter log_con = level_ & m_filter_mask_con; - const eCAL_Logging_Filter log_file = level_ & m_filter_mask_file; - const eCAL_Logging_Filter log_udp = level_ & m_filter_mask_udp; + const eCAL_Logging_Filter log_con = level_ & m_attributes.console.filter_log; + const eCAL_Logging_Filter log_file = level_ & m_attributes.file.filter_log; + const eCAL_Logging_Filter log_udp = level_ & m_attributes.udp.filter_log; if((log_con | log_file | log_udp) == 0) return; auto log_time = eCAL::Time::ecal_clock::now(); - if(log_con != 0) - { - std::cout << msg_ << '\n'; - } + const bool log_to_console = m_attributes.console.enabled && log_con != 0; + const bool log_to_file = m_attributes.file.enabled && log_file != 0; - if((log_file != 0) && (m_logfile != nullptr)) + if (log_to_console || log_to_file) { - std::stringstream msg_stream; - msg_stream << std::chrono::duration_cast(log_time.time_since_epoch()).count(); - msg_stream << " ms"; - msg_stream << " | "; - msg_stream << m_hname; - msg_stream << " | "; - msg_stream << eCAL::Process::GetUnitName(); - msg_stream << " | "; - msg_stream << m_pid; - msg_stream << " | "; - switch(level_) + std::stringstream string_stream; + createLogHeader(string_stream, level_, m_attributes, log_time); + string_stream << msg_; + + if(log_to_console) { - case log_level_none: - case log_level_all: - break; - case log_level_info: - msg_stream << "info"; - break; - case log_level_warning: - msg_stream << "warning"; - break; - case log_level_error: - msg_stream << "error"; - break; - case log_level_fatal: - msg_stream << "fatal"; - break; - case log_level_debug1: - msg_stream << "debug1"; - break; - case log_level_debug2: - msg_stream << "debug2"; - break; - case log_level_debug3: - msg_stream << "debug3"; - break; - case log_level_debug4: - msg_stream << "debug4"; - break; + std::cout << string_stream.str() << '\n'; } - msg_stream << " | "; - msg_stream << msg_; - fprintf(m_logfile, "%s\n", msg_stream.str().c_str()); - fflush(m_logfile); + if (log_to_file) + { + fprintf(m_logfile, "%s\n", string_stream.str().c_str()); + fflush(m_logfile); + } } - if((log_udp != 0) && m_udp_logging_sender) + if(m_attributes.udp.enabled && log_udp != 0 && m_udp_logging_sender) { - // set up log message - Logging::SLogMessage log_message; - log_message.time = std::chrono::duration_cast(log_time.time_since_epoch()).count(); - log_message.hname = m_hname; - log_message.pid = m_pid; - log_message.pname = m_pname; - log_message.uname = eCAL::Process::GetUnitName(); - log_message.level = level_; - log_message.content = msg_; - - // sent it - m_log_message_vec.clear(); - SerializeToBuffer(log_message, m_log_message_vec); - m_udp_logging_sender->Send("_log_message_", m_log_message_vec); + // set up log message + Logging::SLogMessage log_message; + log_message.time = std::chrono::duration_cast(log_time.time_since_epoch()).count(); + log_message.hname = m_attributes.host_name; + log_message.pid = m_attributes.process_id; + log_message.pname = m_attributes.process_name; + log_message.uname = m_attributes.unit_name; + log_message.level = level_; + log_message.content = msg_; + + // sent it + m_log_message_vec.clear(); + SerializeToBuffer(log_message, m_log_message_vec); + m_udp_logging_sender->Send("_log_message_", m_log_message_vec); } } void CLog::Log(const std::string& msg_) { - Log(m_level, msg_); + Log(m_attributes.level, msg_); } void CLog::GetLogging(std::string& log_msg_list_string_) @@ -322,7 +349,7 @@ namespace eCAL { // in "network mode" we accept all log messages // in "local mode" we accept log messages from this host only - if ((m_hname == log_message.hname) || Config::IsNetworkEnabled()) + if ((m_attributes.host_name == log_message.hname) || m_attributes.network_enabled) { const std::lock_guard lock(m_log_mtx); m_log_msglist.log_messages.emplace_back(log_message); diff --git a/ecal/core/src/logging/ecal_log_impl.h b/ecal/core/src/logging/ecal_log_impl.h index f0f3e14e..03aa0210 100644 --- a/ecal/core/src/logging/ecal_log_impl.h +++ b/ecal/core/src/logging/ecal_log_impl.h @@ -33,6 +33,7 @@ #include #include +#include "config/attributes/logging_attributes.h" #include "ecal_global_accessors.h" #include @@ -50,7 +51,7 @@ namespace eCAL /** * @brief Constructor. **/ - CLog(); + CLog(const Logging::SAttributes& attr_); /** * @brief Destructor. @@ -74,6 +75,27 @@ namespace eCAL **/ void SetLogLevel(eCAL_Logging_eLogLevel level_); + /** + * @brief Sets the log filter for file. + * + * @param filter_ The filter. + */ + void SetFileLogFilter(eCAL_Logging_Filter filter_); + + /** + * @brief Sets the log filter for udp. + * + * @param filter_ The filter. + */ + void SetUDPLogFilter(eCAL_Logging_Filter filter_); + + /** + * @brief Sets the log filter for console. + * + * @param filter_ The filter. + */ + void SetConsoleLogFilter(eCAL_Logging_Filter filter_); + /** * @brief Set the current log level. * @@ -118,16 +140,9 @@ namespace eCAL // udp logging receiver std::shared_ptr m_log_receiver; - std::string m_hname; - int m_pid; - std::string m_pname; - std::string m_logfile_name; FILE* m_logfile; - eCAL_Logging_eLogLevel m_level; - eCAL_Logging_Filter m_filter_mask_con; - eCAL_Logging_Filter m_filter_mask_file; - eCAL_Logging_Filter m_filter_mask_udp; + Logging::SAttributes m_attributes; }; } diff --git a/ecal/core/src/monitoring/config/attributes/monitoring_attributes.h b/ecal/core/src/monitoring/config/attributes/monitoring_attributes.h new file mode 100644 index 00000000..baffb87b --- /dev/null +++ b/ecal/core/src/monitoring/config/attributes/monitoring_attributes.h @@ -0,0 +1,35 @@ +/* ========================= 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 Monitoring + { + struct SAttributes + { + 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 e5f115fa..bd17ea16 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 ca567d0c..647cd8bc 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 "config/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_filter.cpp b/ecal/core/src/monitoring/ecal_monitoring_filter.cpp new file mode 100644 index 00000000..a7767cef --- /dev/null +++ b/ecal/core/src/monitoring/ecal_monitoring_filter.cpp @@ -0,0 +1,154 @@ +/* ========================= 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 Filter that checks if a topic / service is to be listed in the monitoring. +**/ + +#include + +#include +#include + +#include + +#ifdef ECAL_OS_LINUX +#include // strcasecmp +#endif + +namespace +{ + struct InsensitiveCompare + { + bool operator() (const std::string& a, const std::string& b) const + { +#ifdef ECAL_OS_WINDOWS + return _stricmp(a.c_str(), b.c_str()) < 0; +#endif +#ifdef ECAL_OS_LINUX + return strcasecmp(a.c_str(), b.c_str()) < 0; +#endif + } + }; + + using StrICaseSetT = std::set; + + void Tokenize(const std::string& str, StrICaseSetT& tokens, const std::string& delimiters, bool trimEmpty) + { + std::string::size_type pos = 0; + std::string::size_type lastPos = 0; + + for (;;) + { + pos = str.find_first_of(delimiters, lastPos); + if (pos == std::string::npos) + { + pos = str.length(); + if (pos != lastPos || !trimEmpty) + { + tokens.emplace(str.data() + lastPos, pos - lastPos); + } + break; + } + else + { + if (pos != lastPos || !trimEmpty) + { + tokens.emplace(str.data() + lastPos, pos - lastPos); + } + } + lastPos = pos + 1; + } + } + + std::vector CreateRegexVector(const std::string& filter_) + { + std::vector regex_vector; + StrICaseSetT compare_set; + Tokenize(filter_, compare_set, ",;", true); + for (const auto& it : compare_set) + { + regex_vector.emplace_back(it, std::regex::icase); + } + return regex_vector; + } + + bool MatchAnyRegex(const std::string& topic_name, const std::vector& regexes) + { + for (const auto& regex : regexes) + { + if (std::regex_match(topic_name, regex)) + { + return true; + } + } + return false; + } +} + + + +eCAL::CMonitoringFilter::CMonitoringFilter(const Monitoring::SAttributes& attr_) + : m_attributes(attr_) +{ + ActivateFilter(); +} + +void eCAL::CMonitoringFilter::SetExclFilter(const std::string& filter_) +{ + m_attributes.filter_excl = filter_; +} + +void eCAL::CMonitoringFilter::SetInclFilter(const std::string& filter_) +{ + m_attributes.filter_incl = filter_; +} + +bool eCAL::CMonitoringFilter::AcceptTopic(const std::string& topic_name) const +{ + // topics are rejected if: + // a) they are matched by the exclude list + // b) there exists an include list, and they are not in the include list + // topics are accepted if they are not rejected. + + const bool reject_because_excluded = MatchAnyRegex(topic_name, m_exclude_filters); + if (reject_because_excluded) + return false; + + if (!m_include_filters.empty()) + { + const bool topic_is_included = MatchAnyRegex(topic_name, m_include_filters); + if (!topic_is_included) + return false; + } + + return true; +} + +void eCAL::CMonitoringFilter::ActivateFilter() +{ + m_exclude_filters = CreateRegexVector(m_attributes.filter_excl); + m_include_filters = CreateRegexVector(m_attributes.filter_incl); +} + +void eCAL::CMonitoringFilter::DeactivateFilter() +{ + m_exclude_filters.clear(); + m_include_filters.clear(); +} \ No newline at end of file diff --git a/ecal/core/src/monitoring/ecal_monitoring_filter.h b/ecal/core/src/monitoring/ecal_monitoring_filter.h new file mode 100644 index 00000000..0eff9a3e --- /dev/null +++ b/ecal/core/src/monitoring/ecal_monitoring_filter.h @@ -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 ================================= +*/ + +/** + * @brief Filter that checks if a topic / service is to be listed in the monitoring. +**/ + +#include +#include +#include + +#include "monitoring/config/attributes/monitoring_attributes.h" + +namespace eCAL +{ + class CMonitoringFilter + { + public: + CMonitoringFilter(const Monitoring::SAttributes& attr_); + + // Sets the filters. The user needs to call activate filter, after setting them. + void SetExclFilter(const std::string& filter_); + void SetInclFilter(const std::string& filter_); + + // Returns true if topic is accepted by the filter, false otherwise + bool AcceptTopic(const std::string& topic_name) const; + + void ActivateFilter(); + void DeactivateFilter(); + + private: + Monitoring::SAttributes m_attributes; + std::vector m_include_filters; + std::vector m_exclude_filters; + }; +} \ No newline at end of file diff --git a/ecal/core/src/monitoring/ecal_monitoring_impl.cpp b/ecal/core/src/monitoring/ecal_monitoring_impl.cpp index 820df996..dc2e10a2 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_impl.cpp +++ b/ecal/core/src/monitoring/ecal_monitoring_impl.cpp @@ -28,8 +28,6 @@ #include "ecal_monitoring_impl.h" #include "ecal_global_accessors.h" -#include - #include "registration/ecal_registration_provider.h" #include "registration/ecal_registration_receiver.h" @@ -41,13 +39,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_monitoring_filter(attr_) { } @@ -55,16 +49,12 @@ 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 receiver to enrich monitor information 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(); - // setup filtering on by default SetFilterState(true); @@ -80,40 +70,26 @@ namespace eCAL void CMonitoringImpl::SetExclFilter(const std::string& filter_) { - m_topic_filter_excl_s = filter_; + const std::lock_guard lock(m_monitoring_filter_mtx); + m_monitoring_filter.SetExclFilter(filter_); } void CMonitoringImpl::SetInclFilter(const std::string& filter_) { - m_topic_filter_incl_s = filter_; + const std::lock_guard lock(m_monitoring_filter_mtx); + m_monitoring_filter.SetInclFilter(filter_); } void CMonitoringImpl::SetFilterState(bool state_) { + const std::lock_guard lock(m_monitoring_filter_mtx); if (state_) { - // 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); - } - - // 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); - } + m_monitoring_filter.ActivateFilter(); } else { - { - const std::lock_guard lock(m_topic_filter_excl_mtx); - m_topic_filter_excl.clear(); - } - { - const std::lock_guard lock(m_topic_filter_incl_mtx); - m_topic_filter_incl.clear(); - } + m_monitoring_filter.DeactivateFilter(); } } @@ -211,38 +187,21 @@ namespace eCAL } const int32_t connections_loc = sample_topic.connections_loc; const int32_t connections_ext = sample_topic.connections_ext; - const int64_t did = sample_topic.did; - const int64_t dclock = sample_topic.dclock; - const int32_t message_drops = sample_topic.message_drops; - const int32_t dfreq = sample_topic.dfreq; + const int64_t did = sample_topic.did; + const int64_t dclock = sample_topic.dclock; + const int32_t message_drops = sample_topic.message_drops; + const int32_t dfreq = sample_topic.dfreq; - // check blacklist topic filter + bool process_topic{false}; { - const std::lock_guard lock(m_topic_filter_excl_mtx); - for (const auto& it : m_topic_filter_excl) - { - if (std::regex_match(topic_name, std::regex(it, std::regex::icase))) - return(false); - } + const std::lock_guard lock(m_monitoring_filter_mtx); + process_topic = m_monitoring_filter.AcceptTopic(topic_name); } - - // check whitelist topic filter - bool is_topic_in_filter(false); + if (!process_topic) { - const std::lock_guard lock(m_topic_filter_incl_mtx); - is_topic_in_filter = m_topic_filter_incl.empty(); - for (const auto& it : m_topic_filter_incl) - { - if (std::regex_match(topic_name, std::regex(it, std::regex::icase))) - { - is_topic_in_filter = true; - break; - } - } + return false; } - if (!is_topic_in_filter) return (false); - ///////////////////////////////// // register in topic map ///////////////////////////////// @@ -626,7 +585,6 @@ namespace eCAL monitoring_.processes.reserve(m_process_map.map->size()); // iterate map - m_process_map.map->erase_expired(); for (const auto& process : (*m_process_map.map)) { monitoring_.processes.emplace_back(process.second); @@ -644,7 +602,6 @@ namespace eCAL monitoring_.publisher.reserve(m_publisher_map.map->size()); // iterate map - m_publisher_map.map->erase_expired(); for (const auto& publisher : (*m_publisher_map.map)) { monitoring_.publisher.emplace_back(publisher.second); @@ -662,7 +619,6 @@ namespace eCAL monitoring_.subscriber.reserve(m_subscriber_map.map->size()); // iterate map - m_subscriber_map.map->erase_expired(); for (const auto& subscriber : (*m_subscriber_map.map)) { monitoring_.subscriber.emplace_back(subscriber.second); @@ -680,7 +636,6 @@ namespace eCAL monitoring_.server.reserve(m_server_map.map->size()); // iterate map - m_server_map.map->erase_expired(); for (const auto& server : (*m_server_map.map)) { monitoring_.server.emplace_back(server.second); @@ -698,7 +653,6 @@ namespace eCAL monitoring_.clients.reserve(m_clients_map.map->size()); // iterate map - m_clients_map.map->erase_expired(); for (const auto& client : (*m_clients_map.map)) { monitoring_.clients.emplace_back(client.second); @@ -712,7 +666,6 @@ namespace eCAL const std::lock_guard lock(m_process_map.sync); // iterate map - m_process_map.map->erase_expired(); for (const auto& process : (*m_process_map.map)) { // add process @@ -726,7 +679,6 @@ namespace eCAL const std::lock_guard lock(m_server_map.sync); // iterate map - m_server_map.map->erase_expired(); for (const auto& server : (*m_server_map.map)) { // add service @@ -740,7 +692,6 @@ namespace eCAL const std::lock_guard lock(m_clients_map.sync); // iterate map - m_clients_map.map->erase_expired(); for (const auto& client : (*m_clients_map.map)) { // add client @@ -754,7 +705,6 @@ namespace eCAL const std::lock_guard lock(map_.sync); // iterate map - map_.map->erase_expired(); for (const auto& topic : (*map_.map)) { if (direction_ == "publisher") @@ -767,32 +717,4 @@ namespace eCAL } } } - - void CMonitoringImpl::Tokenize(const std::string& str, StrICaseSetT& tokens, const std::string& delimiters, bool trimEmpty) - { - std::string::size_type pos = 0; - std::string::size_type lastPos = 0; - - for (;;) - { - pos = str.find_first_of(delimiters, lastPos); - if (pos == std::string::npos) - { - pos = str.length(); - if (pos != lastPos || !trimEmpty) - { - tokens.emplace(std::string(str.data() + lastPos, pos - lastPos)); - } - break; - } - else - { - if (pos != lastPos || !trimEmpty) - { - tokens.emplace(std::string(str.data() + lastPos, pos - lastPos)); - } - } - lastPos = pos + 1; - } - } } diff --git a/ecal/core/src/monitoring/ecal_monitoring_impl.h b/ecal/core/src/monitoring/ecal_monitoring_impl.h index 703bda85..8a8f18be 100644 --- a/ecal/core/src/monitoring/ecal_monitoring_impl.h +++ b/ecal/core/src/monitoring/ecal_monitoring_impl.h @@ -26,19 +26,17 @@ #include #include "ecal_def.h" -#include "util/ecal_expmap.h" +#include "monitoring/config/attributes/monitoring_attributes.h" +#include "monitoring/ecal_monitoring_filter.h" #include "serialization/ecal_serialize_sample_registration.h" #include +#include #include #include #include -#ifdef ECAL_OS_LINUX -#include // strcasecmp -#endif - namespace eCAL { //////////////////////////////////////// @@ -47,7 +45,7 @@ namespace eCAL class CMonitoringImpl { public: - CMonitoringImpl(); + CMonitoringImpl(const Monitoring::SAttributes& attr_); ~CMonitoringImpl() = default; void Create(); @@ -81,64 +79,50 @@ 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::CExpirationMap; + 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::CExpirationMap; + 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::CExpirationMap; + 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::CExpirationMap; + 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; std::unique_ptr map; }; - struct InsensitiveCompare - { - bool operator() (const std::string& a, const std::string& b) const - { -#ifdef ECAL_OS_WINDOWS - return _stricmp(a.c_str(), b.c_str()) < 0; -#endif -#ifdef ECAL_OS_LINUX - return strcasecmp(a.c_str(), b.c_str()) < 0; -#endif - } - }; - using StrICaseSetT = std::set; - STopicMonMap* GetMap(enum ePubSub pubsub_type_); void MonitorProcs(Monitoring::SMonitoring& monitoring_); @@ -146,18 +130,10 @@ namespace eCAL void MonitorClients(Monitoring::SMonitoring& monitoring_); void MonitorTopics(STopicMonMap& map_, Monitoring::SMonitoring& monitoring_, const std::string& direction_); - void Tokenize(const std::string& str, StrICaseSetT& tokens, const std::string& delimiters, bool trimEmpty); - bool m_init; - std::string m_host_name; - - 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; + std::mutex m_monitoring_filter_mtx; + CMonitoringFilter m_monitoring_filter; // database SProcessMonMap m_process_map; diff --git a/ecal/core/src/pubsub/config/builder/reader_attribute_builder.cpp b/ecal/core/src/pubsub/config/builder/reader_attribute_builder.cpp new file mode 100644 index 00000000..f9d72db1 --- /dev/null +++ b/ecal/core/src/pubsub/config/builder/reader_attribute_builder.cpp @@ -0,0 +1,58 @@ +/* ========================= 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 "reader_attribute_builder.h" +#include "ecal/ecal_process.h" + +namespace eCAL +{ + eCALReader::SAttributes BuildReaderAttributes(const std::string& topic_name_, const Subscriber::Configuration& sub_config_, const Publisher::Configuration& pub_config_, const eCAL::TransportLayer::Configuration& tl_config_, const eCAL::Registration::Configuration& reg_config_) + { + eCALReader::SAttributes attributes; + + attributes.network_enabled = reg_config_.network_enabled; + attributes.loopback = reg_config_.loopback; + attributes.drop_out_of_order_messages = sub_config_.drop_out_of_order_messages; + attributes.registation_timeout_ms = reg_config_.registration_timeout; + attributes.topic_name = topic_name_; + attributes.host_name = Process::GetHostName(); + attributes.host_group_name = Process::GetHostGroupName(); + attributes.process_id = Process::GetProcessID(); + attributes.process_name = Process::GetProcessName(); + attributes.share_topic_type = pub_config_.share_topic_type; + attributes.share_topic_description = pub_config_.share_topic_description; + + attributes.udp.enable = sub_config_.layer.udp.enable; + attributes.udp.mode = tl_config_.udp.mode; + attributes.udp.port = tl_config_.udp.port; + attributes.udp.receivebuffer = tl_config_.udp.receive_buffer; + + attributes.udp.local.group = tl_config_.udp.local.group; + + attributes.udp.network.group = tl_config_.udp.network.group; + + attributes.tcp.enable = sub_config_.layer.tcp.enable; + attributes.tcp.thread_pool_size = tl_config_.tcp.number_executor_reader; + attributes.tcp.max_reconnection_attempts = tl_config_.tcp.max_reconnections; + + attributes.shm.enable = sub_config_.layer.shm.enable; + + return attributes; + } +} \ No newline at end of file diff --git a/ecal/core/src/pubsub/config/builder/reader_attribute_builder.h b/ecal/core/src/pubsub/config/builder/reader_attribute_builder.h new file mode 100644 index 00000000..2aaa48b1 --- /dev/null +++ b/ecal/core/src/pubsub/config/builder/reader_attribute_builder.h @@ -0,0 +1,31 @@ +/* ========================= 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 "readwrite/config/attributes/reader_attributes.h" +#include "ecal/config/subscriber.h" +#include "ecal/config/transport_layer.h" +#include "ecal/config/registration.h" +#include "ecal/config/publisher.h" + +namespace eCAL +{ + eCALReader::SAttributes BuildReaderAttributes(const std::string& topic_name_, const Subscriber::Configuration& sub_config_, const Publisher::Configuration& pub_config_, const eCAL::TransportLayer::Configuration& tl_config_, const eCAL::Registration::Configuration& reg_config_); +} \ No newline at end of file diff --git a/ecal/core/src/pubsub/config/builder/writer_attribute_builder.cpp b/ecal/core/src/pubsub/config/builder/writer_attribute_builder.cpp new file mode 100644 index 00000000..6d12889c --- /dev/null +++ b/ecal/core/src/pubsub/config/builder/writer_attribute_builder.cpp @@ -0,0 +1,68 @@ +/* ========================= 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 "writer_attribute_builder.h" +#include "ecal/ecal_process.h" + +namespace eCAL +{ + eCALWriter::SAttributes BuildWriterAttributes(const std::string& topic_name_, const Publisher::Configuration& pub_config_, const eCAL::TransportLayer::Configuration& tl_config_, const eCAL::Registration::Configuration& reg_config_) + { + eCALWriter::SAttributes attributes; + + attributes.network_enabled = reg_config_.network_enabled; + attributes.loopback = reg_config_.loopback; + + attributes.share_topic_type = pub_config_.share_topic_type; + attributes.share_topic_description = pub_config_.share_topic_description; + attributes.layer_priority_local = pub_config_.layer_priority_local; + attributes.layer_priority_remote = pub_config_.layer_priority_remote; + + attributes.host_name = Process::GetHostName(); + attributes.host_group_name = Process::GetHostGroupName(); + attributes.process_id = Process::GetProcessID(); + attributes.process_name = Process::GetProcessName(); + + attributes.unit_name = Process::GetUnitName(); + attributes.topic_name = topic_name_; + + attributes.shm.enable = pub_config_.layer.shm.enable; + attributes.shm.acknowledge_timeout_ms = pub_config_.layer.shm.acknowledge_timeout_ms; + attributes.shm.memfile_buffer_count = pub_config_.layer.shm.memfile_buffer_count; + attributes.shm.memfile_min_size_bytes = pub_config_.layer.shm.memfile_min_size_bytes; + attributes.shm.memfile_reserve_percent = pub_config_.layer.shm.memfile_reserve_percent; + attributes.shm.zero_copy_mode = pub_config_.layer.shm.zero_copy_mode; + + attributes.udp.enable = pub_config_.layer.udp.enable; + attributes.udp.port = tl_config_.udp.port; + attributes.udp.send_buffer = tl_config_.udp.send_buffer; + attributes.udp.mode = tl_config_.udp.mode; + + attributes.udp.network.group = tl_config_.udp.network.group; + attributes.udp.network.ttl = tl_config_.udp.network.ttl; + + attributes.udp.local.group = tl_config_.udp.local.group; + attributes.udp.local.ttl = tl_config_.udp.local.ttl; + + attributes.tcp.enable = pub_config_.layer.tcp.enable; + attributes.tcp.thread_pool_size = tl_config_.tcp.number_executor_writer; + + return attributes; + } +} \ No newline at end of file diff --git a/ecal/core/src/pubsub/config/builder/writer_attribute_builder.h b/ecal/core/src/pubsub/config/builder/writer_attribute_builder.h new file mode 100644 index 00000000..dcc54bae --- /dev/null +++ b/ecal/core/src/pubsub/config/builder/writer_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 "readwrite/config/attributes/writer_attributes.h" +#include "ecal/config/publisher.h" +#include "ecal/config/registration.h" + +namespace eCAL +{ + eCALWriter::SAttributes BuildWriterAttributes(const std::string& topic_name_, const Publisher::Configuration& config_, const eCAL::TransportLayer::Configuration& tl_config_, const eCAL::Registration::Configuration& reg_config_); +} \ No newline at end of file diff --git a/ecal/core/src/pubsub/ecal_pubgate.cpp b/ecal/core/src/pubsub/ecal_pubgate.cpp index 498aec16..1ca99219 100644 --- a/ecal/core/src/pubsub/ecal_pubgate.cpp +++ b/ecal/core/src/pubsub/ecal_pubgate.cpp @@ -121,11 +121,8 @@ namespace eCAL if (topic_name.empty()) return; // TODO: Substitute ProducerInfo type - CDataWriter::SSubscriptionInfo subscription_info; - subscription_info.host_name = ecal_sample_.identifier.host_name; - subscription_info.topic_id = ecal_sample_.identifier.entity_id; - subscription_info.process_id = ecal_sample_.identifier.process_id; - const SDataTypeInformation topic_information = ecal_topic.tdatatype; + 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) @@ -181,11 +178,7 @@ namespace eCAL // check topic name if (topic_name.empty()) return; - //TODO: Remove the subscription info type - CDataWriter::SSubscriptionInfo subscription_info; - subscription_info.host_name = ecal_sample_.identifier.host_name; - subscription_info.topic_id = ecal_sample_.identifier.entity_id; - subscription_info.process_id = ecal_sample_.identifier.process_id; + const auto& subscription_info = ecal_sample_.identifier; // unregister subscriber const std::shared_lock lock(m_topic_name_datawriter_sync); @@ -204,7 +197,7 @@ namespace eCAL const std::shared_lock lock(m_topic_name_datawriter_sync); for (const auto& iter : m_topic_name_datawriter_map) { - reg_sample_list_.samples.emplace_back(iter.second->GetRegistration()); + iter.second->GetRegistration(reg_sample_list_.push_back()); } } } diff --git a/ecal/core/src/pubsub/ecal_publisher.cpp b/ecal/core/src/pubsub/ecal_publisher.cpp index 5fba62ff..5dbd7736 100644 --- a/ecal/core/src/pubsub/ecal_publisher.cpp +++ b/ecal/core/src/pubsub/ecal_publisher.cpp @@ -27,6 +27,9 @@ #include "readwrite/ecal_writer.h" #include "readwrite/ecal_writer_buffer_payload.h" +#include "config/builder/writer_attribute_builder.h" +#include "ecal/ecal_config.h" + #include #include #include @@ -91,7 +94,7 @@ namespace eCAL if (topic_name_.empty()) return(false); // create datawriter - m_datawriter = std::make_shared(topic_name_, data_type_info_, config_); + m_datawriter = std::make_shared(data_type_info_, BuildWriterAttributes(topic_name_, config_, GetTransportLayerConfiguration(), GetRegistrationConfiguration())); // register datawriter g_pubgate()->Register(topic_name_, m_datawriter); @@ -220,6 +223,12 @@ namespace eCAL return(m_datawriter->GetTopicName()); } + Registration::STopicId CPublisher::GetId() const + { + if (m_datawriter == nullptr) return{}; + return(m_datawriter->GetId()); + } + SDataTypeInformation CPublisher::GetDataTypeInformation() const { if (m_datawriter == nullptr) return(SDataTypeInformation{}); diff --git a/ecal/core/src/pubsub/ecal_subgate.cpp b/ecal/core/src/pubsub/ecal_subgate.cpp index bd2f1633..3d0366e1 100644 --- a/ecal/core/src/pubsub/ecal_subgate.cpp +++ b/ecal/core/src/pubsub/ecal_subgate.cpp @@ -124,7 +124,7 @@ namespace eCAL if (layer_ == eTLayerType::tl_none) { // log it - Logging::Log(log_level_error, ecal_sample.topic.tname + " : payload received without layer definition !"); + Logging::Log(log_level_error, ecal_sample.topic_info.tname + " : payload received without layer definition !"); } #endif @@ -153,7 +153,7 @@ namespace eCAL { // apply sample to data reader const std::shared_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(ecal_sample.topic.tname); + auto res = m_topic_name_datareader_map.equal_range(ecal_sample.topic_info.tname); std::transform( res.first, res.second, std::back_inserter(readers_to_apply), [](const auto& match) { return match.second; } ); @@ -163,7 +163,7 @@ namespace eCAL for (const auto& reader : readers_to_apply) { applied_size = reader->ApplySample( - ecal_sample.topic.tid, + ecal_sample.topic_info, payload_addr, payload_size, ecal_sample_content.id, @@ -182,7 +182,7 @@ namespace eCAL return (applied_size > 0); } - bool CSubGate::ApplySample(const std::string& topic_name_, const std::string& topic_id_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_) + bool CSubGate::ApplySample(const Payload::TopicInfo& topic_info_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_) { if (!m_created) return false; @@ -194,7 +194,7 @@ namespace eCAL // Apply the samples to the readers afterwards. { const std::shared_lock lock(m_topic_name_datareader_sync); - auto res = m_topic_name_datareader_map.equal_range(topic_name_); + auto res = m_topic_name_datareader_map.equal_range(topic_info_.tname); std::transform( res.first, res.second, std::back_inserter(readers_to_apply), [](const auto& match) { return match.second; } ); @@ -203,7 +203,7 @@ namespace eCAL for (const auto& reader : readers_to_apply) { - applied_size = reader->ApplySample(topic_id_, buf_, len_, id_, clock_, time_, hash_, layer_); + applied_size = reader->ApplySample(topic_info_, buf_, len_, id_, clock_, time_, hash_, layer_); } return (applied_size > 0); @@ -219,12 +219,8 @@ namespace eCAL // check topic name if (topic_name.empty()) return; - // TODO: Remove the PublicationInfo type - CDataReader::SPublicationInfo publication_info; - publication_info.host_name = ecal_sample_.identifier.host_name; - publication_info.topic_id = ecal_sample_.identifier.entity_id; - publication_info.process_id = ecal_sample_.identifier.process_id; - const SDataTypeInformation topic_information = ecal_topic.tdatatype; + 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) @@ -260,16 +256,7 @@ namespace eCAL { iter->second->ApplyLayerParameter(publication_info, tlayer.type, tlayer.par_layer); } - // 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); - } + iter->second->ApplyPublication(publication_info, topic_information, layer_states); } } @@ -283,11 +270,7 @@ namespace eCAL // check topic name if (topic_name.empty()) return; - // TODO: Remove the SPublicationInfo type! - CDataReader::SPublicationInfo publication_info; - publication_info.host_name = ecal_sample_.identifier.host_name; - publication_info.topic_id = ecal_sample_.identifier.entity_id; - publication_info.process_id = ecal_sample_.identifier.process_id; + const auto& publication_info = ecal_sample_.identifier; // unregister publisher const std::shared_lock lock(m_topic_name_datareader_sync); @@ -306,7 +289,7 @@ namespace eCAL const std::shared_lock lock(m_topic_name_datareader_sync); for (const auto& iter : m_topic_name_datareader_map) { - reg_sample_list_.samples.emplace_back(iter.second->GetRegistration()); + iter.second->GetRegistration(reg_sample_list_.push_back()); } } } diff --git a/ecal/core/src/pubsub/ecal_subgate.h b/ecal/core/src/pubsub/ecal_subgate.h index cc1b589c..cf5a0d3e 100644 --- a/ecal/core/src/pubsub/ecal_subgate.h +++ b/ecal/core/src/pubsub/ecal_subgate.h @@ -49,7 +49,7 @@ namespace eCAL bool HasSample(const std::string& sample_name_); bool ApplySample(const char* serialized_sample_data_, size_t serialized_sample_size_, eTLayerType layer_); - bool ApplySample(const std::string& topic_name_, const std::string& topic_id_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_); + bool ApplySample(const Payload::TopicInfo& topic_info_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_, eTLayerType layer_); void ApplyPubRegistration(const Registration::Sample& ecal_sample_); void ApplyPubUnregistration(const Registration::Sample& ecal_sample_); diff --git a/ecal/core/src/pubsub/ecal_subscriber.cpp b/ecal/core/src/pubsub/ecal_subscriber.cpp index 4e436605..bb234081 100644 --- a/ecal/core/src/pubsub/ecal_subscriber.cpp +++ b/ecal/core/src/pubsub/ecal_subscriber.cpp @@ -25,6 +25,7 @@ #include "ecal_globals.h" #include "readwrite/ecal_reader.h" +#include "config/builder/reader_attribute_builder.h" #include #include @@ -81,7 +82,7 @@ namespace eCAL if (topic_name_.empty()) return(false); // create datareader - m_datareader = std::make_shared(topic_name_, data_type_info_, config_); + m_datareader = std::make_shared(data_type_info_, BuildReaderAttributes(topic_name_, config_, GetPublisherConfiguration(), GetTransportLayerConfiguration(), GetRegistrationConfiguration())); // register datareader g_subgate()->Register(topic_name_, m_datareader); @@ -148,7 +149,16 @@ namespace eCAL bool CSubscriber::AddReceiveCallback(ReceiveCallbackT callback_) { - if(m_datareader == nullptr) return(false); + auto id_callback = [callback_](const Registration::STopicId& topic_id_, const SDataTypeInformation&, const SReceiveCallbackData& data_) + { + callback_(topic_id_.topic_name.c_str(), &data_); + }; + return AddReceiveCallback(id_callback); + } + + bool CSubscriber::AddReceiveCallback(ReceiveIDCallbackT callback_) + { + if (m_datareader == nullptr) return(false); RemReceiveCallback(); return(m_datareader->AddReceiveCallback(std::move(callback_))); } @@ -189,6 +199,12 @@ namespace eCAL if(m_datareader == nullptr) return(""); return(m_datareader->GetTopicName()); } + + Registration::STopicId CSubscriber::GetId() const + { + if (m_datareader == nullptr) return{}; + return(m_datareader->GetId()); + } SDataTypeInformation CSubscriber::GetDataTypeInformation() const { diff --git a/ecal/core/src/readwrite/config/attributes/reader_attributes.h b/ecal/core/src/readwrite/config/attributes/reader_attributes.h new file mode 100644 index 00000000..0d8ad85e --- /dev/null +++ b/ecal/core/src/readwrite/config/attributes/reader_attributes.h @@ -0,0 +1,78 @@ +/* ========================= 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 "ecal/types/ecal_custom_data_types.h" + +namespace eCAL +{ + namespace eCALReader + { + struct SUDPModeAttributes + { + std::string group; + }; + + struct SUDPAttributes + { + bool enable; + Types::UDPMode mode; + int port; + int receivebuffer; + SUDPModeAttributes network; + SUDPModeAttributes local; + }; + + struct STCPAttributes + { + bool enable; + size_t thread_pool_size; + int max_reconnection_attempts; + }; + + struct SSHMAttributes + { + bool enable; + }; + + struct SAttributes + { + bool network_enabled; + bool drop_out_of_order_messages; + bool loopback; + unsigned int registation_timeout_ms; + + SUDPAttributes udp; + STCPAttributes tcp; + SSHMAttributes shm; + + std::string topic_name; + std::string host_name; + std::string host_group_name; + int process_id; + std::string process_name; + std::string unit_name; + bool share_topic_type; + bool share_topic_description; + }; + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/config/attributes/writer_attributes.h b/ecal/core/src/readwrite/config/attributes/writer_attributes.h new file mode 100644 index 00000000..36257241 --- /dev/null +++ b/ecal/core/src/readwrite/config/attributes/writer_attributes.h @@ -0,0 +1,92 @@ +/* ========================= 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 + +#include + +#include "ecal/types/ecal_custom_data_types.h" + +namespace eCAL +{ + namespace eCALWriter + { + struct SUDPModeAttributes + { + std::string group; + int ttl; + }; + + struct SUDPAttributes + { + bool enable; + Types::UDPMode mode; + int port; + int send_buffer; + SUDPModeAttributes network; + SUDPModeAttributes local; + }; + + struct STCPAttributes + { + bool enable; + size_t thread_pool_size; + }; + + struct SSHMAttributes + { + bool enable; + bool zero_copy_mode; + unsigned int acknowledge_timeout_ms; + unsigned int memfile_buffer_count; + unsigned int memfile_min_size_bytes; + unsigned int memfile_reserve_percent; + }; + + + struct SAttributes + { + using LayerPriorityVector = std::vector; + LayerPriorityVector layer_priority_local; + LayerPriorityVector layer_priority_remote; + + bool share_topic_type; + bool share_topic_description; + + bool network_enabled; + bool loopback; + + std::string host_name; + std::string host_group_name; + int process_id; + std::string process_name; + + std::string unit_name; + std::string topic_name; + + SUDPAttributes udp; + STCPAttributes tcp; + SSHMAttributes shm; + }; + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/config/builder/shm_attribute_builder.cpp b/ecal/core/src/readwrite/config/builder/shm_attribute_builder.cpp new file mode 100644 index 00000000..f1b7105a --- /dev/null +++ b/ecal/core/src/readwrite/config/builder/shm_attribute_builder.cpp @@ -0,0 +1,54 @@ +/* ========================= 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 "shm_attribute_builder.h" + +namespace eCAL +{ + namespace eCALReader + { + SHM::SAttributes BuildSHMAttributes(const eCALReader::SAttributes& attr_) + { + SHM::SAttributes attributes; + + attributes.process_id = attr_.process_id; + attributes.registration_timeout_ms = attr_.registation_timeout_ms; + + return attributes; + } + } + + namespace eCALWriter + { + SHM::SAttributes BuildSHMAttributes(const eCALWriter::SAttributes& attr_) + { + SHM::SAttributes attributes; + + attributes.acknowledge_timeout_ms = attr_.shm.acknowledge_timeout_ms; + attributes.memfile_buffer_count = attr_.shm.memfile_buffer_count; + attributes.memfile_reserve_percent = attr_.shm.memfile_reserve_percent; + attributes.memfile_min_size_bytes = attr_.shm.memfile_min_size_bytes; + + attributes.topic_name = attr_.topic_name; + attributes.host_name = attr_.host_name; + + return attributes; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/config/builder/shm_attribute_builder.h b/ecal/core/src/readwrite/config/builder/shm_attribute_builder.h new file mode 100644 index 00000000..b16af644 --- /dev/null +++ b/ecal/core/src/readwrite/config/builder/shm_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 "readwrite/shm/config/attributes/reader_shm_attributes.h" +#include "readwrite/config/attributes/reader_attributes.h" + +#include "readwrite/shm/config/attributes/writer_shm_attributes.h" +#include "readwrite/config/attributes/writer_attributes.h" + +namespace eCAL +{ + namespace eCALReader + { + SHM::SAttributes BuildSHMAttributes(const eCALReader::SAttributes& attr_); + } + + namespace eCALWriter + { + SHM::SAttributes BuildSHMAttributes(const eCALWriter::SAttributes& attr_); + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/config/builder/tcp_attribute_builder.cpp b/ecal/core/src/readwrite/config/builder/tcp_attribute_builder.cpp new file mode 100644 index 00000000..6ffff15c --- /dev/null +++ b/ecal/core/src/readwrite/config/builder/tcp_attribute_builder.cpp @@ -0,0 +1,50 @@ +/* ========================= 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 "tcp_attribute_builder.h" + +namespace eCAL +{ + namespace eCALReader + { + TCPLayer::SAttributes BuildTCPLayerAttributes(const eCALReader::SAttributes& attr_) + { + TCPLayer::SAttributes attributes; + + attributes.max_reconnection_attempts = attr_.tcp.max_reconnection_attempts; + attributes.thread_pool_size = attr_.tcp.thread_pool_size; + + return attributes; + } + } + + namespace eCALWriter + { + TCP::SAttributes BuildTCPAttributes(const std::string& topic_id_, const eCALWriter::SAttributes& attr_) + { + TCP::SAttributes attributes; + + attributes.topic_name = attr_.topic_name; + attributes.topic_id = topic_id_; + attributes.thread_pool_size = attr_.tcp.thread_pool_size; + + return attributes; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/config/builder/tcp_attribute_builder.h b/ecal/core/src/readwrite/config/builder/tcp_attribute_builder.h new file mode 100644 index 00000000..dab81def --- /dev/null +++ b/ecal/core/src/readwrite/config/builder/tcp_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 "readwrite/tcp/config/attributes/tcp_reader_layer_attributes.h" +#include "readwrite/config/attributes/reader_attributes.h" + +#include "readwrite/tcp/config/attributes/data_writer_tcp_attributes.h" +#include "readwrite/config/attributes/writer_attributes.h" + +namespace eCAL +{ + namespace eCALReader + { + TCPLayer::SAttributes BuildTCPLayerAttributes(const eCALReader::SAttributes& attr_); + } + + namespace eCALWriter + { + TCP::SAttributes BuildTCPAttributes(const std::string& topic_id_, const eCALWriter::SAttributes& attr_); + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/config/builder/udp_attribute_builder.cpp b/ecal/core/src/readwrite/config/builder/udp_attribute_builder.cpp new file mode 100644 index 00000000..07633ee9 --- /dev/null +++ b/ecal/core/src/readwrite/config/builder/udp_attribute_builder.cpp @@ -0,0 +1,86 @@ +/* ========================= 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" +#include "ecal/types/ecal_custom_data_types.h" + +namespace eCAL +{ + namespace eCALReader + { + UDP::SAttributes BuildUDPAttributes(const eCALReader::SAttributes& attr_) + { + UDP::SAttributes attributes; + + attributes.broadcast = !attr_.network_enabled; + attributes.loopback = true; + + attributes.receive_buffer = attr_.udp.receivebuffer; + attributes.port = attr_.udp.port; + + switch (attr_.udp.mode) + { + case Types::UDPMode::NETWORK: + attributes.address = attr_.udp.network.group; + break; + case Types::UDPMode::LOCAL: + attributes.address = attr_.udp.local.group; + break; + default: + break; + } + + return attributes; + } + } + + namespace eCALWriter + { + UDP::SAttributes BuildUDPAttributes(const std::string& topic_id_, const eCALWriter::SAttributes& attr_) + { + UDP::SAttributes attributes; + + attributes.broadcast = !attr_.network_enabled; + attributes.loopback = attr_.loopback; + + attributes.topic_id = topic_id_; + attributes.topic_name = attr_.topic_name; + attributes.host_name = attr_.host_name; + + attributes.send_buffer = attr_.udp.send_buffer; + attributes.port = attr_.udp.port; + + switch (attr_.udp.mode) + { + case Types::UDPMode::NETWORK: + attributes.address = attr_.udp.network.group; + attributes.ttl = attr_.udp.network.ttl; + break; + case Types::UDPMode::LOCAL: + attributes.address = attr_.udp.local.group; + attributes.ttl = attr_.udp.local.ttl; + break; + default: + break; + } + + return attributes; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/config/builder/udp_attribute_builder.h b/ecal/core/src/readwrite/config/builder/udp_attribute_builder.h new file mode 100644 index 00000000..e9d95c62 --- /dev/null +++ b/ecal/core/src/readwrite/config/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 "readwrite/udp/config/attributes/reader_udp_attributes.h" +#include "readwrite/config/attributes/reader_attributes.h" + +#include "readwrite/udp/config/attributes/writer_udp_attributes.h" +#include "readwrite/config/attributes/writer_attributes.h" + +namespace eCAL +{ + namespace eCALReader + { + UDP::SAttributes BuildUDPAttributes(const eCALReader::SAttributes& attr_); + } + + namespace eCALWriter + { + UDP::SAttributes BuildUDPAttributes(const std::string& topic_id, const eCALWriter::SAttributes& attr_); + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/ecal_reader.cpp b/ecal/core/src/readwrite/ecal_reader.cpp index 6f590d4b..fedd83e2 100644 --- a/ecal/core/src/readwrite/ecal_reader.cpp +++ b/ecal/core/src/readwrite/ecal_reader.cpp @@ -36,14 +36,17 @@ #if ECAL_CORE_TRANSPORT_UDP #include "udp/ecal_reader_udp.h" +#include "config/builder/udp_attribute_builder.h" #endif #if ECAL_CORE_TRANSPORT_SHM #include "shm/ecal_reader_shm.h" +#include "config/builder/shm_attribute_builder.h" #endif #if ECAL_CORE_TRANSPORT_TCP #include "tcp/ecal_reader_tcp.h" +#include "config/builder/tcp_attribute_builder.h" #endif #include @@ -62,26 +65,18 @@ namespace eCAL //////////////////////////////////////// // CDataReader //////////////////////////////////////// - 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()), - m_pname(Process::GetProcessName()), - m_topic_name(topic_name_), + CDataReader::CDataReader(const SDataTypeInformation& topic_info_, const eCAL::eCALReader::SAttributes& attr_) : m_topic_info(topic_info_), m_topic_size(0), - m_config(config_), - m_connected(false), + m_attributes(attr_), m_receive_time(0), m_clock(0), m_frequency_calculator(3.0f), - m_share_ttype(Config::IsTopicTypeSharingEnabled()), - m_share_tdesc(Config::IsTopicDescriptionSharingEnabled()), m_created(false) { #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataReader::Constructor"); + Logging::Log(log_level_debug1, m_attributes.topic_name + "::CDataReader::Constructor"); #endif // build topic id @@ -89,10 +84,6 @@ 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 InitializeLayers(); StartTransportLayer(); @@ -105,7 +96,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataReader::Destructor"); + Logging::Log(log_level_debug1, m_attributes.topic_name + "::CDataReader::Destructor"); #endif Stop(); @@ -116,7 +107,7 @@ namespace eCAL if (!m_created) return false; #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataReader::Stop"); + Logging::Log(log_level_debug1, m_attributes.topic_name + "::CDataReader::Stop"); #endif // stop transport layers @@ -167,7 +158,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataReader::Receive"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataReader::Receive"); #endif // copy content to target string buf_.clear(); @@ -184,7 +175,7 @@ namespace eCAL return(false); } - bool CDataReader::AddReceiveCallback(ReceiveCallbackT callback_) + bool CDataReader::AddReceiveCallback(ReceiveIDCallbackT callback_) { if (!m_created) return(false); @@ -193,7 +184,7 @@ namespace eCAL const std::lock_guard lock(m_receive_callback_mtx); #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddReceiveCallback"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataReader::AddReceiveCallback"); #endif m_receive_callback = std::move(callback_); } @@ -210,7 +201,7 @@ namespace eCAL const std::lock_guard lock(m_receive_callback_mtx); #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemReceiveCallback"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataReader::RemReceiveCallback"); #endif m_receive_callback = nullptr; } @@ -226,7 +217,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::AddEventCallback"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataReader::AddEventCallback"); #endif const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map[type_] = std::move(callback_); @@ -243,7 +234,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::RemEventCallback"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataReader::RemEventCallback"); #endif const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map[type_] = nullptr; @@ -258,7 +249,7 @@ namespace eCAL #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::SetAttribute"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataReader::SetAttribute"); #endif return(true); @@ -270,7 +261,7 @@ namespace eCAL #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataReader::ClearAttribute"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataReader::ClearAttribute"); #endif return(true); @@ -281,35 +272,98 @@ namespace eCAL m_id_set = id_set_; } - void CDataReader::ApplyPublication(const SPublicationInfo& publication_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_) + void CDataReader::ApplyPublication(const SPublicationInfo& publication_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& pub_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; + m_layers.udp.write_enabled = pub_layer_states_.udp.write_enabled; #endif #if ECAL_CORE_TRANSPORT_SHM - m_layers.shm.write_enabled = layer_states_.shm.write_enabled; + m_layers.shm.write_enabled = pub_layer_states_.shm.write_enabled; #endif #if ECAL_CORE_TRANSPORT_TCP - m_layers.tcp.write_enabled = layer_states_.tcp.write_enabled; + m_layers.tcp.write_enabled = pub_layer_states_.tcp.write_enabled; #endif - FireConnectEvent(publication_info_.topic_id, data_type_info_); + // add key to connection map, including connection state + bool is_new_connection = false; + bool is_updated_connection = false; + { + const std::lock_guard lock(m_connection_map_mtx); + auto publication_info_iter = m_connection_map.find(publication_info_); + + if (publication_info_iter == m_connection_map.end()) + { + // add publisher to connection map, connection state false + m_connection_map[publication_info_] = SConnection{ data_type_info_, pub_layer_states_, false }; + } + else + { + // existing connection, we got the second update now + auto& connection = publication_info_iter->second; + + // if this connection was inactive before + // activate it now and flag a new connection finally + if (!connection.state) + { + is_new_connection = true; + } + // the connection was active, so we just update it + else + { + is_updated_connection = true; + } + + // update the data type and layer states, even if the connection is not new + connection = SConnection{ data_type_info_, pub_layer_states_, true }; + } + + // update connection count + m_connection_count = GetConnectionCount(); + } - // add key to publisher map + // handle these events outside the lock + if (is_new_connection) { - const std::lock_guard lock(m_pub_map_mtx); - m_pub_map[publication_info_] = std::make_tuple(data_type_info_, layer_states_); + // fire connect event + FireConnectEvent(publication_info_.entity_id, data_type_info_); } + else if (is_updated_connection) + { + // fire update event + FireUpdateEvent(publication_info_.entity_id, data_type_info_); + } + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataReader::ApplyPublication"); +#endif } void CDataReader::RemovePublication(const SPublicationInfo& publication_info_) { - // remove key from publisher map + // remove key from connection map + bool last_connection_gone(false); + { + const std::lock_guard lock(m_connection_map_mtx); + + m_connection_map.erase(publication_info_); + last_connection_gone = m_connection_map.empty(); + + // update connection count + m_connection_count = GetConnectionCount(); + } + + if (last_connection_gone) { - const std::lock_guard lock(m_pub_map_mtx); - m_pub_map.erase(publication_info_); + // fire disconnect event + FireDisconnectEvent(); } + +#ifndef NDEBUG + // log it + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataReader::RemovePublication"); +#endif } void CDataReader::ApplyLayerParameter(const SPublicationInfo& publication_info_, eTLayerType type_, const Registration::ConnectionPar& parameter_) @@ -317,8 +371,8 @@ namespace eCAL 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.topic_name = m_attributes.topic_name; + par.topic_id = publication_info_.entity_id; par.parameter = parameter_; switch (type_) @@ -342,30 +396,30 @@ namespace eCAL { // initialize udp layer #if ECAL_CORE_TRANSPORT_UDP - if (m_config.layer.udp.enable) + if (m_attributes.udp.enable) { - CUDPReaderLayer::Get()->Initialize(); + CUDPReaderLayer::Get()->Initialize(eCAL::eCALReader::BuildUDPAttributes(m_attributes)); } #endif // initialize shm layer #if ECAL_CORE_TRANSPORT_SHM - if (m_config.layer.shm.enable) + if (m_attributes.shm.enable) { - CSHMReaderLayer::Get()->Initialize(); + CSHMReaderLayer::Get()->Initialize(eCAL::eCALReader::BuildSHMAttributes(m_attributes)); } #endif // initialize tcp layer #if ECAL_CORE_TRANSPORT_TCP - if (m_config.layer.tcp.enable) + if (m_attributes.tcp.enable) { - CTCPReaderLayer::Get()->Initialize(); + CTCPReaderLayer::Get()->Initialize(eCAL::eCALReader::BuildTCPLayerAttributes(m_attributes)); } #endif } - 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_) + size_t CDataReader::ApplySample(const Payload::TopicInfo& topic_info_, 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); @@ -375,13 +429,13 @@ namespace eCAL switch (layer_) { case tl_ecal_udp: - if (!m_config.layer.udp.enable) return 0; + if (!m_attributes.udp.enable) return 0; break; case tl_ecal_shm: - if (!m_config.layer.shm.enable) return 0; + if (!m_attributes.shm.enable) return 0; break; case tl_ecal_tcp: - if (!m_config.layer.tcp.enable) return 0; + if (!m_attributes.tcp.enable) return 0; break; default: break; @@ -402,7 +456,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataReader::AddSample discard sample because of multiple receive"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataReader::AddSample discard sample because of multiple receive"); #endif return(size_); } @@ -423,7 +477,7 @@ namespace eCAL // - a dropped message // - an out-of-order message // - a multiple sent message - if (!CheckMessageClock(tid_, clock_)) + if (!CheckMessageClock(topic_info_.tid, clock_)) { // we will not process that message return(0); @@ -431,7 +485,7 @@ namespace eCAL #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataReader::AddSample"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataReader::AddSample"); #endif // increase read clock @@ -458,7 +512,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataReader::AddSample::ReceiveCallback"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataReader::AddSample::ReceiveCallback"); #endif // prepare data struct SReceiveCallbackData cb_data; @@ -467,8 +521,21 @@ namespace eCAL cb_data.id = id_; cb_data.time = time_; cb_data.clock = clock_; + + Registration::STopicId topic_id; + topic_id.topic_name = topic_info_.tname; + topic_id.topic_id.host_name = topic_info_.hname; + topic_id.topic_id.entity_id = topic_info_.tid; + topic_id.topic_id.process_id = topic_info_.pid; + + SPublicationInfo pub_info; + pub_info.entity_id = topic_info_.tid; + pub_info.host_name = topic_info_.hname; + pub_info.process_id = topic_info_.pid; + // execute it - (m_receive_callback)(m_topic_name.c_str(), &cb_data); + std::lock_guard lock(m_connection_map_mtx); + (m_receive_callback)(topic_id, m_connection_map[pub_info].data_type_info, cb_data); processed = true; } } @@ -487,7 +554,7 @@ namespace eCAL m_read_buf_cv.notify_one(); #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataReader::AddSample::Receive::Buffered"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataReader::AddSample::Receive::Buffered"); #endif } @@ -502,9 +569,9 @@ namespace eCAL 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_host_name: " << m_attributes.host_name << '\n'; + out << indent_ << "m_host_group_name: " << m_attributes.host_group_name << '\n'; + out << indent_ << "m_topic_name: " << m_attributes.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'; @@ -523,11 +590,13 @@ namespace eCAL void CDataReader::Register() { #if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); + Registration::Sample sample; + GetRegistrationSample(sample); + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(sample); #ifndef NDEBUG // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataReader::Register"); + Logging::Log(log_level_debug4, m_attributes.topic_name + "::CDataReader::Register"); #endif #endif // ECAL_CORE_REGISTRATION } @@ -535,58 +604,54 @@ namespace eCAL void CDataReader::Unregister() { #if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); + Registration::Sample sample; + GetUnregistrationSample(sample); + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(sample); #ifndef NDEBUG // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataReader::Unregister"); + Logging::Log(log_level_debug4, m_attributes.topic_name + "::CDataReader::Unregister"); #endif #endif // ECAL_CORE_REGISTRATION } - void CDataReader::CheckConnections() + void CDataReader::GetRegistration(Registration::Sample& sample) { - const std::lock_guard lock(m_pub_map_mtx); - m_pub_map.erase_expired(); - - if (m_pub_map.empty()) - { - FireDisconnectEvent(); - } + // return registration + return GetRegistrationSample(sample); } - Registration::Sample CDataReader::GetRegistration() + bool CDataReader::IsPublished() const { - // check connection timeouts - CheckConnections(); + return m_connection_count > 0; + } - // return registration - return GetRegistrationSample(); + size_t CDataReader::GetPublisherCount() const + { + return m_connection_count; } - Registration::Sample CDataReader::GetRegistrationSample() + void CDataReader::GetRegistrationSample(Registration::Sample& ecal_reg_sample) { - // create registration sample - Registration::Sample ecal_reg_sample; ecal_reg_sample.cmd_type = bct_reg_subscriber; auto& ecal_reg_sample_identifier = ecal_reg_sample.identifier; - ecal_reg_sample_identifier.process_id = m_pid; + ecal_reg_sample_identifier.process_id = m_attributes.process_id; ecal_reg_sample_identifier.entity_id = m_topic_id; - ecal_reg_sample_identifier.host_name = m_host_name; + ecal_reg_sample_identifier.host_name = m_attributes.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; + ecal_reg_sample_topic.hgname = m_attributes.host_group_name; + ecal_reg_sample_topic.tname = m_attributes.topic_name; // topic_information { auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; - if (m_share_ttype) + if (m_attributes.share_topic_type) { ecal_reg_sample_tdatatype.encoding = m_topic_info.encoding; ecal_reg_sample_tdatatype.name = m_topic_info.name; } - if (m_share_tdesc) + if (m_attributes.share_topic_description) { ecal_reg_sample_tdatatype.descriptor = m_topic_info.descriptor; } @@ -630,8 +695,8 @@ namespace eCAL } #endif - ecal_reg_sample_topic.pname = m_pname; - ecal_reg_sample_topic.uname = Process::GetUnitName(); + ecal_reg_sample_topic.pname = m_attributes.process_name; + ecal_reg_sample_topic.uname = m_attributes.unit_name; ecal_reg_sample_topic.dclock = m_clock; ecal_reg_sample_topic.dfreq = GetFrequency(); ecal_reg_sample_topic.message_drops = static_cast(m_message_drops); @@ -639,62 +704,56 @@ namespace eCAL // 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; } - Registration::Sample CDataReader::GetUnregistrationSample() + void CDataReader::GetUnregistrationSample(Registration::Sample& ecal_unreg_sample) { - // create unregistration sample - Registration::Sample ecal_unreg_sample; ecal_unreg_sample.cmd_type = bct_unreg_subscriber; auto& ecal_reg_sample_identifier = ecal_unreg_sample.identifier; - ecal_reg_sample_identifier.process_id = m_pid; + ecal_reg_sample_identifier.process_id = m_attributes.process_id; ecal_reg_sample_identifier.entity_id = m_topic_id; - ecal_reg_sample_identifier.host_name = m_host_name; + ecal_reg_sample_identifier.host_name = m_attributes.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; + ecal_reg_sample_topic.hgname = m_attributes.host_group_name; + ecal_reg_sample_topic.pname = m_attributes.process_name; + ecal_reg_sample_topic.tname = m_attributes.topic_name; + ecal_reg_sample_topic.uname = m_attributes.unit_name; } void CDataReader::StartTransportLayer() { #if ECAL_CORE_TRANSPORT_UDP - if (m_config.layer.udp.enable) + if (m_attributes.udp.enable) { // 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); + CUDPReaderLayer::Get()->AddSubscription(m_attributes.host_name, m_attributes.topic_name, m_topic_id); } #endif #if ECAL_CORE_TRANSPORT_SHM - if (m_config.layer.shm.enable) + if (m_attributes.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); + CSHMReaderLayer::Get()->AddSubscription(m_attributes.host_name, m_attributes.topic_name, m_topic_id); } #endif #if ECAL_CORE_TRANSPORT_TCP - if (m_config.layer.tcp.enable) + if (m_attributes.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); + CTCPReaderLayer::Get()->AddSubscription(m_attributes.host_name, m_attributes.topic_name, m_topic_id); } #endif } @@ -702,97 +761,97 @@ namespace eCAL void CDataReader::StopTransportLayer() { #if ECAL_CORE_TRANSPORT_UDP - if (m_config.layer.udp.enable) + if (m_attributes.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); + CUDPReaderLayer::Get()->RemSubscription(m_attributes.host_name, m_attributes.topic_name, m_topic_id); } #endif #if ECAL_CORE_TRANSPORT_SHM - if (m_config.layer.shm.enable) + if (m_attributes.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); + CSHMReaderLayer::Get()->RemSubscription(m_attributes.host_name, m_attributes.topic_name, m_topic_id); } #endif #if ECAL_CORE_TRANSPORT_TCP - if (m_config.layer.tcp.enable) + if (m_attributes.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); + CTCPReaderLayer::Get()->RemSubscription(m_attributes.host_name, m_attributes.topic_name, m_topic_id); } #endif } 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(); - data.clock = 0; - - if (!m_connected) - { - m_connected = true; - - // fire sub_event_connected - { - const std::lock_guard lock(m_event_callback_map_mtx); - auto iter = m_event_callback_map.find(sub_event_connected); - if (iter != m_event_callback_map.end() && iter->second) - { - data.type = sub_event_connected; - data.tid = tid_; - data.tdatatype = tinfo_; - (iter->second)(m_topic_name.c_str(), &data); - } - } + const std::lock_guard lock(m_event_callback_map_mtx); + auto iter = m_event_callback_map.find(sub_event_connected); + if (iter != m_event_callback_map.end() && iter->second) + { + SSubEventCallbackData data; + data.type = sub_event_connected; + data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + data.clock = 0; + data.tid = tid_; + data.tdatatype = tinfo_; + (iter->second)(m_attributes.topic_name.c_str(), &data); } + } - // fire sub_event_update_connection - { - const std::lock_guard lock(m_event_callback_map_mtx); - auto iter = m_event_callback_map.find(sub_event_update_connection); - if (iter != m_event_callback_map.end() && iter->second) - { - data.type = sub_event_update_connection; - data.tid = tid_; - data.tdatatype = tinfo_; - (iter->second)(m_topic_name.c_str(), &data); - } + void CDataReader::FireUpdateEvent(const std::string& tid_, const SDataTypeInformation& tinfo_) + { + const std::lock_guard lock(m_event_callback_map_mtx); + auto iter = m_event_callback_map.find(sub_event_update_connection); + if (iter != m_event_callback_map.end() && iter->second) + { + SSubEventCallbackData data; + data.type = sub_event_update_connection; + data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + data.clock = 0; + data.tid = tid_; + data.tdatatype = tinfo_; + (iter->second)(m_attributes.topic_name.c_str(), &data); } } void CDataReader::FireDisconnectEvent() { - if (m_connected) + const std::lock_guard lock(m_event_callback_map_mtx); + auto iter = m_event_callback_map.find(sub_event_disconnected); + if (iter != m_event_callback_map.end() && iter->second) { - m_connected = false; + SSubEventCallbackData data; + data.type = sub_event_disconnected; + data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + data.clock = 0; + (iter->second)(m_attributes.topic_name.c_str(), &data); + } + } - // fire sub_event_disconnected + size_t CDataReader::GetConnectionCount() + { + // no need to lock map here for now, map locked by caller + size_t count(0); + for (const auto& sub : m_connection_map) + { + if (sub.second.state) { - const std::lock_guard lock(m_event_callback_map_mtx); - auto iter = m_event_callback_map.find(sub_event_disconnected); - if (iter != m_event_callback_map.end() && iter->second) - { - SSubEventCallbackData data; - data.type = sub_event_disconnected; - data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - data.clock = 0; - (iter->second)(m_topic_name.c_str(), &data); - } + count++; } } + return count; } bool CDataReader::CheckMessageClock(const std::string& tid_, long long current_clock_) @@ -841,11 +900,11 @@ namespace eCAL // we log this std::string msg = std::to_string(counter_ - counter_last) + " Messages lost ! "; msg += "(Unit: \'"; - msg += Process::GetUnitName(); + msg += m_attributes.unit_name; msg += "@"; - msg += Process::GetHostName(); + msg += m_attributes.host_name; msg += "\' | Subscriber: \'"; - msg += m_topic_name; + msg += m_attributes.topic_name; msg += "\')"; Logging::Log(log_level_warning, msg); #endif @@ -859,7 +918,7 @@ namespace eCAL data.type = sub_event_dropped; data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); data.clock = current_clock_; - (citer->second)(m_topic_name.c_str(), &data); + (citer->second)(m_attributes.topic_name.c_str(), &data); } } @@ -899,7 +958,7 @@ namespace eCAL // but we log this std::string msg = "Subscriber: \'"; - msg += m_topic_name; + msg += m_attributes.topic_name; msg += "\'"; msg += " received a message in the wrong order"; Logging::Log(log_level_warning, msg); diff --git a/ecal/core/src/readwrite/ecal_reader.h b/ecal/core/src/readwrite/ecal_reader.h index c6955e2d..5c01ce4e 100644 --- a/ecal/core/src/readwrite/ecal_reader.h +++ b/ecal/core/src/readwrite/ecal_reader.h @@ -25,12 +25,11 @@ #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 "config/attributes/reader_attributes.h" #include #include @@ -42,7 +41,6 @@ #include #include #include -#include #include namespace eCAL @@ -64,27 +62,15 @@ namespace eCAL SLayerState tcp; }; - struct SPublicationInfo - { - 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); - } - }; - - CDataReader(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Subscriber::Configuration& config_ = {}); + using SPublicationInfo = Registration::SampleIdentifier; + CDataReader(const SDataTypeInformation& topic_info_, const eCAL::eCALReader::SAttributes& attr_); ~CDataReader(); bool Stop(); bool Read(std::string& buf_, long long* time_ = nullptr, int rcv_timeout_ms_ = 0); - bool AddReceiveCallback(ReceiveCallbackT callback_); + bool AddReceiveCallback(ReceiveIDCallbackT callback_); bool RemReceiveCallback(); bool AddEventCallback(eCAL_Subscriber_Event type_, SubEventCallbackT callback_); @@ -95,32 +81,33 @@ namespace eCAL void SetID(const std::set& id_set_); - void ApplyPublication(const SPublicationInfo& publication_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& layer_states_); + void ApplyPublication(const SPublicationInfo& publication_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& pub_layer_states_); void RemovePublication(const SPublicationInfo& publication_info_); void ApplyLayerParameter(const SPublicationInfo& publication_info_, eTLayerType type_, const Registration::ConnectionPar& parameter_); - Registration::Sample GetRegistration(); + void GetRegistration(Registration::Sample& sample); bool IsCreated() const { return(m_created); } - bool IsPublished() const - { - std::lock_guard const lock(m_pub_map_mtx); - return(!m_pub_map.empty()); - } + bool IsPublished() const; + size_t GetPublisherCount() const; - size_t GetPublisherCount() const + Registration::STopicId GetId() const { - const std::lock_guard lock(m_pub_map_mtx); - return(m_pub_map.size()); + Registration::STopicId id; + id.topic_name = m_attributes.topic_name; + id.topic_id.entity_id = m_topic_id; + id.topic_id.host_name = m_attributes.host_name; + id.topic_id.process_id = m_attributes.process_id; + return id; } - std::string GetTopicName() const { return(m_topic_name); } + std::string GetTopicName() const { return(m_attributes.topic_name); } std::string GetTopicID() const { return(m_topic_id); } SDataTypeInformation GetDataTypeInformation() const { return(m_topic_info); } 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 ApplySample(const Payload::TopicInfo& topic_info_, 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_ = ""); @@ -128,36 +115,37 @@ namespace eCAL void Register(); void Unregister(); - void CheckConnections(); - - Registration::Sample GetRegistrationSample(); - Registration::Sample GetUnregistrationSample(); + void GetRegistrationSample(Registration::Sample& sample); + void GetUnregistrationSample(Registration::Sample& sample); void StartTransportLayer(); void StopTransportLayer(); void FireConnectEvent(const std::string& tid_, const SDataTypeInformation& tinfo_); + void FireUpdateEvent(const std::string& tid_, const SDataTypeInformation& tinfo_); void FireDisconnectEvent(); + + size_t GetConnectionCount(); bool CheckMessageClock(const std::string& tid_, long long current_clock_); int32_t GetFrequency(); - std::string m_host_name; - std::string m_host_group_name; - int m_pid = 0; - std::string m_pname; - std::string m_topic_name; std::string m_topic_id; SDataTypeInformation m_topic_info; std::map m_attr; std::atomic m_topic_size; - Subscriber::Configuration m_config; - std::atomic m_connected; - using PublicationMapT = Util::CExpirationMap>; - mutable std::mutex m_pub_map_mtx; - PublicationMapT m_pub_map; + struct SConnection + { + SDataTypeInformation data_type_info; + SLayerStates layer_states; + bool state = false; + }; + using ConnectionMapT = std::map; + mutable std::mutex m_connection_map_mtx; + ConnectionMapT m_connection_map; + std::atomic m_connection_count{ 0 }; mutable std::mutex m_read_buf_mtx; std::condition_variable m_read_buf_cv; @@ -166,7 +154,7 @@ namespace eCAL long long m_read_time = 0; std::mutex m_receive_callback_mtx; - ReceiveCallbackT m_receive_callback; + ReceiveIDCallbackT m_receive_callback; std::atomic m_receive_time; std::deque m_sample_hash_queue; @@ -186,10 +174,9 @@ namespace eCAL WriterCounterMapT m_writer_counter_map; long long m_message_drops = 0; - bool m_share_ttype = false; - bool m_share_tdesc = false; - SLayerStates m_layers; std::atomic m_created; + + eCAL::eCALReader::SAttributes m_attributes; }; } diff --git a/ecal/core/src/readwrite/ecal_reader_layer.h b/ecal/core/src/readwrite/ecal_reader_layer.h index 276a45a6..3ce7dd0b 100644 --- a/ecal/core/src/readwrite/ecal_reader_layer.h +++ b/ecal/core/src/readwrite/ecal_reader_layer.h @@ -42,7 +42,7 @@ namespace eCAL }; // ecal data layer base class - template + template class CReaderLayer { public: @@ -52,7 +52,7 @@ namespace eCAL // initialize layer // will be called one time on eCAL initialization - virtual void Initialize() = 0; + virtual void Initialize(const U& attr_) = 0; // activate / create a specific subscription virtual void AddSubscription(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) = 0; @@ -72,6 +72,5 @@ namespace eCAL } return layer; } - }; } diff --git a/ecal/core/src/readwrite/ecal_writer.cpp b/ecal/core/src/readwrite/ecal_writer.cpp index 2c08fef9..e4d9c3b5 100644 --- a/ecal/core/src/readwrite/ecal_writer.cpp +++ b/ecal/core/src/readwrite/ecal_writer.cpp @@ -36,6 +36,10 @@ #include "ecal_global_accessors.h" #include "ecal_transport_layer.h" +#include "config/builder/shm_attribute_builder.h" +#include "config/builder/tcp_attribute_builder.h" +#include "config/builder/udp_attribute_builder.h" + #include #include #include @@ -93,21 +97,15 @@ namespace namespace eCAL { - CDataWriter::CDataWriter(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Publisher::Configuration& config_) : - m_host_name(Process::GetHostName()), - m_host_group_name(Process::GetHostGroupName()), - m_pid(Process::GetProcessID()), - m_pname(Process::GetProcessName()), - m_topic_name(topic_name_), + CDataWriter::CDataWriter(const SDataTypeInformation& topic_info_, const eCAL::eCALWriter::SAttributes& attr_) : m_topic_info(topic_info_), - m_config(config_), - m_connected(false), + m_attributes(attr_), m_frequency_calculator(3.0f), m_created(false) { #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Constructor"); + Logging::Log(log_level_debug1, m_attributes.topic_name + "::CDataWriter::Constructor"); #endif // build topic id @@ -115,10 +113,6 @@ 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; } @@ -127,7 +121,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Destructor"); + Logging::Log(log_level_debug1, m_attributes.topic_name + "::CDataWriter::Destructor"); #endif Stop(); @@ -138,7 +132,7 @@ namespace eCAL if (!m_created) return false; #ifndef NDEBUG // log it - Logging::Log(log_level_debug1, m_topic_name + "::CDataWriter::Stop"); + Logging::Log(log_level_debug1, m_attributes.topic_name + "::CDataWriter::Stop"); #endif // stop all transport layer @@ -146,8 +140,8 @@ namespace eCAL // clear subscriber maps { - const std::lock_guard lock(m_sub_map_mtx); - m_sub_map.clear(); + const std::lock_guard lock(m_connection_map_mtx); + m_connection_map.clear(); } // clear event callback map @@ -173,7 +167,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.layer.shm.zero_copy_mode; // zero copy mode activated by user + allow_zero_copy = m_attributes.shm.zero_copy_mode; // zero copy mode activated by user #endif #if ECAL_CORE_TRANSPORT_UDP // udp is active -> no zero copy @@ -205,7 +199,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::SHM"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataWriter::Send::SHM"); #endif // send it @@ -218,8 +212,8 @@ namespace eCAL wattr.clock = m_clock; wattr.hash = snd_hash; wattr.time = time_; - wattr.zero_copy = m_config.layer.shm.zero_copy_mode; - wattr.acknowledge_timeout_ms = m_config.layer.shm.acknowledge_timeout_ms; + wattr.zero_copy = m_attributes.shm.zero_copy_mode; + wattr.acknowledge_timeout_ms = m_attributes.shm.acknowledge_timeout_ms; // prepare send if (m_writer_shm->PrepareWrite(wattr)) @@ -252,11 +246,11 @@ namespace eCAL // log it if (shm_sent) { - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::SHM - SUCCESS"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataWriter::Send::SHM - SUCCESS"); } else { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::SHM - FAILED"); + Logging::Log(log_level_error, m_attributes.topic_name + "::CDataWriter::Send::SHM - FAILED"); } #endif } @@ -270,7 +264,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::udp"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataWriter::Send::udp"); #endif // send it @@ -283,7 +277,7 @@ namespace eCAL wattr.clock = m_clock; wattr.hash = snd_hash; wattr.time = time_; - wattr.loopback = eCAL::GetConfiguration().registration.loopback; + wattr.loopback = m_attributes.loopback; // prepare send if (m_writer_udp->PrepareWrite(wattr)) @@ -303,11 +297,11 @@ namespace eCAL // log it if (udp_sent) { - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::udp - SUCCESS"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataWriter::Send::udp - SUCCESS"); } else { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::udp - FAILED"); + Logging::Log(log_level_error, m_attributes.topic_name + "::CDataWriter::Send::udp - FAILED"); } #endif } @@ -321,7 +315,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::TCP"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataWriter::Send::TCP"); #endif // send it @@ -345,11 +339,11 @@ namespace eCAL // log it if (tcp_sent) { - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::Send::TCP - SUCCESS"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataWriter::Send::TCP - SUCCESS"); } else { - Logging::Log(log_level_error, m_topic_name + "::CDataWriter::Send::TCP - FAILED"); + Logging::Log(log_level_error, m_attributes.topic_name + "::CDataWriter::Send::TCP - FAILED"); } #endif } @@ -366,7 +360,7 @@ namespace eCAL #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetDescription"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::SetDescription"); #endif return(true); @@ -378,7 +372,7 @@ namespace eCAL #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::SetAttribute"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::SetAttribute"); #endif return(true); @@ -390,7 +384,7 @@ namespace eCAL #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ClearAttribute"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::ClearAttribute"); #endif return(true); @@ -404,7 +398,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::AddEventCallback"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::AddEventCallback"); #endif const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map[type_] = std::move(callback_); @@ -421,7 +415,7 @@ namespace eCAL { #ifndef NDEBUG // log it - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::RemEventCallback"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::RemEventCallback"); #endif const std::lock_guard lock(m_event_callback_map_mtx); m_event_callback_map[type_] = nullptr; @@ -432,25 +426,23 @@ namespace eCAL void CDataWriter::ApplySubscription(const SSubscriptionInfo& subscription_info_, const SDataTypeInformation& data_type_info_, const SLayerStates& sub_layer_states_, const std::string& reader_par_) { - FireConnectEvent(subscription_info_.topic_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 (m_attributes.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 (m_attributes.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 (m_attributes.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 @@ -459,7 +451,7 @@ namespace eCAL // 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); + const TLayer::eTransportLayer layer2activate = DetermineTransportLayer2Start(pub_layers, sub_layers, m_attributes.host_name == subscription_info_.host_name); switch (layer2activate) { case tl_ecal_udp: @@ -480,51 +472,107 @@ namespace eCAL //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_, 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 + // add key to connection map, including connection state + bool is_new_connection = false; + bool is_updated_connection = false; + { + const std::lock_guard lock(m_connection_map_mtx); + auto subscription_info_iter = m_connection_map.find(subscription_info_); + + if (subscription_info_iter == m_connection_map.end()) + { + // add subscriber to connection map, connection state false + m_connection_map[subscription_info_] = SConnection{ data_type_info_, sub_layer_states_, false }; + } + else + { + // existing connection, we got the second update now + auto& connection = subscription_info_iter->second; + + // if this connection was inactive before + // activate it now and flag a new connection finally + if (!connection.state) + { + is_new_connection = true; + } + // the connection was active, so we just update it + else + { + is_updated_connection = true; + } + + // update the data type, the layer states and set the state active + connection = SConnection{ data_type_info_, sub_layer_states_, true }; + } + + // update connection count + m_connection_count = GetConnectionCount(); + } + + + // handle these events outside the lock + if (is_new_connection) + { + // fire connect event + FireConnectEvent(subscription_info_.entity_id, data_type_info_); + } + else if (is_updated_connection) + { + // fire update event + FireUpdateEvent(subscription_info_.entity_id, data_type_info_); + } + #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::ApplySubscription"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataWriter::ApplySubscription"); #endif } void CDataWriter::RemoveSubscription(const SSubscriptionInfo& subscription_info_) { - // remove key from subscriber map - { - const std::lock_guard lock(m_sub_map_mtx); - m_sub_map.erase(subscription_info_); - } - // 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 + // remove key from connection map + bool last_connection_gone(false); + { + const std::lock_guard lock(m_connection_map_mtx); + + m_connection_map.erase(subscription_info_); + last_connection_gone = m_connection_map.empty(); + + // update connection count + m_connection_count = GetConnectionCount(); + } + + if (last_connection_gone) + { + // fire disconnect event + FireDisconnectEvent(); + } + #ifndef NDEBUG // log it - Logging::Log(log_level_debug3, m_topic_name + "::CDataWriter::RemoveSubscription"); + Logging::Log(log_level_debug3, m_attributes.topic_name + "::CDataWriter::RemoveSubscription"); #endif } @@ -540,7 +588,16 @@ namespace eCAL const std::lock_guard lock(m_frequency_calculator_mtx); m_frequency_calculator.addTick(send_time); } + } + + bool CDataWriter::IsSubscribed() const + { + return m_connection_count > 0; + } + size_t CDataWriter::GetSubscriberCount() const + { + return m_connection_count; } std::string CDataWriter::Dump(const std::string& indent_ /* = "" */) @@ -551,9 +608,9 @@ namespace eCAL out << indent_ << "--------------------------" << '\n'; out << indent_ << " class CDataWriter " << '\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_host_name: " << m_attributes.host_name << '\n'; + out << indent_ << "m_host_group_name: " << m_attributes.host_group_name << '\n'; + out << indent_ << "m_topic_name: " << m_attributes.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'; @@ -570,11 +627,13 @@ namespace eCAL void CDataWriter::Register() { #if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(GetRegistrationSample()); + Registration::Sample registration_sample; + GetRegistrationSample(registration_sample); + if (g_registration_provider() != nullptr) g_registration_provider()->RegisterSample(registration_sample); #ifndef NDEBUG // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Register"); + Logging::Log(log_level_debug4, m_attributes.topic_name + "::CDataWriter::Register"); #endif #endif // ECAL_CORE_REGISTRATION } @@ -582,58 +641,44 @@ namespace eCAL void CDataWriter::Unregister() { #if ECAL_CORE_REGISTRATION - if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(GetUnregistrationSample()); + Registration::Sample unregistration_sample; + GetUnregistrationSample(unregistration_sample); + if (g_registration_provider() != nullptr) g_registration_provider()->UnregisterSample(unregistration_sample); #ifndef NDEBUG // log it - Logging::Log(log_level_debug4, m_topic_name + "::CDataWriter::Unregister"); + Logging::Log(log_level_debug4, m_attributes.topic_name + "::CDataWriter::Unregister"); #endif #endif // ECAL_CORE_REGISTRATION } - void CDataWriter::CheckConnections() - { - const std::lock_guard lock(m_sub_map_mtx); - m_sub_map.erase_expired(); - - if (m_sub_map.empty()) - { - FireDisconnectEvent(); - } - } - - Registration::Sample CDataWriter::GetRegistration() + void CDataWriter::GetRegistration(Registration::Sample& sample) { - // check connection timeouts - CheckConnections(); - - return GetRegistrationSample(); + GetRegistrationSample(sample); } - Registration::Sample CDataWriter::GetRegistrationSample() + void CDataWriter::GetRegistrationSample(Registration::Sample& ecal_reg_sample) { - // 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.process_id = m_attributes.process_id; ecal_reg_sample_identifier.entity_id = m_topic_id; - ecal_reg_sample_identifier.host_name = m_host_name; + ecal_reg_sample_identifier.host_name = m_attributes.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; + ecal_reg_sample_topic.hgname = m_attributes.host_group_name; + ecal_reg_sample_topic.tname = m_attributes.topic_name; // topic_information { auto& ecal_reg_sample_tdatatype = ecal_reg_sample_topic.tdatatype; - if (m_config.share_topic_type) + if (m_attributes.share_topic_type) { ecal_reg_sample_tdatatype.encoding = m_topic_info.encoding; ecal_reg_sample_tdatatype.name = m_topic_info.name; } - if (m_config.share_topic_description) + if (m_attributes.share_topic_description) { ecal_reg_sample_tdatatype.descriptor = m_topic_info.descriptor; } @@ -683,8 +728,8 @@ namespace eCAL } #endif - ecal_reg_sample_topic.pname = m_pname; - ecal_reg_sample_topic.uname = Process::GetUnitName(); + ecal_reg_sample_topic.pname = m_attributes.process_name; + ecal_reg_sample_topic.uname = m_attributes.unit_name; ecal_reg_sample_topic.did = m_id; ecal_reg_sample_topic.dclock = m_clock; ecal_reg_sample_topic.dfreq = GetFrequency(); @@ -692,100 +737,94 @@ namespace eCAL size_t loc_connections(0); size_t ext_connections(0); { - const std::lock_guard lock(m_sub_map_mtx); - for (const auto& sub : m_sub_map) + const std::lock_guard lock(m_connection_map_mtx); + for (const auto& sub : m_connection_map) { - if (sub.first.host_name == m_host_name) + if (sub.first.host_name == m_attributes.host_name) { loc_connections++; } } - ext_connections = m_sub_map.size() - loc_connections; + ext_connections = m_connection_map.size() - loc_connections; } ecal_reg_sample_topic.connections_loc = static_cast(loc_connections); ecal_reg_sample_topic.connections_ext = static_cast(ext_connections); - - return ecal_reg_sample; } - Registration::Sample CDataWriter::GetUnregistrationSample() + void CDataWriter::GetUnregistrationSample(Registration::Sample& ecal_unreg_sample) { - // 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.process_id = m_attributes.process_id; ecal_reg_sample_identifier.entity_id = m_topic_id; - ecal_reg_sample_identifier.host_name = m_host_name; + ecal_reg_sample_identifier.host_name = m_attributes.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; + ecal_reg_sample_topic.hgname = m_attributes.host_group_name; + ecal_reg_sample_topic.pname = m_attributes.process_name; + ecal_reg_sample_topic.tname = m_attributes.topic_name; + ecal_reg_sample_topic.uname = m_attributes.unit_name; } 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(); - data.clock = 0; - - if (!m_connected) + const std::lock_guard lock(m_event_callback_map_mtx); + auto iter = m_event_callback_map.find(pub_event_connected); + if (iter != m_event_callback_map.end() && iter->second) { - m_connected = true; - - // fire pub_event_connected - { - const std::lock_guard lock(m_event_callback_map_mtx); - auto iter = m_event_callback_map.find(pub_event_connected); - if (iter != m_event_callback_map.end() && iter->second) - { - data.type = pub_event_connected; - data.tid = tid_; - data.tdatatype = tinfo_; - (iter->second)(m_topic_name.c_str(), &data); - } - } + SPubEventCallbackData data; + data.type = pub_event_connected; + data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + data.clock = 0; + data.tid = tid_; + data.tdatatype = tinfo_; + (iter->second)(m_attributes.topic_name.c_str(), &data); } + } - // fire pub_event_update_connection + void CDataWriter::FireUpdateEvent(const std::string& tid_, const SDataTypeInformation& tinfo_) + { + const std::lock_guard lock(m_event_callback_map_mtx); + auto iter = m_event_callback_map.find(pub_event_update_connection); + if (iter != m_event_callback_map.end() && iter->second) { - const std::lock_guard lock(m_event_callback_map_mtx); - auto iter = m_event_callback_map.find(pub_event_update_connection); - if (iter != m_event_callback_map.end() && iter->second) - { - data.type = pub_event_update_connection; - data.tid = tid_; - data.tdatatype = tinfo_; - (iter->second)(m_topic_name.c_str(), &data); - } + SPubEventCallbackData data; + data.type = pub_event_update_connection; + data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + data.clock = 0; + data.tid = tid_; + data.tdatatype = tinfo_; + (iter->second)(m_attributes.topic_name.c_str(), &data); } } void CDataWriter::FireDisconnectEvent() { - if (m_connected) + const std::lock_guard lock(m_event_callback_map_mtx); + auto iter = m_event_callback_map.find(pub_event_disconnected); + if (iter != m_event_callback_map.end() && iter->second) { - m_connected = false; + SPubEventCallbackData data; + data.type = pub_event_disconnected; + data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + data.clock = 0; + (iter->second)(m_attributes.topic_name.c_str(), &data); + } + } - // fire pub_event_disconnected + size_t CDataWriter::GetConnectionCount() + { + // no need to lock map here for now, map locked by caller + size_t count(0); + for (const auto& sub : m_connection_map) + { + if (sub.second.state) { - const std::lock_guard lock(m_event_callback_map_mtx); - auto iter = m_event_callback_map.find(pub_event_disconnected); - if (iter != m_event_callback_map.end() && iter->second) - { - SPubEventCallbackData data; - data.type = pub_event_disconnected; - data.time = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - data.clock = 0; - (iter->second)(m_topic_name.c_str(), &data); - } + count++; } } + return count; } bool CDataWriter::StartUdpLayer() @@ -797,16 +836,16 @@ namespace eCAL m_layers.udp.write_enabled = true; // log state - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateUdpLayer::ACTIVATED"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::ActivateUdpLayer::ACTIVATED"); // create writer - m_writer_udp = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.layer.udp); + m_writer_udp = std::make_unique(eCAL::eCALWriter::BuildUDPAttributes(m_topic_id, m_attributes)); // register activated layer Register(); #ifndef NDEBUG - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateUdpLayer::WRITER_CREATED"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::ActivateUdpLayer::WRITER_CREATED"); #endif return true; #else // ECAL_CORE_TRANSPORT_UDP @@ -823,16 +862,16 @@ namespace eCAL m_layers.shm.write_enabled = true; // log state - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateShmLayer::ACTIVATED"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::ActivateShmLayer::ACTIVATED"); // create writer - m_writer_shm = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.layer.shm); + m_writer_shm = std::make_unique(eCAL::eCALWriter::BuildSHMAttributes(m_attributes)); // register activated layer Register(); #ifndef NDEBUG - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateShmLayer::WRITER_CREATED"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::ActivateShmLayer::WRITER_CREATED"); #endif return true; #else // ECAL_CORE_TRANSPORT_SHM @@ -849,16 +888,16 @@ namespace eCAL m_layers.tcp.write_enabled = true; // log state - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateTcpLayer::ACTIVATED"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::ActivateTcpLayer::ACTIVATED"); // create writer - m_writer_tcp = std::make_unique(m_host_name, m_topic_name, m_topic_id, m_config.layer.tcp); + m_writer_tcp = std::make_unique(eCAL::eCALWriter::BuildTCPAttributes(m_topic_id, m_attributes)); // register activated layer Register(); #ifndef NDEBUG - Logging::Log(log_level_debug2, m_topic_name + "::CDataWriter::ActivateTcpLayer::WRITER_CREATED"); + Logging::Log(log_level_debug2, m_attributes.topic_name + "::CDataWriter::ActivateTcpLayer::WRITER_CREATED"); #endif return true; #else // ECAL_CORE_TRANSPORT_TCP @@ -912,26 +951,10 @@ namespace eCAL return snd_hash; } - bool CDataWriter::IsInternalSubscribedOnly() - { - const int32_t process_id = static_cast(Process::GetProcessID()); - bool is_internal_only(true); - const std::lock_guard lock(m_sub_map_mtx); - for (auto sub : m_sub_map) - { - if (sub.first.process_id != process_id) - { - is_internal_only = false; - break; - } - } - 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; + const Publisher::Configuration::LayerPriorityVector& layer_priority_vector = same_host_ ? m_attributes.layer_priority_local : m_attributes.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 diff --git a/ecal/core/src/readwrite/ecal_writer.h b/ecal/core/src/readwrite/ecal_writer.h index 1ea35da0..fae87781 100644 --- a/ecal/core/src/readwrite/ecal_writer.h +++ b/ecal/core/src/readwrite/ecal_writer.h @@ -27,11 +27,10 @@ #include #include #include -#include #include "serialization/ecal_serialize_sample_registration.h" -#include "util/ecal_expmap.h" #include "util/frequency_calculator.h" +#include "config/attributes/writer_attributes.h" #if ECAL_CORE_TRANSPORT_UDP #include "udp/ecal_writer_udp.h" @@ -52,7 +51,6 @@ #include #include #include -#include #include namespace eCAL @@ -74,20 +72,9 @@ namespace eCAL SLayerState tcp; }; - struct SSubscriptionInfo - { - 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); - } - }; + using SSubscriptionInfo = Registration::SampleIdentifier; - CDataWriter(const std::string& topic_name_, const SDataTypeInformation& topic_info_, const Publisher::Configuration& config_ = {}); + CDataWriter(const SDataTypeInformation& topic_info_, const eCAL::eCALWriter::SAttributes& attr_); ~CDataWriter(); bool Stop(); @@ -105,24 +92,25 @@ namespace eCAL 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_); - Registration::Sample GetRegistration(); + void GetRegistration(Registration::Sample& sample); void RefreshSendCounter(); bool IsCreated() const { return(m_created); } - bool IsSubscribed() const - { - std::lock_guard const lock(m_sub_map_mtx); - return(!m_sub_map.empty()); - } + bool IsSubscribed() const; + size_t GetSubscriberCount() const; - size_t GetSubscriberCount() const + Registration::STopicId GetId() const { - std::lock_guard const lock(m_sub_map_mtx); - return(m_sub_map.size()); + Registration::STopicId id; + id.topic_name = m_attributes.topic_name; + id.topic_id.entity_id = m_topic_id; + id.topic_id.host_name = m_attributes.host_name; + id.topic_id.process_id = m_attributes.process_id; + return id; } - const std::string& GetTopicName() const { return(m_topic_name); } + const std::string& GetTopicName() const { return(m_attributes.topic_name); } const SDataTypeInformation& GetDataTypeInformation() const { return m_topic_info; } std::string Dump(const std::string& indent_ = ""); @@ -131,10 +119,8 @@ namespace eCAL void Register(); void Unregister(); - void CheckConnections(); - - Registration::Sample GetRegistrationSample(); - Registration::Sample GetUnregistrationSample(); + void GetRegistrationSample(Registration::Sample& sample); + void GetUnregistrationSample(Registration::Sample& sample); bool StartUdpLayer(); bool StartShmLayer(); @@ -143,33 +129,35 @@ namespace eCAL void StopAllLayer(); void FireConnectEvent(const std::string& tid_, const SDataTypeInformation& tinfo_); + void FireUpdateEvent(const std::string& tid_, const SDataTypeInformation& tinfo_); void FireDisconnectEvent(); + size_t GetConnectionCount(); + size_t PrepareWrite(long long id_, size_t len_); - 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; - std::string m_host_group_name; - int m_pid; - std::string m_pname; - std::string m_topic_name; std::string m_topic_id; SDataTypeInformation m_topic_info; std::map m_attr; size_t m_topic_size = 0; - Publisher::Configuration m_config; + eCAL::eCALWriter::SAttributes m_attributes; std::vector m_payload_buffer; - std::atomic m_connected; - - using SSubscriptionMapT = Util::CExpirationMap>; - mutable std::mutex m_sub_map_mtx; - SSubscriptionMapT m_sub_map; + struct SConnection + { + SDataTypeInformation data_type_info; + SLayerStates layer_states; + bool state = false; + }; + using SSubscriptionMapT = std::map; + mutable std::mutex m_connection_map_mtx; + SSubscriptionMapT m_connection_map; + std::atomic m_connection_count{ 0 }; using EventCallbackMapT = std::map; std::mutex m_event_callback_map_mtx; diff --git a/ecal/core/src/readwrite/ecal_writer_base.h b/ecal/core/src/readwrite/ecal_writer_base.h index 48ef9cde..747068ce 100644 --- a/ecal/core/src/readwrite/ecal_writer_base.h +++ b/ecal/core/src/readwrite/ecal_writer_base.h @@ -50,10 +50,5 @@ namespace eCAL virtual bool PrepareWrite(const SWriterAttr& /*attr_*/) { return false; }; virtual bool Write(CPayloadWriter& /*payload_*/, const SWriterAttr& /*attr_*/) { return false; }; virtual bool Write(const void* /*buf_*/, const SWriterAttr& /*attr_*/) { return false; }; - - protected: - std::string m_host_name; - std::string m_topic_name; - std::string m_topic_id; }; } diff --git a/ecal/core/src/readwrite/shm/config/attributes/reader_shm_attributes.h b/ecal/core/src/readwrite/shm/config/attributes/reader_shm_attributes.h new file mode 100644 index 00000000..59784568 --- /dev/null +++ b/ecal/core/src/readwrite/shm/config/attributes/reader_shm_attributes.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 ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace eCALReader + { + namespace SHM + { + struct SAttributes + { + int process_id; + unsigned int registration_timeout_ms; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/shm/config/attributes/writer_shm_attributes.h b/ecal/core/src/readwrite/shm/config/attributes/writer_shm_attributes.h new file mode 100644 index 00000000..3359a843 --- /dev/null +++ b/ecal/core/src/readwrite/shm/config/attributes/writer_shm_attributes.h @@ -0,0 +1,42 @@ +/* ========================= 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 eCALWriter + { + namespace SHM + { + struct SAttributes + { + unsigned int acknowledge_timeout_ms; + unsigned int memfile_buffer_count; + unsigned int memfile_min_size_bytes; + unsigned int memfile_reserve_percent; + + std::string host_name; + std::string topic_name; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/shm/ecal_reader_shm.cpp b/ecal/core/src/readwrite/shm/ecal_reader_shm.cpp index a20c0db7..1592fb8a 100644 --- a/ecal/core/src/readwrite/shm/ecal_reader_shm.cpp +++ b/ecal/core/src/readwrite/shm/ecal_reader_shm.cpp @@ -38,34 +38,41 @@ namespace eCAL //////////////// // LAYER //////////////// + void CSHMReaderLayer::Initialize(const eCAL::eCALReader::SHM::SAttributes& attr_) + { + m_attributes = attr_; + } + void CSHMReaderLayer::SetConnectionParameter(SReaderLayerPar& par_) { for (const auto& memfile_name : par_.parameter.layer_par_shm.memory_file_list) { // start memory file receive thread if topic is subscribed in this process if (g_memfile_pool() != nullptr) - { - const std::string process_id = std::to_string(Process::GetProcessID()); + { + const std::string process_id = std::to_string(m_attributes.process_id); const std::string memfile_event = memfile_name + "_" + process_id; - const MemFileDataCallbackT memfile_data_callback = std::bind(&CSHMReaderLayer::OnNewShmFileContent, this, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4, - std::placeholders::_5, - std::placeholders::_6, - std::placeholders::_7, - std::placeholders::_8); - g_memfile_pool()->ObserveFile(memfile_name, memfile_event, par_.topic_name, par_.topic_id, Config::GetRegistrationTimeoutMs(), memfile_data_callback); + + Payload::TopicInfo topic_info; + topic_info.tname = par_.topic_name; + topic_info.hname = par_.host_name; + topic_info.tid = par_.topic_id; + topic_info.pid = par_.process_id; + + auto data_callback = [this, topic_info](const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_)->size_t + { + return OnNewShmFileContent(topic_info, buf_, len_, id_, clock_, time_, hash_); + }; + g_memfile_pool()->ObserveFile(memfile_name, memfile_event, m_attributes.registration_timeout_ms, data_callback); } } } - size_t CSHMReaderLayer::OnNewShmFileContent(const std::string& topic_name_, const std::string& topic_id_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_) + size_t CSHMReaderLayer::OnNewShmFileContent(const Payload::TopicInfo& topic_info_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_) { if (g_subgate() != nullptr) { - if (g_subgate()->ApplySample(topic_name_, topic_id_, buf_, len_, id_, clock_, time_, hash_, tl_ecal_shm)) + if (g_subgate()->ApplySample(topic_info_, buf_, len_, id_, clock_, time_, hash_, tl_ecal_shm)) { return len_; } diff --git a/ecal/core/src/readwrite/shm/ecal_reader_shm.h b/ecal/core/src/readwrite/shm/ecal_reader_shm.h index e3c5cb39..be981a03 100644 --- a/ecal/core/src/readwrite/shm/ecal_reader_shm.h +++ b/ecal/core/src/readwrite/shm/ecal_reader_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. @@ -25,6 +25,8 @@ #include "ecal_def.h" #include "readwrite/ecal_reader_layer.h" +#include "serialization/ecal_struct_sample_payload.h" +#include "config/attributes/reader_shm_attributes.h" #include #include @@ -35,19 +37,21 @@ namespace eCAL //////////////// // LAYER //////////////// - class CSHMReaderLayer : public CReaderLayer + class CSHMReaderLayer : public CReaderLayer { public: CSHMReaderLayer() = default; ~CSHMReaderLayer() override = default; - void Initialize() override {} + void Initialize(const eCAL::eCALReader::SHM::SAttributes& attr_) override; void AddSubscription(const std::string& /*host_name_*/, const std::string& /*topic_name_*/, const std::string& /*topic_id_*/) override {} void RemSubscription(const std::string& /*host_name_*/, const std::string& /*topic_name_*/, const std::string& /*topic_id_*/) override {} void SetConnectionParameter(SReaderLayerPar& par_) override; private: - size_t OnNewShmFileContent(const std::string& topic_name_, const std::string& topic_id_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_); + size_t OnNewShmFileContent(const Payload::TopicInfo& topic_info_, const char* buf_, size_t len_, long long id_, long long clock_, long long time_, size_t hash_); + + eCAL::eCALReader::SHM::SAttributes m_attributes; }; } diff --git a/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp b/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp index a53d329b..99bd7cc5 100644 --- a/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp +++ b/ecal/core/src/readwrite/shm/ecal_writer_shm.cpp @@ -25,7 +25,6 @@ #include "ecal_def.h" #include "ecal_writer_shm.h" -#include "ecal/ecal_config.h" #include @@ -33,15 +32,12 @@ 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::Layer::SHM::Configuration& shm_config_) : - m_config(shm_config_) + CDataWriterSHM::CDataWriterSHM(const eCALWriter::SHM::SAttributes& attr_) : + m_attributes(attr_) { - 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); + if (m_attributes.memfile_buffer_count < 1) m_attributes.memfile_buffer_count = 1; + SetBufferCount(m_attributes.memfile_buffer_count); } SWriterInfo CDataWriterSHM::GetInfo() @@ -90,7 +86,7 @@ namespace eCAL void CDataWriterSHM::ApplySubscription(const std::string& host_name_, const int32_t process_id_, const std::string& /*topic_id_*/, const std::string& /*conn_par_*/) { // we accept local connections only - if (host_name_ != m_host_name) return; + if (host_name_ != m_attributes.host_name) return; for (auto& memory_file : m_memory_file_vec) { @@ -119,16 +115,16 @@ namespace eCAL // buffer count zero not allowed if (buffer_count_ < 1) { - Logging::Log(log_level_error, m_topic_name + "::CDataWriterSHM::SetBufferCount minimal number of memory files is 1 !"); + Logging::Log(log_level_error, m_attributes.topic_name + "::CDataWriterSHM::SetBufferCount minimal number of memory files is 1 !"); return false; } // prepare memfile attributes SSyncMemoryFileAttr memory_file_attr = {}; - 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.min_size = m_attributes.memfile_min_size_bytes; + memory_file_attr.reserve = m_attributes.memfile_reserve_percent; memory_file_attr.timeout_open_ms = PUB_MEMFILE_OPEN_TO; - memory_file_attr.timeout_ack_ms = m_config.acknowledge_timeout_ms; + memory_file_attr.timeout_ack_ms = m_attributes.acknowledge_timeout_ms; // retrieve the memory file size of existing files size_t memory_file_size(0); diff --git a/ecal/core/src/readwrite/shm/ecal_writer_shm.h b/ecal/core/src/readwrite/shm/ecal_writer_shm.h index 8126f499..2c3c122f 100644 --- a/ecal/core/src/readwrite/shm/ecal_writer_shm.h +++ b/ecal/core/src/readwrite/shm/ecal_writer_shm.h @@ -23,7 +23,7 @@ #pragma once -#include +#include "config/attributes/writer_shm_attributes.h" #include "io/shm/ecal_memfile_sync.h" #include "readwrite/ecal_writer_base.h" @@ -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::Layer::SHM::Configuration& shm_config_); + CDataWriterSHM(const eCALWriter::SHM::SAttributes& attr_); SWriterInfo GetInfo() override; @@ -53,7 +53,7 @@ namespace eCAL protected: bool SetBufferCount(size_t buffer_count_); - Publisher::Layer::SHM::Configuration m_config; + eCALWriter::SHM::SAttributes m_attributes; size_t m_write_idx = 0; std::vector> m_memory_file_vec; diff --git a/ecal/core/src/readwrite/tcp/config/attributes/data_reader_tcp_attributes.h b/ecal/core/src/readwrite/tcp/config/attributes/data_reader_tcp_attributes.h new file mode 100644 index 00000000..33586b0d --- /dev/null +++ b/ecal/core/src/readwrite/tcp/config/attributes/data_reader_tcp_attributes.h @@ -0,0 +1,38 @@ +/* ========================= 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 eCALReader + { + namespace TCP + { + struct SAttributes + { + size_t ecal_magic = (4 * sizeof(char)); + int max_reconnection_attempts; + size_t thread_pool_size; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/tcp/config/attributes/data_writer_tcp_attributes.h b/ecal/core/src/readwrite/tcp/config/attributes/data_writer_tcp_attributes.h new file mode 100644 index 00000000..8f6657a2 --- /dev/null +++ b/ecal/core/src/readwrite/tcp/config/attributes/data_writer_tcp_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 +#include + +namespace eCAL +{ + namespace eCALWriter + { + namespace TCP + { + struct SAttributes + { + std::string topic_name; + std::string topic_id; + + size_t thread_pool_size; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/tcp/config/attributes/tcp_reader_layer_attributes.h b/ecal/core/src/readwrite/tcp/config/attributes/tcp_reader_layer_attributes.h new file mode 100644 index 00000000..2bdb2d43 --- /dev/null +++ b/ecal/core/src/readwrite/tcp/config/attributes/tcp_reader_layer_attributes.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 ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace eCALReader + { + namespace TCPLayer + { + struct SAttributes + { + size_t thread_pool_size; + int max_reconnection_attempts; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/tcp/config/builder/data_reader_tcp_attribute_builder.cpp b/ecal/core/src/readwrite/tcp/config/builder/data_reader_tcp_attribute_builder.cpp new file mode 100644 index 00000000..9a3a5811 --- /dev/null +++ b/ecal/core/src/readwrite/tcp/config/builder/data_reader_tcp_attribute_builder.cpp @@ -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 ================================= +*/ + +#include "data_reader_tcp_attribute_builder.h" + +namespace eCAL +{ + namespace eCALReader + { + namespace TCP + { + TCP::SAttributes BuildTCPReaderAttributes(const TCPLayer::SAttributes& attr_) + { + TCP::SAttributes attributes; + + attributes.thread_pool_size = attr_.thread_pool_size; + attributes.max_reconnection_attempts = attr_.max_reconnection_attempts; + + return attributes; + } + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/tcp/config/builder/data_reader_tcp_attribute_builder.h b/ecal/core/src/readwrite/tcp/config/builder/data_reader_tcp_attribute_builder.h new file mode 100644 index 00000000..a74bdc65 --- /dev/null +++ b/ecal/core/src/readwrite/tcp/config/builder/data_reader_tcp_attribute_builder.h @@ -0,0 +1,35 @@ +/* ========================= 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 "readwrite/tcp/config/attributes/tcp_reader_layer_attributes.h" +#include "readwrite/tcp/config/attributes/data_reader_tcp_attributes.h" + + +namespace eCAL +{ + namespace eCALReader + { + namespace TCP + { + TCP::SAttributes BuildTCPReaderAttributes(const TCPLayer::SAttributes& attr_); + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/tcp/ecal_reader_tcp.cpp b/ecal/core/src/readwrite/tcp/ecal_reader_tcp.cpp index e916fcdc..af3893ec 100644 --- a/ecal/core/src/readwrite/tcp/ecal_reader_tcp.cpp +++ b/ecal/core/src/readwrite/tcp/ecal_reader_tcp.cpp @@ -22,6 +22,7 @@ **/ #include +#include "config/builder/data_reader_tcp_attribute_builder.h" #include "ecal_global_accessors.h" #include "ecal_reader_tcp.h" @@ -36,7 +37,10 @@ namespace eCAL //////////////// // READER //////////////// - CDataReaderTCP::CDataReaderTCP() : m_callback_active(false) {} + CDataReaderTCP::CDataReaderTCP(const eCAL::eCALReader::TCP::SAttributes& attr_) + : m_callback_active(false) + , m_attributes(attr_) + {} bool CDataReaderTCP::Create(std::shared_ptr& executor_) { @@ -75,7 +79,7 @@ namespace eCAL // add new session and activate callback if we add the first session if (new_session) { - m_subscriber->addSession(host_name_, port_, Config::GetTcpPubsubMaxReconnectionAttemps()); + m_subscriber->addSession(host_name_, port_, m_attributes.max_reconnection_attempts); if (!m_callback_active) { m_subscriber->setCallback(std::bind(&CDataReaderTCP::OnTcpMessage, this, std::placeholders::_1)); @@ -88,11 +92,9 @@ namespace eCAL void CDataReaderTCP::OnTcpMessage(const tcp_pubsub::CallbackData& data_) { - // extract header size - const size_t ecal_magic(4 * sizeof(char)); - // ECAL + header size field - const size_t header_length = ecal_magic + sizeof(uint16_t); - const uint16_t header_size = le16toh(*reinterpret_cast(data_.buffer_->data() + ecal_magic)); + // ECAL + header size field + const size_t header_length = m_attributes.ecal_magic + sizeof(uint16_t); + const uint16_t header_size = le16toh(*reinterpret_cast(data_.buffer_->data() + m_attributes.ecal_magic)); // extract header const char* header_payload = data_.buffer_->data() + header_length; @@ -105,12 +107,11 @@ namespace eCAL if (g_subgate() != nullptr) { // use this intermediate variables as optimization - const auto& ecal_header_topic = m_ecal_header.topic; - const auto& ecal_header_content = m_ecal_header.content; - // apply sample + const auto& ecal_header_topic_info = m_ecal_header.topic_info; + const auto& ecal_header_content = m_ecal_header.content; + g_subgate()->ApplySample( - ecal_header_topic.tname, - ecal_header_topic.tid, + ecal_header_topic_info, data_payload, static_cast(ecal_header_content.size), ecal_header_content.id, @@ -125,15 +126,18 @@ namespace eCAL //////////////// // LAYER //////////////// - CTCPReaderLayer::CTCPReaderLayer() : m_initialized(false) {} + CTCPReaderLayer::CTCPReaderLayer() + : m_initialized(false) + {} - void CTCPReaderLayer::Initialize() + void CTCPReaderLayer::Initialize(const eCAL::eCALReader::TCPLayer::SAttributes& attr_) { + m_attributes = attr_; 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); + m_executor = std::make_shared(m_attributes.thread_pool_size, tcp_pubsub_logger); } void CTCPReaderLayer::AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) @@ -143,7 +147,7 @@ namespace eCAL const std::lock_guard lock(m_datareadertcp_sync); if (m_datareadertcp_map.find(map_key) != m_datareadertcp_map.end()) return; - const std::shared_ptr reader = std::make_shared(); + const std::shared_ptr reader = std::make_shared(eCAL::eCALReader::TCP::BuildTCPReaderAttributes(m_attributes)); reader->Create(m_executor); m_datareadertcp_map.insert(std::pair>(map_key, reader)); diff --git a/ecal/core/src/readwrite/tcp/ecal_reader_tcp.h b/ecal/core/src/readwrite/tcp/ecal_reader_tcp.h index 2eae0b2a..b33264e3 100644 --- a/ecal/core/src/readwrite/tcp/ecal_reader_tcp.h +++ b/ecal/core/src/readwrite/tcp/ecal_reader_tcp.h @@ -24,6 +24,8 @@ #pragma once #include "readwrite/ecal_reader_layer.h" +#include "config/attributes/data_reader_tcp_attributes.h" +#include "config/attributes/tcp_reader_layer_attributes.h" #include #include @@ -42,7 +44,7 @@ namespace eCAL class CDataReaderTCP { public: - CDataReaderTCP(); + CDataReaderTCP(const eCAL::eCALReader::TCP::SAttributes& attr_); bool Create(std::shared_ptr& executor_); bool Destroy(); @@ -56,17 +58,18 @@ namespace eCAL std::shared_ptr m_subscriber; bool m_callback_active; + eCAL::eCALReader::TCP::SAttributes m_attributes; }; //////////////// // LAYER //////////////// - class CTCPReaderLayer : public CReaderLayer + class CTCPReaderLayer : public CReaderLayer { public: CTCPReaderLayer(); - void Initialize() override; + void Initialize(const eCAL::eCALReader::TCPLayer::SAttributes& attr_) override; void AddSubscription(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) override; void RemSubscription(const std::string& host_name_, const std::string& topic_name_, const std::string& topic_id_) override; @@ -80,5 +83,6 @@ namespace eCAL using DataReaderTCPMapT = std::unordered_map>; std::mutex m_datareadertcp_sync; DataReaderTCPMapT m_datareadertcp_map; + eCAL::eCALReader::TCPLayer::SAttributes m_attributes; }; } diff --git a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp index c9f98bf1..d1728912 100644 --- a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.cpp +++ b/ecal/core/src/readwrite/tcp/ecal_writer_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. @@ -22,6 +22,7 @@ **/ #include +#include #include "serialization/ecal_serialize_sample_payload.h" @@ -37,25 +38,20 @@ 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::Layer::TCP::Configuration& tcp_config_) : - m_config(tcp_config_) + CDataWriterTCP::CDataWriterTCP(const eCAL::eCALWriter::TCP::SAttributes& attr_) : + m_attributes(attr_) { { const std::lock_guard lock(g_tcp_writer_executor_mtx); if (!g_tcp_writer_executor) { - g_tcp_writer_executor = std::make_shared(Config::GetTcpPubsubWriterThreadpoolSize(), TcpPubsubLogger); + g_tcp_writer_executor = std::make_shared(m_attributes.thread_pool_size, TcpPubsubLogger); } } // create publisher m_publisher = std::make_shared(g_tcp_writer_executor); m_port = m_publisher->getPort(); - - // writer parameter - m_host_name = host_name_; - m_topic_name = topic_name_; - m_topic_id = topic_id_; } SWriterInfo CDataWriterTCP::GetInfo() @@ -79,9 +75,9 @@ namespace eCAL // create new payload sample (header information only, no payload) Payload::Sample proto_header; - auto& proto_header_topic = proto_header.topic; - proto_header_topic.tname = m_topic_name; - proto_header_topic.tid = m_topic_id; + auto& proto_header_topic = proto_header.topic_info; + proto_header_topic.tname = m_attributes.topic_name; + proto_header_topic.tid = m_attributes.topic_id; // set payload content (without payload) auto& proto_header_content = proto_header.content; diff --git a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h index 2aeedf01..af9ea016 100644 --- a/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h +++ b/ecal/core/src/readwrite/tcp/ecal_writer_tcp.h @@ -23,7 +23,7 @@ #pragma once -#include +#include "config/attributes/data_writer_tcp_attributes.h" #include "readwrite/ecal_writer_base.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::Layer::TCP::Configuration& tcp_config_); + CDataWriterTCP(const eCAL::eCALWriter::TCP::SAttributes& attr_); SWriterInfo GetInfo() override; @@ -49,7 +49,7 @@ namespace eCAL Registration::ConnectionPar GetConnectionParameter() override; private: - Publisher::Layer::TCP::Configuration m_config; + eCAL::eCALWriter::TCP::SAttributes m_attributes; std::vector m_header_buffer; diff --git a/ecal/core/src/readwrite/udp/config/attributes/reader_udp_attributes.h b/ecal/core/src/readwrite/udp/config/attributes/reader_udp_attributes.h new file mode 100644 index 00000000..20f958e9 --- /dev/null +++ b/ecal/core/src/readwrite/udp/config/attributes/reader_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 eCALReader + { + namespace UDP + { + struct SAttributes + { + std::string address; + int port; + bool broadcast; + bool loopback; + int receive_buffer; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/udp/config/attributes/writer_udp_attributes.h b/ecal/core/src/readwrite/udp/config/attributes/writer_udp_attributes.h new file mode 100644 index 00000000..c829864e --- /dev/null +++ b/ecal/core/src/readwrite/udp/config/attributes/writer_udp_attributes.h @@ -0,0 +1,45 @@ +/* ========================= 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 eCALWriter + { + namespace UDP + { + struct SAttributes + { + std::string address; + int port; + int ttl; + bool broadcast; + bool loopback; + int send_buffer; + + std::string host_name; + std::string topic_name; + std::string topic_id; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/udp/config/builder/udp_attribute_builder.cpp b/ecal/core/src/readwrite/udp/config/builder/udp_attribute_builder.cpp new file mode 100644 index 00000000..24744167 --- /dev/null +++ b/ecal/core/src/readwrite/udp/config/builder/udp_attribute_builder.cpp @@ -0,0 +1,62 @@ +/* ========================= 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 eCALReader + { + namespace UDP + { + eCAL::UDP::SReceiverAttr ConvertToIOUDPReceiverAttributes(const eCAL::eCALReader::UDP::SAttributes& attr_) + { + eCAL::UDP::SReceiverAttr receiver_attr; + receiver_attr.broadcast = attr_.broadcast; + receiver_attr.loopback = attr_.loopback; + + receiver_attr.rcvbuf = attr_.receive_buffer; + receiver_attr.port = attr_.port; + receiver_attr.address = attr_.address; + + return receiver_attr; + } + } + } + + namespace eCALWriter + { + namespace UDP + { + eCAL::UDP::SSenderAttr ConvertToIOUDPSenderAttributes(const SAttributes& attr_) + { + eCAL::UDP::SSenderAttr sender_attr; + sender_attr.broadcast = attr_.broadcast; + sender_attr.loopback = attr_.loopback; + + sender_attr.sndbuf = attr_.send_buffer; + sender_attr.port = attr_.port; + sender_attr.address = attr_.address; + sender_attr.ttl = attr_.ttl; + + return sender_attr; + } + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/udp/config/builder/udp_attribute_builder.h b/ecal/core/src/readwrite/udp/config/builder/udp_attribute_builder.h new file mode 100644 index 00000000..176ae2e1 --- /dev/null +++ b/ecal/core/src/readwrite/udp/config/builder/udp_attribute_builder.h @@ -0,0 +1,44 @@ +/* ========================= 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 "readwrite/udp/config/attributes/reader_udp_attributes.h" +#include "readwrite/udp/config/attributes/writer_udp_attributes.h" +#include "io/udp/ecal_udp_receiver_attr.h" +#include "io/udp/ecal_udp_sender_attr.h" + +namespace eCAL +{ + namespace eCALReader + { + namespace UDP + { + eCAL::UDP::SReceiverAttr ConvertToIOUDPReceiverAttributes(const SAttributes& attr_); + } + } + + namespace eCALWriter + { + namespace UDP + { + eCAL::UDP::SSenderAttr ConvertToIOUDPSenderAttributes(const SAttributes& attr_); + } + } +} \ No newline at end of file diff --git a/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp b/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp index 2036f6fe..b4ed915f 100644 --- a/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp +++ b/ecal/core/src/readwrite/udp/ecal_reader_udp.cpp @@ -28,6 +28,7 @@ #include "io/udp/ecal_udp_configurations.h" #include "pubsub/ecal_subgate.h" +#include "config/builder/udp_attribute_builder.h" #include #include @@ -39,40 +40,32 @@ namespace eCAL //////////////// // LAYER //////////////// - CUDPReaderLayer::CUDPReaderLayer() : - m_started(false), - m_local_mode(false) + CUDPReaderLayer::CUDPReaderLayer() : m_started(false) {} CUDPReaderLayer::~CUDPReaderLayer() = default; - void CUDPReaderLayer::Initialize() + void CUDPReaderLayer::Initialize(const eCAL::eCALReader::UDP::SAttributes& attr_) { + m_attributes = attr_; } void CUDPReaderLayer::AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) { if (!m_started) - { - // set local mode - m_local_mode = UDP::IsBroadcast(); - - // set network attributes - eCAL::UDP::SReceiverAttr attr; - attr.address = UDP::GetPayloadAddress(); - attr.port = UDP::GetPayloadPort(); - attr.broadcast = UDP::IsBroadcast(); - attr.loopback = true; - 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)); + m_payload_receiver = std::make_shared( + eCALReader::UDP::ConvertToIOUDPReceiverAttributes(m_attributes), + std::bind(&CUDPReaderLayer::HasSample, this, std::placeholders::_1), + std::bind(&CUDPReaderLayer::ApplySample, this, std::placeholders::_1, std::placeholders::_2) + ); m_started = true; } // we use udp broadcast in local mode - if (m_local_mode) return; + if (m_attributes.broadcast) return; // add topic name based multicast address const std::string mcast_address = UDP::GetTopicPayloadAddress(topic_name_); @@ -87,7 +80,7 @@ namespace eCAL void CUDPReaderLayer::RemSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) { // we use udp broadcast in local mode - if (m_local_mode) return; + if (m_attributes.broadcast) return; const std::string mcast_address = UDP::GetTopicPayloadAddress(topic_name_); if (m_topic_name_mcast_map.find(mcast_address) == m_topic_name_mcast_map.end()) diff --git a/ecal/core/src/readwrite/udp/ecal_reader_udp.h b/ecal/core/src/readwrite/udp/ecal_reader_udp.h index 5360dc5c..e0038d99 100644 --- a/ecal/core/src/readwrite/udp/ecal_reader_udp.h +++ b/ecal/core/src/readwrite/udp/ecal_reader_udp.h @@ -25,6 +25,7 @@ #include "io/udp/ecal_udp_sample_receiver.h" #include "readwrite/ecal_reader_layer.h" +#include "config/attributes/reader_udp_attributes.h" #include #include @@ -36,13 +37,13 @@ namespace eCAL //////////////// // LAYER //////////////// - class CUDPReaderLayer : public CReaderLayer + class CUDPReaderLayer : public CReaderLayer { public: CUDPReaderLayer(); ~CUDPReaderLayer() override; - void Initialize() override; + void Initialize(const eCAL::eCALReader::UDP::SAttributes& attr_) override; void AddSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) override; void RemSubscription(const std::string& /*host_name_*/, const std::string& topic_name_, const std::string& /*topic_id_*/) override; @@ -54,8 +55,9 @@ namespace eCAL bool ApplySample(const char* serialized_sample_data_, size_t serialized_sample_size_); bool m_started; - bool m_local_mode; std::shared_ptr m_payload_receiver; std::map m_topic_name_mcast_map; + + eCAL::eCALReader::UDP::SAttributes m_attributes; }; } diff --git a/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp b/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp index efae1208..76af9e7e 100644 --- a/ecal/core/src/readwrite/udp/ecal_writer_udp.cpp +++ b/ecal/core/src/readwrite/udp/ecal_writer_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. @@ -22,38 +22,27 @@ **/ #include +#include #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 "config/builder/udp_attribute_builder.h" #include namespace eCAL { - 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_) + CDataWriterUdpMC::CDataWriterUdpMC(const eCALWriter::UDP::SAttributes& attr_) : + m_attributes(attr_) { - m_host_name = host_name_; - m_topic_name = topic_name_; - m_topic_id = topic_id_; - - // set network attributes - eCAL::UDP::SSenderAttr attr; - attr.address = UDP::GetTopicPayloadAddress(topic_name_); - attr.port = UDP::GetPayloadPort(); - attr.ttl = UDP::GetMulticastTtl(); - attr.broadcast = UDP::IsBroadcast(); - attr.sndbuf = UDP::GetSendBufferSize(); - // create udp/sample sender with activated loop-back - attr.loopback = true; - m_sample_sender_loopback = std::make_shared(attr); + m_attributes.loopback = true; + m_sample_sender_loopback = std::make_shared(eCAL::eCALWriter::UDP::ConvertToIOUDPSenderAttributes(m_attributes)); // create udp/sample sender without activated loop-back - attr.loopback = false; - m_sample_sender_no_loopback = std::make_shared(attr); + m_attributes.loopback = false; + m_sample_sender_no_loopback = std::make_shared(eCAL::eCALWriter::UDP::ConvertToIOUDPSenderAttributes(m_attributes)); } SWriterInfo CDataWriterUdpMC::GetInfo() @@ -77,10 +66,11 @@ namespace eCAL Payload::Sample ecal_sample; ecal_sample.cmd_type = eCmdType::bct_set_sample; - auto& ecal_sample_topic = ecal_sample.topic; - ecal_sample_topic.hname = m_host_name; - ecal_sample_topic.tname = m_topic_name; - ecal_sample_topic.tid = m_topic_id; + // fill sample info + auto& ecal_sample_topic_info = ecal_sample.topic_info; + ecal_sample_topic_info.hname = m_attributes.host_name; + ecal_sample_topic_info.tname = m_attributes.topic_name; + ecal_sample_topic_info.tid = m_attributes.topic_id; // append content auto& ecal_sample_content = ecal_sample.content; @@ -100,14 +90,14 @@ namespace eCAL { if (m_sample_sender_loopback) { - sent = m_sample_sender_loopback->Send(ecal_sample.topic.tname, m_sample_buffer); + sent = m_sample_sender_loopback->Send(ecal_sample.topic_info.tname, m_sample_buffer); } } else { if (m_sample_sender_no_loopback) { - sent = m_sample_sender_no_loopback->Send(ecal_sample.topic.tname, m_sample_buffer); + sent = m_sample_sender_no_loopback->Send(ecal_sample.topic_info.tname, m_sample_buffer); } } } diff --git a/ecal/core/src/readwrite/udp/ecal_writer_udp.h b/ecal/core/src/readwrite/udp/ecal_writer_udp.h index 8a76cc04..bfcbbfc6 100644 --- a/ecal/core/src/readwrite/udp/ecal_writer_udp.h +++ b/ecal/core/src/readwrite/udp/ecal_writer_udp.h @@ -23,10 +23,9 @@ #pragma once -#include - #include "io/udp/ecal_udp_sample_sender.h" #include "readwrite/ecal_writer_base.h" +#include "config/attributes/writer_udp_attributes.h" #include #include @@ -37,17 +36,17 @@ 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::Layer::UDP::Configuration& udp_config_); + CDataWriterUdpMC(const eCALWriter::UDP::SAttributes& attr_); SWriterInfo GetInfo() override; bool Write(const void* buf_, const SWriterAttr& attr_) override; protected: - Publisher::Layer::UDP::Configuration m_config; - std::vector m_sample_buffer; std::shared_ptr m_sample_sender_loopback; std::shared_ptr m_sample_sender_no_loopback; + + eCALWriter::UDP::SAttributes m_attributes; }; } diff --git a/ecal/core/src/registration/config/attributes/registration_attributes.h b/ecal/core/src/registration/config/attributes/registration_attributes.h new file mode 100644 index 00000000..f19db2b3 --- /dev/null +++ b/ecal/core/src/registration/config/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 SUDPModeAttributes + { + std::string group; + int ttl; + }; + + struct SUDPAttributes + { + Types::UDPMode mode; + int port; + int sendbuffer; + int receivebuffer; + SUDPModeAttributes network; + SUDPModeAttributes 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/config/attributes/sample_applier_attributes.h b/ecal/core/src/registration/config/attributes/sample_applier_attributes.h new file mode 100644 index 00000000..efec45de --- /dev/null +++ b/ecal/core/src/registration/config/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/config/builder/sample_applier_attribute_builder.cpp b/ecal/core/src/registration/config/builder/sample_applier_attribute_builder.cpp new file mode 100644 index 00000000..cab74eef --- /dev/null +++ b/ecal/core/src/registration/config/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/config/builder/sample_applier_attribute_builder.h b/ecal/core/src/registration/config/builder/sample_applier_attribute_builder.h new file mode 100644 index 00000000..b8af9a45 --- /dev/null +++ b/ecal/core/src/registration/config/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/config/attributes/registration_attributes.h" +#include "registration/config/attributes/sample_applier_attributes.h" + +namespace eCAL +{ + namespace Registration + { + namespace SampleApplier + { + SAttributes BuildSampleApplierAttributes(const Registration::SAttributes& attr_); + } + } +} diff --git a/ecal/core/src/registration/config/builder/udp_shm_attribute_builder.cpp b/ecal/core/src/registration/config/builder/udp_shm_attribute_builder.cpp new file mode 100644 index 00000000..487ecc69 --- /dev/null +++ b/ecal/core/src/registration/config/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.receive_buffer = 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/config/builder/udp_shm_attribute_builder.h b/ecal/core/src/registration/config/builder/udp_shm_attribute_builder.h new file mode 100644 index 00000000..1ee7a142 --- /dev/null +++ b/ecal/core/src/registration/config/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/config/attributes/registration_sender_udp_attributes.h" +#include "registration/udp/config/attributes/registration_receiver_udp_attributes.h" +#include "registration/shm/config/attributes/registration_shm_attributes.h" +#include "registration/config/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 index 34b6104a..465193dd 100644 --- a/ecal/core/src/registration/ecal_process_registration.cpp +++ b/ecal/core/src/registration/ecal_process_registration.cpp @@ -38,6 +38,9 @@ eCAL::Registration::Sample eCAL::Registration::GetProcessRegisterSample() auto& process_sample_identifier = process_sample.identifier; process_sample_identifier.host_name = eCAL::Process::GetHostName(); process_sample_identifier.process_id = eCAL::Process::GetProcessID(); + // We need to set the pid as entity_id. + // However, we cannot send anything over the wire :( + process_sample_identifier.entity_id = std::to_string(process_sample_identifier.process_id); auto& process_sample_process = process_sample.process; process_sample_process.hgname = eCAL::Process::GetHostGroupName(); diff --git a/ecal/core/src/registration/ecal_registration.cpp b/ecal/core/src/registration/ecal_registration.cpp new file mode 100644 index 00000000..1a7108b1 --- /dev/null +++ b/ecal/core/src/registration/ecal_registration.cpp @@ -0,0 +1,509 @@ +/* ========================= 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_); + } + + ECAL_API CallbackToken AddPublisherEventCallback(const TopicIDCallbackT& callback_) + { + if (g_descgate() == nullptr) return CallbackToken(); + return g_descgate()->AddPublisherEventCallback(callback_); + } + + ECAL_API void RemPublisherEventCallback(CallbackToken token_) + { + if (g_descgate() == nullptr) return; + return g_descgate()->RemPublisherEventCallback(token_); + } + + 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_); + } + + ECAL_API CallbackToken AddSubscriberEventCallback(const TopicIDCallbackT& callback_) + { + if (g_descgate() == nullptr) return CallbackToken(); + return g_descgate()->AddSubscriberEventCallback(callback_); + } + + ECAL_API void RemSubscriberEventCallback(CallbackToken token_) + { + if (g_descgate() == nullptr) return; + return g_descgate()->RemSubscriberEventCallback(token_); + } + + 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 b8816c2f..88be2113 100644 --- a/ecal/core/src/registration/ecal_registration_provider.cpp +++ b/ecal/core/src/registration/ecal_registration_provider.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -44,13 +45,15 @@ #include #endif +#include "config/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_) { } @@ -63,27 +66,26 @@ namespace eCAL { if(m_created) return; - // send registration over udp or shared memory - m_use_registration_shm = Config::IsShmRegistrationEnabled(); - m_use_registration_udp = !m_use_registration_shm; - // TODO Create the registration sender #if ECAL_CORE_REGISTRATION_SHM - if (m_use_registration_shm) + if (m_attributes.shm_enabled) { - m_reg_sender = std::make_unique(); + 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)); } else { -#endif - m_reg_sender = std::make_unique(); -#if ECAL_CORE_REGISTRATION_SHM + 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; } @@ -135,7 +137,7 @@ namespace eCAL void CRegistrationProvider::AddSingleSample(const Registration::Sample& sample_) { const std::lock_guard lock(m_applied_sample_list_mtx); - m_applied_sample_list.samples.push_back(sample_); + m_applied_sample_list.push_back(sample_); } void CRegistrationProvider::RegisterSendThread() @@ -143,38 +145,39 @@ namespace eCAL // collect all registrations and send them out cyclic { // create sample list - Registration::SampleList sample_list; + m_send_thread_sample_list.clear(); // and add process registration sample - sample_list.samples.push_back(Registration::GetProcessRegisterSample()); + m_send_thread_sample_list.push_back(Registration::GetProcessRegisterSample()); #if ECAL_CORE_SUBSCRIBER // add subscriber registrations - if (g_subgate() != nullptr) g_subgate()->GetRegistrations(sample_list); + if (g_subgate() != nullptr) g_subgate()->GetRegistrations(m_send_thread_sample_list); #endif #if ECAL_CORE_PUBLISHER // add publisher registrations - if (g_pubgate() != nullptr) g_pubgate()->GetRegistrations(sample_list); + if (g_pubgate() != nullptr) g_pubgate()->GetRegistrations(m_send_thread_sample_list); #endif #if ECAL_CORE_SERVICE // add server registrations - if (g_servicegate() != nullptr) g_servicegate()->GetRegistrations(sample_list); + if (g_servicegate() != nullptr) g_servicegate()->GetRegistrations(m_send_thread_sample_list); // add client registrations - if (g_clientgate() != nullptr) g_clientgate()->GetRegistrations(sample_list); + if (g_clientgate() != nullptr) g_clientgate()->GetRegistrations(m_send_thread_sample_list); #endif - // send collected registration sample list - m_reg_sender->SendSampleList(sample_list); - - // send asynchronously applied samples at the end of the registration loop + // append applied samples list to sample list + if (!m_applied_sample_list.empty()) { const std::lock_guard lock(m_applied_sample_list_mtx); - m_reg_sender->SendSampleList(m_applied_sample_list); - m_applied_sample_list.samples.clear(); + std::copy(m_applied_sample_list.begin(), m_applied_sample_list.end(), std::back_inserter(m_send_thread_sample_list)); + m_applied_sample_list.clear(); } + + // send collected registration sample list + m_reg_sender->SendSampleList(m_send_thread_sample_list); } } } diff --git a/ecal/core/src/registration/ecal_registration_provider.h b/ecal/core/src/registration/ecal_registration_provider.h index 5fe66527..cba3e35f 100644 --- a/ecal/core/src/registration/ecal_registration_provider.h +++ b/ecal/core/src/registration/ecal_registration_provider.h @@ -31,16 +31,20 @@ #include "registration/ecal_registration_sender.h" #include "util/ecal_thread.h" +#include "config/attributes/registration_attributes.h" +#include #include #include +#include "util/ecal_thread.h" + namespace eCAL { class CRegistrationProvider { public: - CRegistrationProvider(); + CRegistrationProvider(const Registration::SAttributes& attr_); ~CRegistrationProvider(); void Start(); @@ -60,8 +64,9 @@ namespace eCAL std::mutex m_applied_sample_list_mtx; Registration::SampleList m_applied_sample_list; - - bool m_use_registration_udp; - bool m_use_registration_shm; + + Registration::SampleList m_send_thread_sample_list; + + 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 ed092060..d4d8fee6 100644 --- a/ecal/core/src/registration/ecal_registration_receiver.cpp +++ b/ecal/core/src/registration/ecal_registration_receiver.cpp @@ -27,18 +27,24 @@ #include "registration/ecal_registration_receiver.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 "config/builder/udp_shm_attribute_builder.h" +#include "config/builder/sample_applier_attribute_builder.h" + namespace eCAL { ////////////////////////////////////////////////////////////////// @@ -46,10 +52,17 @@ namespace eCAL ////////////////////////////////////////////////////////////////// std::atomic CRegistrationReceiver::m_created; - CRegistrationReceiver::CRegistrationReceiver() - : m_use_registration_udp(false) - , m_use_registration_shm(false) - , m_sample_applier(Config::IsNetworkEnabled(), false, Process::GetHostGroupName(), Process::GetProcessID()) + CRegistrationReceiver::CRegistrationReceiver(const Registration::SAttributes& attr_) + : m_timeout_provider(nullptr) + , m_timeout_provider_thread(nullptr) +#if ECAL_CORE_TRANSPORT_UDP + , m_registration_receiver_udp(nullptr) +#endif +#if ECAL_CORE_REGISTRATION_SHM + , m_registration_receiver_shm(nullptr) +#endif + , m_sample_applier(Registration::SampleApplier::BuildSampleApplierAttributes(attr_)) + , m_attributes(attr_) { // Connect User registration callback and gates callback with the sample applier m_sample_applier.SetCustomApplySampleCallback("gates", [](const eCAL::Registration::Sample& sample_) @@ -75,19 +88,30 @@ namespace eCAL { if(m_created) return; - // receive registration via udp or shared memory - m_use_registration_shm = Config::IsShmRegistrationEnabled(); - m_use_registration_udp = !m_use_registration_shm; + 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) { - m_registration_receiver_udp = std::make_unique([this](const Registration::Sample& sample_) {return m_sample_applier.ApplySample(sample_); }); + 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_registration_receiver_shm = std::make_unique([this](const Registration::Sample& sample_) {return m_sample_applier.ApplySample(sample_); }); + m_registration_receiver_shm = std::make_unique([this](const Registration::Sample& sample_) {return m_sample_applier.ApplySample(sample_); }, Registration::BuildSHMAttributes(m_attributes)); } #endif @@ -99,18 +123,22 @@ namespace eCAL if(!m_created) return; // stop network registration receive thread - if (m_use_registration_udp) + if (m_attributes.udp_enabled) { m_registration_receiver_udp = nullptr; } #if ECAL_CORE_REGISTRATION_SHM - if (m_use_registration_shm) + if (m_attributes.shm_enabled) { m_registration_receiver_shm = nullptr; } #endif + m_timeout_provider_thread = nullptr; + m_sample_applier.RemCustomApplySampleCallback("timeout"); + m_timeout_provider = nullptr; + m_created = false; } diff --git a/ecal/core/src/registration/ecal_registration_receiver.h b/ecal/core/src/registration/ecal_registration_receiver.h index 2a6d1c61..91ca5d85 100644 --- a/ecal/core/src/registration/ecal_registration_receiver.h +++ b/ecal/core/src/registration/ecal_registration_receiver.h @@ -34,6 +34,7 @@ #include "registration/ecal_registration_sample_applier.h" #include "registration/ecal_registration_sample_applier_gates.h" #include "registration/ecal_registration_sample_applier_user.h" +#include "config/attributes/registration_attributes.h" #include #include @@ -48,10 +49,17 @@ 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? @@ -72,20 +80,23 @@ namespace eCAL // why is this a static variable? can someone explain? static std::atomic m_created; + // this class gets samples and tracks them for timouts + std::unique_ptr> m_timeout_provider; + std::unique_ptr m_timeout_provider_thread; + 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; + Registration::CSampleApplier m_sample_applier; // 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; + Registration::CSampleApplierUser m_user_applier; + + 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 index 975cfbc0..0db73c20 100644 --- a/ecal/core/src/registration/ecal_registration_sample_applier.cpp +++ b/ecal/core/src/registration/ecal_registration_sample_applier.cpp @@ -26,18 +26,14 @@ namespace eCAL ////////////////////////////////////////////////////////////////// // CSampleApplier ////////////////////////////////////////////////////////////////// - CSampleApplier::CSampleApplier(bool network, bool loopback, const std::string& host_group_name, uint32_t pid) - : - m_network(network), - m_loopback(loopback), - m_host_group_name(host_group_name), - m_pid(pid) + CSampleApplier::CSampleApplier(const SampleApplier::SAttributes& attr_) + : m_attributes(attr_) { } void CSampleApplier::EnableLoopback(bool state_) { - m_loopback = state_; + m_attributes.loopback = state_; } bool CSampleApplier::ApplySample(const Registration::Sample& sample_) @@ -85,9 +81,9 @@ namespace eCAL const std::string& sample_host_group_name = host_group_name.empty() ? host_name : host_group_name; - if (sample_host_group_name.empty() || m_host_group_name.empty()) + if (sample_host_group_name.empty() || m_attributes.host_group_name.empty()) return false; - if (sample_host_group_name != m_host_group_name) + if (sample_host_group_name != m_attributes.host_group_name) return false; return true; @@ -97,7 +93,7 @@ namespace eCAL { // is this actually sufficient? should we also check host_name? const int32_t pid = sample_.identifier.process_id; - return pid == m_pid; + return pid == m_attributes.process_id; } bool CSampleApplier::AcceptRegistrationSample(const Registration::Sample& sample_) @@ -107,12 +103,12 @@ namespace eCAL { // register if the sample is from another process // or if loopback mode is enabled - return !IsSameProcess(sample_) || m_loopback; + return !IsSameProcess(sample_) || m_attributes.loopback; } else { // if the sample is from an external host, register only if network mode is enabled - return m_network; + return m_attributes.network_enabled; } } diff --git a/ecal/core/src/registration/ecal_registration_sample_applier.h b/ecal/core/src/registration/ecal_registration_sample_applier.h index bd2fcf88..7b313de4 100644 --- a/ecal/core/src/registration/ecal_registration_sample_applier.h +++ b/ecal/core/src/registration/ecal_registration_sample_applier.h @@ -29,6 +29,7 @@ #include #include "serialization/ecal_struct_sample_registration.h" +#include "config/attributes/sample_applier_attributes.h" #include #include @@ -42,8 +43,7 @@ namespace eCAL class CSampleApplier { public: - // to be replaced by config version soon! - CSampleApplier(bool network, bool loopback, const std::string& host_group_name, uint32_t pid); + CSampleApplier(const SampleApplier::SAttributes& attr_); // to be removed for eCAL 6, but keep until eCAL 5.14 void EnableLoopback(bool state_); @@ -59,10 +59,7 @@ namespace eCAL bool AcceptRegistrationSample(const Registration::Sample& sample_); - bool m_network; - bool m_loopback; - std::string m_host_group_name; - int32_t m_pid; + 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 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 00000000..28171908 --- /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 00000000..8aac6235 --- /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/shm/config/attributes/registration_shm_attributes.h b/ecal/core/src/registration/shm/config/attributes/registration_shm_attributes.h new file mode 100644 index 00000000..922d6751 --- /dev/null +++ b/ecal/core/src/registration/shm/config/attributes/registration_shm_attributes.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 ================================= +*/ + +#pragma once + +#include + +namespace eCAL +{ + namespace Registration + { + namespace SHM + { + struct SAttributes + { + std::string domain; + size_t 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 18c29834..f23622d2 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 321a42b7..d7e76275 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 "config/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/shm/ecal_registration_receiver_shm.cpp b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp index 895f67e0..bb62ded2 100644 --- a/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp +++ b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.cpp @@ -40,11 +40,11 @@ namespace eCAL // CMemfileRegistrationReceiver ////////////////////////////////////////////////////////////////// - CRegistrationReceiverSHM::CRegistrationReceiverSHM(RegistrationApplySampleCallbackT apply_sample_callback) + CRegistrationReceiverSHM::CRegistrationReceiverSHM(RegistrationApplySampleCallbackT apply_sample_callback, const Registration::SHM::SAttributes& attr_) : m_apply_sample_callback(apply_sample_callback) { m_memfile_broadcast = std::make_unique(); - m_memfile_broadcast->Create(Config::Experimental::GetShmMonitoringDomain(), Config::Experimental::GetShmMonitoringQueueSize()); + m_memfile_broadcast->Create(attr_); m_memfile_broadcast->FlushLocalEventQueue(); m_memfile_broadcast_reader = std::make_unique(); @@ -70,15 +70,17 @@ namespace eCAL void CRegistrationReceiverSHM::Receive() { + // At the moment this function is called synchronously by a dedicated thread. + // If this changes, we need to protect the sample list member variable MemfileBroadcastMessageListT message_list; if (m_memfile_broadcast_reader->Read(message_list, 0)) { - eCAL::Registration::SampleList sample_list; + m_sample_list.clear(); for (const auto& message : message_list) { - if (DeserializeFromBuffer(static_cast(message.data), message.size, sample_list)) + if (DeserializeFromBuffer(static_cast(message.data), message.size, m_sample_list)) { - for (const auto& sample : sample_list.samples) + for (const auto& sample : m_sample_list) { m_apply_sample_callback(sample); } diff --git a/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h index 968f6af3..ad4f1d65 100644 --- a/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h +++ b/ecal/core/src/registration/shm/ecal_registration_receiver_shm.h @@ -29,6 +29,7 @@ #include #include +#include "config/attributes/registration_shm_attributes.h" namespace eCAL { @@ -39,7 +40,7 @@ namespace eCAL class CRegistrationReceiverSHM { public: - CRegistrationReceiverSHM(RegistrationApplySampleCallbackT apply_sample_callback); + CRegistrationReceiverSHM(RegistrationApplySampleCallbackT apply_sample_callback, const Registration::SHM::SAttributes& attr_); ~CRegistrationReceiverSHM(); // default copy constructor @@ -58,6 +59,8 @@ namespace eCAL std::unique_ptr m_memfile_broadcast_reader; std::unique_ptr m_memfile_broadcast_reader_thread; + eCAL::Registration::SampleList m_sample_list; + 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 index c8fed5d3..c27eb9a3 100644 --- a/ecal/core/src/registration/shm/ecal_registration_sender_shm.cpp +++ b/ecal/core/src/registration/shm/ecal_registration_sender_shm.cpp @@ -30,10 +30,10 @@ #include "serialization/ecal_serialize_sample_registration.h" -eCAL::CRegistrationSenderSHM::CRegistrationSenderSHM() +eCAL::CRegistrationSenderSHM::CRegistrationSenderSHM(const Registration::SHM::SAttributes& attr_) { - 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()); + 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); } @@ -54,13 +54,13 @@ bool eCAL::CRegistrationSenderSHM::SendSampleList(const Registration::SampleList { bool return_value{true}; // serialize whole sample list - std::vector sample_list_buffer; - if (SerializeToBuffer(sample_list, sample_list_buffer)) + m_sample_list_buffer.clear(); + if (SerializeToBuffer(sample_list, m_sample_list_buffer)) { - if (!sample_list_buffer.empty()) + if (!m_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_value &= m_memfile_broadcast_writer.Write(m_sample_list_buffer.data(), m_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 index 67f4638b..88f0a312 100644 --- a/ecal/core/src/registration/shm/ecal_registration_sender_shm.h +++ b/ecal/core/src/registration/shm/ecal_registration_sender_shm.h @@ -33,12 +33,16 @@ #include "registration/shm/ecal_memfile_broadcast.h" #include "registration/shm/ecal_memfile_broadcast_writer.h" +#include "config/attributes/registration_shm_attributes.h" + +#include + namespace eCAL { class CRegistrationSenderSHM : public CRegistrationSender { public: - CRegistrationSenderSHM(); + CRegistrationSenderSHM(const Registration::SHM::SAttributes& attr_); ~CRegistrationSenderSHM() override; // Special member functionss @@ -53,5 +57,6 @@ namespace eCAL private: CMemoryFileBroadcast m_memfile_broadcast; CMemoryFileBroadcastWriter m_memfile_broadcast_writer; + std::vector m_sample_list_buffer; }; } \ No newline at end of file diff --git a/ecal/core/src/registration/udp/config/attributes/registration_receiver_udp_attributes.h b/ecal/core/src/registration/udp/config/attributes/registration_receiver_udp_attributes.h new file mode 100644 index 00000000..eb677166 --- /dev/null +++ b/ecal/core/src/registration/udp/config/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 receive_buffer = 1024 * 1024; + }; + } + } +} \ No newline at end of file diff --git a/ecal/core/src/registration/udp/config/attributes/registration_sender_udp_attributes.h b/ecal/core/src/registration/udp/config/attributes/registration_sender_udp_attributes.h new file mode 100644 index 00000000..8af5f51a --- /dev/null +++ b/ecal/core/src/registration/udp/config/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/config/builder/udp_attribute_builder.cpp b/ecal/core/src/registration/udp/config/builder/udp_attribute_builder.cpp new file mode 100644 index 00000000..f90e184f --- /dev/null +++ b/ecal/core/src/registration/udp/config/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_.receive_buffer; + 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/config/builder/udp_attribute_builder.h b/ecal/core/src/registration/udp/config/builder/udp_attribute_builder.h new file mode 100644 index 00000000..84e153bc --- /dev/null +++ b/ecal/core/src/registration/udp/config/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/config/attributes/registration_sender_udp_attributes.h" +#include "registration/udp/config/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 index 0ec879a0..1d7c39c3 100644 --- a/ecal/core/src/registration/udp/ecal_registration_receiver_udp.cpp +++ b/ecal/core/src/registration/udp/ecal_registration_receiver_udp.cpp @@ -19,35 +19,19 @@ #include "registration/udp/ecal_registration_receiver_udp.h" -#include "io/udp/ecal_udp_receiver_attr.h" #include "io/udp/ecal_udp_sample_receiver.h" #include "io/udp/ecal_udp_configurations.h" #include "serialization/ecal_serialize_sample_registration.h" #include -namespace -{ - using namespace eCAL; - UDP::SReceiverAttr CreateAttributes() - { - // set network attributes - eCAL::UDP::SReceiverAttr attr; - attr.address = UDP::GetRegistrationAddress(); - attr.port = UDP::GetRegistrationPort(); - attr.broadcast = UDP::IsBroadcast(); - attr.loopback = true; - attr.rcvbuf = UDP::GetReceiveBufferSize(); - return attr; - } - -} +#include "registration/udp/config/builder/udp_attribute_builder.h" using namespace eCAL; -eCAL::CRegistrationReceiverUDP::CRegistrationReceiverUDP(RegistrationApplySampleCallbackT apply_sample_callback) +eCAL::CRegistrationReceiverUDP::CRegistrationReceiverUDP(RegistrationApplySampleCallbackT apply_sample_callback, const Registration::UDP::SReceiverAttributes& attr_) : m_registration_receiver(std::make_unique( - CreateAttributes(), - [](const std::string& sample_name_) {return true; }, + 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; diff --git a/ecal/core/src/registration/udp/ecal_registration_receiver_udp.h b/ecal/core/src/registration/udp/ecal_registration_receiver_udp.h index d547d2fe..cbab1551 100644 --- a/ecal/core/src/registration/udp/ecal_registration_receiver_udp.h +++ b/ecal/core/src/registration/udp/ecal_registration_receiver_udp.h @@ -26,6 +26,7 @@ #include #include +#include "registration/udp/config/attributes/registration_receiver_udp_attributes.h" namespace eCAL { @@ -37,7 +38,7 @@ namespace eCAL class CRegistrationReceiverUDP { public: - CRegistrationReceiverUDP(RegistrationApplySampleCallbackT apply_sample_callback); + CRegistrationReceiverUDP(RegistrationApplySampleCallbackT apply_sample_callback, const Registration::UDP::SReceiverAttributes& attr_); ~CRegistrationReceiverUDP(); // Special member functionss diff --git a/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp b/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp index ab8d5fed..2a0fce33 100644 --- a/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp +++ b/ecal/core/src/registration/udp/ecal_registration_sender_udp.cpp @@ -32,27 +32,12 @@ #include "io/udp/ecal_udp_configurations.h" #include -namespace -{ - using namespace eCAL; - UDP::SSenderAttr CreateAttributes() - { - 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 = UDP::GetSendBufferSize(); - return attr; - } - -} +#include "registration/udp/config/builder/udp_attribute_builder.h" namespace eCAL { - CRegistrationSenderUDP::CRegistrationSenderUDP() - : m_reg_sample_snd(CreateAttributes()) + CRegistrationSenderUDP::CRegistrationSenderUDP(const eCAL::Registration::UDP::SSenderAttributes& attr_) + : m_reg_sample_snd(Registration::UDP::ConvertToIOUDPSenderAttributes(attr_)) { } @@ -73,7 +58,7 @@ namespace eCAL bool CRegistrationSenderUDP::SendSampleList(const Registration::SampleList& sample_list) { bool return_value{ true }; - for (const auto& sample : sample_list.samples) + for (const auto& sample : sample_list) { return_value &= SendSample(sample); } diff --git a/ecal/core/src/registration/udp/ecal_registration_sender_udp.h b/ecal/core/src/registration/udp/ecal_registration_sender_udp.h index ad49e8ed..f39fc97a 100644 --- a/ecal/core/src/registration/udp/ecal_registration_sender_udp.h +++ b/ecal/core/src/registration/udp/ecal_registration_sender_udp.h @@ -29,13 +29,14 @@ #include "registration/ecal_registration_sender.h" #include "io/udp/ecal_udp_sample_sender.h" +#include "registration/udp/config/attributes/registration_sender_udp_attributes.h" namespace eCAL { class CRegistrationSenderUDP : public CRegistrationSender { public: - CRegistrationSenderUDP(); + CRegistrationSenderUDP(const eCAL::Registration::UDP::SSenderAttributes& attr_); ~CRegistrationSenderUDP() override; // Special member functionss diff --git a/ecal/core/src/serialization/ecal_serialize_common.cpp b/ecal/core/src/serialization/ecal_serialize_common.cpp index 6bb0ff62..13d06492 100644 --- a/ecal/core/src/serialization/ecal_serialize_common.cpp +++ b/ecal/core/src/serialization/ecal_serialize_common.cpp @@ -22,7 +22,7 @@ * @brief eCAL common (de)serialization **/ -#include "nanopb/ecal.pb.h" +#include "nanopb/ecal/core/pb/ecal.npb.h" #include "nanopb/pb_decode.h" #include "nanopb/pb_encode.h" #include @@ -202,12 +202,12 @@ namespace eCAL /////////////////////////////////////////////// // list /////////////////////////////////////////////// - bool encode_string_list_field(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg) + bool encode_string_vector_field(pb_ostream_t* stream, const pb_field_iter_t* field, void* const* arg) { if (arg == nullptr) return false; if (*arg == nullptr) return false; - auto* str_list = static_cast*>(*arg); + auto* str_list = static_cast*>(*arg); for (const auto& str : *str_list) { @@ -225,21 +225,20 @@ namespace eCAL return true; } - bool decode_string_list_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) + bool decode_string_vector_field(pb_istream_t* stream, const pb_field_iter_t* /*field*/, void** arg) { if (arg == nullptr) return false; if (*arg == nullptr) return false; + auto* tgt_list = static_cast*>(*arg); + auto& tgt_string = tgt_list->push_back(); + size_t len = stream->bytes_left; - std::string tgt_string; tgt_string.resize(len); if (!pb_read(stream, (pb_byte_t*)(tgt_string.data()), tgt_string.size())) // NOLINT(*-pro-type-cstyle-cast) return false; - auto* tgt_list = static_cast*>(*arg); - tgt_list->push_back(tgt_string); - return true; } @@ -251,7 +250,7 @@ namespace eCAL if (arg == nullptr) return false; if (*arg == nullptr) return false; - auto* layer_vec = static_cast*>(*arg); + auto* layer_vec = static_cast*>(*arg); for (auto layer : *layer_vec) { @@ -278,7 +277,7 @@ namespace eCAL // shm layer parameter pb_layer.par_layer.has_layer_par_shm = true; - pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.encode = &encode_string_list_field; // NOLINT(*-pro-type-union-access) + pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.encode = &encode_string_vector_field; // NOLINT(*-pro-type-union-access) pb_layer.par_layer.layer_par_shm.memory_file_list.arg = (void*)(&layer.par_layer.layer_par_shm.memory_file_list); if (!pb_encode_submessage(stream, eCAL_pb_TLayer_fields, &pb_layer)) @@ -290,7 +289,7 @@ namespace eCAL return true; } - void encode_registration_layer(pb_callback_t& pb_callback, const std::vector& layer_vec) + void encode_registration_layer(pb_callback_t& pb_callback, const Util::CExpandingVector& layer_vec) { pb_callback.funcs.encode = &encode_registration_layer_field; // NOLINT(*-pro-type-union-access) pb_callback.arg = (void*)(&layer_vec); @@ -302,10 +301,11 @@ namespace eCAL if (*arg == nullptr) return false; eCAL_pb_TLayer pb_layer = eCAL_pb_TLayer_init_default; - eCAL::Registration::TLayer layer{}; + auto* tgt_vector = static_cast*>(*arg); + auto& layer = tgt_vector->push_back(); // decode shm layer parameter - pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.decode = &decode_string_list_field; // NOLINT(*-pro-type-union-access) + pb_layer.par_layer.layer_par_shm.memory_file_list.funcs.decode = &decode_string_vector_field; // NOLINT(*-pro-type-union-access) pb_layer.par_layer.layer_par_shm.memory_file_list.arg = (void*)(&layer.par_layer.layer_par_shm.memory_file_list); if (!pb_decode(stream, eCAL_pb_TLayer_fields, &pb_layer)) @@ -322,14 +322,10 @@ namespace eCAL // apply tcp layer parameter layer.par_layer.layer_par_tcp.port = pb_layer.par_layer.layer_par_tcp.port; - // add layer - auto* tgt_vector = static_cast*>(*arg); - tgt_vector->push_back(layer); - return true; } - void decode_registration_layer(pb_callback_t& pb_callback, std::vector& layer_vec) + void decode_registration_layer(pb_callback_t& pb_callback, Util::CExpandingVector& layer_vec) { pb_callback.funcs.decode = &decode_registration_layer_field; // NOLINT(*-pro-type-union-access) pb_callback.arg = &layer_vec; @@ -343,7 +339,7 @@ namespace eCAL if (arg == nullptr) return false; if (*arg == nullptr) return false; - auto* method_vec = static_cast*>(*arg); + auto* method_vec = static_cast*>(*arg); for (const auto& method : *method_vec) { @@ -369,7 +365,7 @@ namespace eCAL return true; } - void encode_service_methods(pb_callback_t& pb_callback, const std::vector& method_vec) + void encode_service_methods(pb_callback_t& pb_callback, const eCAL::Util::CExpandingVector& method_vec) { pb_callback.funcs.encode = &encode_service_methods_field; // NOLINT(*-pro-type-union-access) pb_callback.arg = (void*)(&method_vec); @@ -381,7 +377,8 @@ namespace eCAL if (*arg == nullptr) return false; eCAL_pb_Method pb_method = eCAL_pb_Method_init_default; - eCAL::Service::Method method{}; + auto* method_vec = static_cast*>(*arg); + auto& method = method_vec->push_back(); // decode method parameter decode_string(pb_method.mname, method.mname); @@ -399,14 +396,10 @@ namespace eCAL // apply method values method.call_count = pb_method.call_count; - // add method to vector - auto* method_vec = static_cast*>(*arg); - method_vec->emplace_back(method); - return true; } - void decode_service_methods(pb_callback_t& pb_callback, std::vector& method_vec) + void decode_service_methods(pb_callback_t& pb_callback, eCAL::Util::CExpandingVector& method_vec) { pb_callback.funcs.decode = &decode_service_methods_field; // NOLINT(*-pro-type-union-access) pb_callback.arg = &method_vec; diff --git a/ecal/core/src/serialization/ecal_serialize_common.h b/ecal/core/src/serialization/ecal_serialize_common.h index 634cc960..0a38bb7d 100644 --- a/ecal/core/src/serialization/ecal_serialize_common.h +++ b/ecal/core/src/serialization/ecal_serialize_common.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. @@ -56,10 +56,10 @@ namespace eCAL void encode_map(pb_callback_t& pb_callback, const std::map& str_map); void decode_map(pb_callback_t& pb_callback, std::map& str_map); - void encode_registration_layer(pb_callback_t& pb_callback, const std::vector& layer_vec); - void decode_registration_layer(pb_callback_t& pb_callback, std::vector& layer_vec); + void encode_registration_layer(pb_callback_t& pb_callback, const Util::CExpandingVector& layer_vec); + void decode_registration_layer(pb_callback_t& pb_callback, Util::CExpandingVector& layer_vec); - void encode_service_methods(pb_callback_t& pb_callback, const std::vector& method_vec); - void decode_service_methods(pb_callback_t& pb_callback, std::vector& method_vec); + void encode_service_methods(pb_callback_t& pb_callback, const Util::CExpandingVector& method_vec); + void decode_service_methods(pb_callback_t& pb_callback, Util::CExpandingVector& method_vec); } } diff --git a/ecal/core/src/serialization/ecal_serialize_logging.cpp b/ecal/core/src/serialization/ecal_serialize_logging.cpp index ef679925..89c75664 100644 --- a/ecal/core/src/serialization/ecal_serialize_logging.cpp +++ b/ecal/core/src/serialization/ecal_serialize_logging.cpp @@ -24,7 +24,7 @@ #include "nanopb/pb_encode.h" #include "nanopb/pb_decode.h" -#include "nanopb/logging.pb.h" +#include "nanopb/ecal/core/pb/logging.npb.h" #include "ecal_serialize_common.h" #include "ecal_serialize_logging.h" diff --git a/ecal/core/src/serialization/ecal_serialize_monitoring.cpp b/ecal/core/src/serialization/ecal_serialize_monitoring.cpp index 5e0bd04a..59fafbff 100644 --- a/ecal/core/src/serialization/ecal_serialize_monitoring.cpp +++ b/ecal/core/src/serialization/ecal_serialize_monitoring.cpp @@ -24,7 +24,7 @@ #include "nanopb/pb_encode.h" #include "nanopb/pb_decode.h" -#include "nanopb/monitoring.pb.h" +#include "nanopb/ecal/core/pb/monitoring.npb.h" #include "ecal_serialize_common.h" #include "ecal_serialize_monitoring.h" diff --git a/ecal/core/src/serialization/ecal_serialize_sample_payload.cpp b/ecal/core/src/serialization/ecal_serialize_sample_payload.cpp index 45e2b920..f80ceb27 100644 --- a/ecal/core/src/serialization/ecal_serialize_sample_payload.cpp +++ b/ecal/core/src/serialization/ecal_serialize_sample_payload.cpp @@ -24,7 +24,7 @@ #include "nanopb/pb_encode.h" #include "nanopb/pb_decode.h" -#include "nanopb/ecal.pb.h" +#include "nanopb/ecal/core/pb/ecal.npb.h" #include "ecal_serialize_common.h" #include "ecal_struct_sample_payload.h" @@ -73,11 +73,13 @@ namespace // topic information pb_sample_.has_topic = true; // hname - eCAL::nanopb::encode_string(pb_sample_.topic.hname, payload_.topic.hname); + eCAL::nanopb::encode_string(pb_sample_.topic.hname, payload_.topic_info.hname); + // pid + pb_sample_.topic.pid = payload_.topic_info.pid; // tid - eCAL::nanopb::encode_string(pb_sample_.topic.tid, payload_.topic.tid); + eCAL::nanopb::encode_string(pb_sample_.topic.tid, payload_.topic_info.tid); // tname - eCAL::nanopb::encode_string(pb_sample_.topic.tname, payload_.topic.tname); + eCAL::nanopb::encode_string(pb_sample_.topic.tname, payload_.topic_info.tname); // topic content pb_sample_.has_content = true; @@ -148,11 +150,11 @@ namespace // assign decoder /////////////////////////////////////////////// // hname - eCAL::nanopb::decode_string(pb_sample.topic.hname, payload_.topic.hname); + eCAL::nanopb::decode_string(pb_sample.topic.hname, payload_.topic_info.hname); // tid - eCAL::nanopb::decode_string(pb_sample.topic.tid, payload_.topic.tid); + eCAL::nanopb::decode_string(pb_sample.topic.tid, payload_.topic_info.tid); // tname - eCAL::nanopb::decode_string(pb_sample.topic.tname, payload_.topic.tname); + eCAL::nanopb::decode_string(pb_sample.topic.tname, payload_.topic_info.tname); // topic content payload payload_.content.payload.type = eCAL::Payload::pl_vec; eCAL::nanopb::decode_bytes(pb_sample.content.payload, payload_.content.payload.vec); @@ -177,6 +179,9 @@ namespace // command type payload_.cmd_type = static_cast(pb_sample.cmd_type); + // pid + payload_.topic_info.pid = pb_sample.topic.pid; + // topic content payload_.content.id = pb_sample.content.id; payload_.content.clock = pb_sample.content.clock; diff --git a/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp b/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp index 8390d1e0..744f6ebd 100644 --- a/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp +++ b/ecal/core/src/serialization/ecal_serialize_sample_registration.cpp @@ -24,7 +24,7 @@ #include "nanopb/pb_encode.h" #include "nanopb/pb_decode.h" -#include "nanopb/ecal.pb.h" +#include "nanopb/ecal/core/pb/ecal.npb.h" #include "ecal_serialize_common.h" #include "ecal_serialize_sample_registration.h" @@ -33,6 +33,7 @@ #include #include #include +#include #include namespace @@ -406,6 +407,8 @@ namespace registration_.process.rclock = pb_sample_.process.rclock; // pid registration_.identifier.process_id = pb_sample_.process.pid; + // tid -> we need to use the PID here, because we don't have a designated field for it + registration_.identifier.entity_id = std::to_string(registration_.identifier.process_id); // state.severity registration_.process.state.severity = static_cast(pb_sample_.process.state.severity); // state.severity_level @@ -505,7 +508,7 @@ namespace if (arg == nullptr) return false; if (*arg == nullptr) return false; - auto* sample_list = static_cast*>(*arg); + auto* sample_list = static_cast(*arg); for (const auto& sample : *sample_list) { @@ -535,7 +538,7 @@ namespace // prepare sample for encoding /////////////////////////////////////////////// pb_sample_list_.samples.funcs.encode = &encode_sample_list_field; // NOLINT(*-pro-type-union-access) - pb_sample_list_.samples.arg = (void*)(®istration_list_.samples); + pb_sample_list_.samples.arg = (void*)(®istration_list_); /////////////////////////////////////////////// // evaluate byte size @@ -582,7 +585,11 @@ namespace if (*arg == nullptr) return false; eCAL_pb_Sample pb_sample = eCAL_pb_Sample_init_default; - eCAL::Registration::Sample sample{}; + + // add sample to list + auto* sample_list = static_cast(*arg); + // Create a new element directly at the end of the vector + auto& sample = sample_list->push_back(); // prepare sample for decoding PrepareDecoding(pb_sample, sample); @@ -596,10 +603,6 @@ namespace // apply sample values AssignValues(pb_sample, sample); - // add sample to list - auto* sample_list = static_cast*>(*arg); - sample_list->push_back(sample); - return true; } @@ -615,7 +618,7 @@ namespace // prepare sample for decoding /////////////////////////////////////////////// pb_sample_list.samples.funcs.decode = &decode_sample_list_field; // NOLINT(*-pro-type-union-access) - pb_sample_list.samples.arg = ®istration_list_.samples; + pb_sample_list.samples.arg = ®istration_list_; /////////////////////////////////////////////// // decode it diff --git a/ecal/core/src/serialization/ecal_serialize_service.cpp b/ecal/core/src/serialization/ecal_serialize_service.cpp index 9bf6260d..08902c53 100644 --- a/ecal/core/src/serialization/ecal_serialize_service.cpp +++ b/ecal/core/src/serialization/ecal_serialize_service.cpp @@ -24,7 +24,7 @@ #include "nanopb/pb_encode.h" #include "nanopb/pb_decode.h" -#include "nanopb/ecal.pb.h" +#include "nanopb/ecal/core/pb/ecal.npb.h" #include "ecal_serialize_common.h" #include "ecal_serialize_service.h" diff --git a/ecal/core/src/serialization/ecal_struct_sample_common.h b/ecal/core/src/serialization/ecal_struct_sample_common.h index 3524f24d..9a78d612 100644 --- a/ecal/core/src/serialization/ecal_struct_sample_common.h +++ b/ecal/core/src/serialization/ecal_struct_sample_common.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. diff --git a/ecal/core/src/serialization/ecal_struct_sample_payload.h b/ecal/core/src/serialization/ecal_struct_sample_payload.h index 47b033ac..f9c14dc2 100644 --- a/ecal/core/src/serialization/ecal_struct_sample_payload.h +++ b/ecal/core/src/serialization/ecal_struct_sample_payload.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. @@ -37,11 +37,12 @@ namespace eCAL namespace Payload { // Topic information - struct Topic + struct TopicInfo { std::string hname; // host name std::string tid; // topic id std::string tname; // topic name + int32_t pid = 0; // process id }; // Topic content payload @@ -75,7 +76,7 @@ namespace eCAL struct Sample { eCmdType cmd_type = bct_none; // payload command type - Topic topic; // topic information + TopicInfo topic_info; // topic information Content content; // topic content std::vector padding; // padding to artificially increase the size of the message. This is a workaround for TCP topics, to get the actual user-payload 8-byte-aligned. REMOVE ME IN ECAL6 }; diff --git a/ecal/core/src/serialization/ecal_struct_sample_registration.h b/ecal/core/src/serialization/ecal_struct_sample_registration.h index 13d6c8d5..cc9d133a 100644 --- a/ecal/core/src/serialization/ecal_struct_sample_registration.h +++ b/ecal/core/src/serialization/ecal_struct_sample_registration.h @@ -28,12 +28,14 @@ #include "ecal_struct_service.h" #include +#include "util/expanding_vector.h" #include #include #include #include #include +#include namespace eCAL { @@ -69,6 +71,15 @@ namespace eCAL struct OSInfo { std::string osname; // name + + bool operator==(const OSInfo& other) const { + return osname == other.osname; + } + + void clear() + { + osname.clear(); + } }; // eCAL host @@ -76,6 +87,16 @@ 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; + } + + void clear() + { + hname.clear(); + os.clear(); + } }; // Process severity information @@ -84,23 +105,59 @@ 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; + } + + void clear() + { + severity = proc_sev_unknown; + severity_level = proc_sev_level_unknown; + info.clear(); + } }; // 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; + } + + void clear() + {} }; // 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; + } + + void clear() + { + port = 0; + } }; // Transport layer parameters for ecal shm struct LayerParShm { - std::list memory_file_list; // list of memory file names + Util::CExpandingVector memory_file_list; // list of memory file names + + bool operator==(const LayerParShm& other) const { + return memory_file_list == other.memory_file_list; + } + + void clear() + { + memory_file_list.clear(); + } }; // Connection parameter for reader/writer @@ -109,6 +166,19 @@ 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; + } + + void clear() + { + layer_par_udpmc.clear(); + layer_par_tcp.clear(); + layer_par_shm.clear(); + } }; // Transport layer information @@ -119,6 +189,23 @@ namespace eCAL 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; + } + + void clear() + { + type = tl_none; + version = 0; + enabled = false; + active = false; + par_layer.clear(); + } }; // Process information @@ -135,6 +222,35 @@ 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; + } + + void clear() + { + rclock = 0; + hgname.clear(); + pname.clear(); + uname.clear(); + pparam.clear(); + state.clear(); + tsync_state = tsync_none; + tsync_mod_name.clear(); + component_init_state = 0; + component_init_info.clear(); + ecal_runtime_version.clear(); + } }; // eCAL topic information @@ -148,7 +264,7 @@ namespace eCAL std::string direction; // direction (publisher, subscriber) SDataTypeInformation tdatatype; // topic datatype information (encoding & type & description) - std::vector tlayer; // active topic transport layers and its specific parameter + Util::CExpandingVector tlayer; // active topic transport layers and its specific parameter int32_t tsize = 0; // topic size int32_t connections_loc = 0; // number of local connected entities @@ -160,6 +276,49 @@ 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; + } + + void clear() + { + rclock = 0; + hgname.clear(); + pname.clear(); + uname.clear(); + tname.clear(); + direction.clear(); + tdatatype.clear(); + + tlayer.clear(); + tsize = 0; + + connections_loc = 0; + connections_ext = 0; + message_drops = 0; + + did = 0; + dclock = 0; + dfreq = 0; + + attr.clear(); + } }; struct SampleIdentifier @@ -167,6 +326,23 @@ namespace eCAL 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 + + // This is a hack that assumes the entity_id is unique within the whole ecal system (which it should be) + bool operator==(const SampleIdentifier& other) const { + return entity_id == other.entity_id; + } + + bool operator<(const SampleIdentifier& other) const + { + return entity_id < other.entity_id; + } + + void clear() + { + entity_id.clear(); + process_id = 0; + host_name.clear(); + } }; // Registration sample @@ -179,12 +355,30 @@ namespace eCAL 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; + } + + void clear() + { + identifier.clear(); + cmd_type = bct_none; + host.clear(); + process.clear(); + service.clear(); + client.clear(); + topic.clear(); + } }; // Registration sample list - struct SampleList - { - std::list samples; // list of Samples used currently by SHM registration - }; + using SampleList = Util::CExpandingVector; } } diff --git a/ecal/core/src/serialization/ecal_struct_service.h b/ecal/core/src/serialization/ecal_struct_service.h index 24123461..97572072 100644 --- a/ecal/core/src/serialization/ecal_struct_service.h +++ b/ecal/core/src/serialization/ecal_struct_service.h @@ -27,6 +27,9 @@ #include #include #include +#include + +#include namespace eCAL { @@ -50,6 +53,27 @@ 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; + } + + void clear() + { + hname.clear(); + sname.clear(); + sid.clear(); + mname.clear(); + error.clear(); + id = 0; + state = none; + } }; // Service Request @@ -57,6 +81,17 @@ 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; + } + + void clear() + { + header.clear(); + request.clear(); + } }; // Service Response @@ -65,6 +100,19 @@ 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; + } + + void clear() + { + header.clear(); + response.clear(); + ret_state = 0; + } }; // Service Method @@ -76,30 +124,91 @@ 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; + } + + void clear() + { + mname.clear(); + req_type.clear(); + req_desc.clear(); + resp_type.clear(); + resp_desc.clear(); + call_count = 0; + } }; // Service struct Service { - int32_t rclock = 0; // Registration clock - std::string pname; // Process name - std::string uname; // Unit name - std::string sname; // Service name - 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) + int32_t rclock = 0; // Registration clock + std::string pname; // Process name + std::string uname; // Unit name + std::string sname; // Service name + Util::CExpandingVector 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; + } + + void clear() + { + rclock = 0; + pname.clear(); + uname.clear(); + sname.clear(); + methods.clear(); + version = 0; + tcp_port_v0 = 0; + tcp_port_v1 = 0; + } }; // Client struct Client { - int32_t rclock = 0; // Registration clock - std::string pname; // Process name - std::string uname; // Unit name - std::string sname; // Service name - std::vector methods; // List of methods - uint32_t version = 0; // Client protocol version + int32_t rclock = 0; // Registration clock + std::string pname; // Process name + std::string uname; // Unit name + std::string sname; // Service name + Util::CExpandingVector 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; + } + + void clear() + { + rclock = 0; + pname.clear(); + uname.clear(); + sname.clear(); + methods.clear(); + version = 0; + } }; } } diff --git a/ecal/core/src/serialization/nanopb/ecal.pb.c b/ecal/core/src/serialization/nanopb/ecal/core/pb/ecal.npb.c similarity index 85% rename from ecal/core/src/serialization/nanopb/ecal.pb.c rename to ecal/core/src/serialization/nanopb/ecal/core/pb/ecal.npb.c index aaf947bc..cd260f71 100644 --- a/ecal/core/src/serialization/nanopb/ecal.pb.c +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/ecal.npb.c @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#include "ecal.pb.h" +#include "ecal.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif @@ -17,3 +17,4 @@ PB_BIND(eCAL_pb_SampleList, eCAL_pb_SampleList, AUTO) + diff --git a/ecal/core/src/serialization/nanopb/ecal.pb.h b/ecal/core/src/serialization/nanopb/ecal/core/pb/ecal.npb.h similarity index 96% rename from ecal/core/src/serialization/nanopb/ecal.pb.h rename to ecal/core/src/serialization/nanopb/ecal/core/pb/ecal.npb.h index 8711ae4c..d77987cb 100644 --- a/ecal/core/src/serialization/nanopb/ecal.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/ecal.npb.h @@ -1,13 +1,13 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#ifndef PB_ECAL_PB_ECAL_PB_H_INCLUDED -#define PB_ECAL_PB_ECAL_PB_H_INCLUDED +#ifndef PB_ECAL_PB_ECAL_NPB_H_INCLUDED +#define PB_ECAL_PB_ECAL_NPB_H_INCLUDED #include -#include "host.pb.h" -#include "process.pb.h" -#include "service.pb.h" -#include "topic.pb.h" +#include "ecal/core/pb/host.npb.h" +#include "ecal/core/pb/process.npb.h" +#include "ecal/core/pb/service.npb.h" +#include "ecal/core/pb/topic.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. diff --git a/ecal/core/src/serialization/nanopb/host.pb.c b/ecal/core/src/serialization/nanopb/ecal/core/pb/host.npb.c similarity index 83% rename from ecal/core/src/serialization/nanopb/host.pb.c rename to ecal/core/src/serialization/nanopb/ecal/core/pb/host.npb.c index e7720239..54b6cb5e 100644 --- a/ecal/core/src/serialization/nanopb/host.pb.c +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/host.npb.c @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#include "host.pb.h" +#include "host.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif diff --git a/ecal/core/src/serialization/nanopb/host.pb.h b/ecal/core/src/serialization/nanopb/ecal/core/pb/host.npb.h similarity index 95% rename from ecal/core/src/serialization/nanopb/host.pb.h rename to ecal/core/src/serialization/nanopb/ecal/core/pb/host.npb.h index 63d43d6f..adc7f502 100644 --- a/ecal/core/src/serialization/nanopb/host.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/host.npb.h @@ -1,8 +1,8 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#ifndef PB_ECAL_PB_HOST_PB_H_INCLUDED -#define PB_ECAL_PB_HOST_PB_H_INCLUDED +#ifndef PB_ECAL_PB_HOST_NPB_H_INCLUDED +#define PB_ECAL_PB_HOST_NPB_H_INCLUDED #include #if PB_PROTO_HEADER_VERSION != 40 diff --git a/ecal/core/src/serialization/nanopb/layer.pb.c b/ecal/core/src/serialization/nanopb/ecal/core/pb/layer.npb.c similarity index 89% rename from ecal/core/src/serialization/nanopb/layer.pb.c rename to ecal/core/src/serialization/nanopb/ecal/core/pb/layer.npb.c index 876144d5..aa8ba1cf 100644 --- a/ecal/core/src/serialization/nanopb/layer.pb.c +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/layer.npb.c @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#include "layer.pb.h" +#include "layer.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif @@ -23,3 +23,4 @@ PB_BIND(eCAL_pb_TLayer, eCAL_pb_TLayer, AUTO) + diff --git a/ecal/core/src/serialization/nanopb/layer.pb.h b/ecal/core/src/serialization/nanopb/ecal/core/pb/layer.npb.h similarity index 97% rename from ecal/core/src/serialization/nanopb/layer.pb.h rename to ecal/core/src/serialization/nanopb/ecal/core/pb/layer.npb.h index 64f39f35..56e6ddac 100644 --- a/ecal/core/src/serialization/nanopb/layer.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/layer.npb.h @@ -1,8 +1,8 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#ifndef PB_ECAL_PB_LAYER_PB_H_INCLUDED -#define PB_ECAL_PB_LAYER_PB_H_INCLUDED +#ifndef PB_ECAL_PB_LAYER_NPB_H_INCLUDED +#define PB_ECAL_PB_LAYER_NPB_H_INCLUDED #include #if PB_PROTO_HEADER_VERSION != 40 @@ -150,7 +150,7 @@ extern const pb_msgdesc_t eCAL_pb_TLayer_msg; /* eCAL_pb_LayerParShm_size depends on runtime parameters */ /* eCAL_pb_ConnnectionPar_size depends on runtime parameters */ /* eCAL_pb_TLayer_size depends on runtime parameters */ -#define ECAL_PB_LAYER_PB_H_MAX_SIZE eCAL_pb_LayerParTcp_size +#define ECAL_PB_LAYER_NPB_H_MAX_SIZE eCAL_pb_LayerParTcp_size #define eCAL_pb_LayerParTcp_size 11 #define eCAL_pb_LayerParUdpMC_size 0 diff --git a/ecal/core/src/serialization/nanopb/logging.pb.c b/ecal/core/src/serialization/nanopb/ecal/core/pb/logging.npb.c similarity index 83% rename from ecal/core/src/serialization/nanopb/logging.pb.c rename to ecal/core/src/serialization/nanopb/ecal/core/pb/logging.npb.c index aba64cb7..a00a3e9e 100644 --- a/ecal/core/src/serialization/nanopb/logging.pb.c +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/logging.npb.c @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#include "logging.pb.h" +#include "logging.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif diff --git a/ecal/core/src/serialization/nanopb/logging.pb.h b/ecal/core/src/serialization/nanopb/ecal/core/pb/logging.npb.h similarity index 96% rename from ecal/core/src/serialization/nanopb/logging.pb.h rename to ecal/core/src/serialization/nanopb/ecal/core/pb/logging.npb.h index 696fa029..1a71f1f1 100644 --- a/ecal/core/src/serialization/nanopb/logging.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/logging.npb.h @@ -1,8 +1,8 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#ifndef PB_ECAL_PB_LOGGING_PB_H_INCLUDED -#define PB_ECAL_PB_LOGGING_PB_H_INCLUDED +#ifndef PB_ECAL_PB_LOGGING_NPB_H_INCLUDED +#define PB_ECAL_PB_LOGGING_NPB_H_INCLUDED #include #if PB_PROTO_HEADER_VERSION != 40 diff --git a/ecal/core/src/serialization/nanopb/monitoring.pb.c b/ecal/core/src/serialization/nanopb/ecal/core/pb/monitoring.npb.c similarity index 79% rename from ecal/core/src/serialization/nanopb/monitoring.pb.c rename to ecal/core/src/serialization/nanopb/ecal/core/pb/monitoring.npb.c index e512c68e..f1332cba 100644 --- a/ecal/core/src/serialization/nanopb/monitoring.pb.c +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/monitoring.npb.c @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#include "monitoring.pb.h" +#include "monitoring.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif diff --git a/ecal/core/src/serialization/nanopb/monitoring.pb.h b/ecal/core/src/serialization/nanopb/ecal/core/pb/monitoring.npb.h similarity index 89% rename from ecal/core/src/serialization/nanopb/monitoring.pb.h rename to ecal/core/src/serialization/nanopb/ecal/core/pb/monitoring.npb.h index e6cc1dbc..24f53596 100644 --- a/ecal/core/src/serialization/nanopb/monitoring.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/monitoring.npb.h @@ -1,13 +1,13 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#ifndef PB_ECAL_PB_MONITORING_PB_H_INCLUDED -#define PB_ECAL_PB_MONITORING_PB_H_INCLUDED +#ifndef PB_ECAL_PB_MONITORING_NPB_H_INCLUDED +#define PB_ECAL_PB_MONITORING_NPB_H_INCLUDED #include -#include "host.pb.h" -#include "process.pb.h" -#include "service.pb.h" -#include "topic.pb.h" +#include "ecal/core/pb/host.npb.h" +#include "ecal/core/pb/process.npb.h" +#include "ecal/core/pb/service.npb.h" +#include "ecal/core/pb/topic.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. diff --git a/ecal/core/src/serialization/nanopb/process.pb.c b/ecal/core/src/serialization/nanopb/ecal/core/pb/process.npb.c similarity index 82% rename from ecal/core/src/serialization/nanopb/process.pb.c rename to ecal/core/src/serialization/nanopb/ecal/core/pb/process.npb.c index 60dfc2db..adecbca8 100644 --- a/ecal/core/src/serialization/nanopb/process.pb.c +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/process.npb.c @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#include "process.pb.h" +#include "process.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif @@ -16,3 +16,6 @@ PB_BIND(eCAL_pb_Process, eCAL_pb_Process, AUTO) + + + diff --git a/ecal/core/src/serialization/nanopb/process.pb.h b/ecal/core/src/serialization/nanopb/ecal/core/pb/process.npb.h similarity index 98% rename from ecal/core/src/serialization/nanopb/process.pb.h rename to ecal/core/src/serialization/nanopb/ecal/core/pb/process.npb.h index 592f0b5f..7139ac49 100644 --- a/ecal/core/src/serialization/nanopb/process.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/process.npb.h @@ -1,8 +1,8 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#ifndef PB_ECAL_PB_PROCESS_PB_H_INCLUDED -#define PB_ECAL_PB_PROCESS_PB_H_INCLUDED +#ifndef PB_ECAL_PB_PROCESS_NPB_H_INCLUDED +#define PB_ECAL_PB_PROCESS_NPB_H_INCLUDED #include #if PB_PROTO_HEADER_VERSION != 40 diff --git a/ecal/core/src/serialization/nanopb/service.pb.c b/ecal/core/src/serialization/nanopb/ecal/core/pb/service.npb.c similarity index 89% rename from ecal/core/src/serialization/nanopb/service.pb.c rename to ecal/core/src/serialization/nanopb/ecal/core/pb/service.npb.c index 9a2b5339..77181a02 100644 --- a/ecal/core/src/serialization/nanopb/service.pb.c +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/service.npb.c @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#include "service.pb.h" +#include "service.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif @@ -26,3 +26,4 @@ PB_BIND(eCAL_pb_Client, eCAL_pb_Client, AUTO) + diff --git a/ecal/core/src/serialization/nanopb/service.pb.h b/ecal/core/src/serialization/nanopb/ecal/core/pb/service.npb.h similarity index 98% rename from ecal/core/src/serialization/nanopb/service.pb.h rename to ecal/core/src/serialization/nanopb/ecal/core/pb/service.npb.h index 611f224e..f60c1b2c 100644 --- a/ecal/core/src/serialization/nanopb/service.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/service.npb.h @@ -1,8 +1,8 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#ifndef PB_ECAL_PB_SERVICE_PB_H_INCLUDED -#define PB_ECAL_PB_SERVICE_PB_H_INCLUDED +#ifndef PB_ECAL_PB_SERVICE_NPB_H_INCLUDED +#define PB_ECAL_PB_SERVICE_NPB_H_INCLUDED #include #if PB_PROTO_HEADER_VERSION != 40 diff --git a/ecal/core/src/serialization/nanopb/topic.pb.c b/ecal/core/src/serialization/nanopb/ecal/core/pb/topic.npb.c similarity index 86% rename from ecal/core/src/serialization/nanopb/topic.pb.c rename to ecal/core/src/serialization/nanopb/ecal/core/pb/topic.npb.c index 7b8316dd..85f7d197 100644 --- a/ecal/core/src/serialization/nanopb/topic.pb.c +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/topic.npb.c @@ -1,7 +1,7 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#include "topic.pb.h" +#include "topic.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif diff --git a/ecal/core/src/serialization/nanopb/topic.pb.h b/ecal/core/src/serialization/nanopb/ecal/core/pb/topic.npb.h similarity index 97% rename from ecal/core/src/serialization/nanopb/topic.pb.h rename to ecal/core/src/serialization/nanopb/ecal/core/pb/topic.npb.h index c7eb1ffc..97993e98 100644 --- a/ecal/core/src/serialization/nanopb/topic.pb.h +++ b/ecal/core/src/serialization/nanopb/ecal/core/pb/topic.npb.h @@ -1,10 +1,10 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.8 */ +/* Generated by nanopb-0.4.9 */ -#ifndef PB_ECAL_PB_TOPIC_PB_H_INCLUDED -#define PB_ECAL_PB_TOPIC_PB_H_INCLUDED +#ifndef PB_ECAL_PB_TOPIC_NPB_H_INCLUDED +#define PB_ECAL_PB_TOPIC_NPB_H_INCLUDED #include -#include "layer.pb.h" +#include "ecal/core/pb/layer.npb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. diff --git a/ecal/core/src/serialization/nanopb/nanopb/pb.h b/ecal/core/src/serialization/nanopb/pb.h similarity index 99% rename from ecal/core/src/serialization/nanopb/nanopb/pb.h rename to ecal/core/src/serialization/nanopb/pb.h index c7a055f0..1bff70e4 100644 --- a/ecal/core/src/serialization/nanopb/nanopb/pb.h +++ b/ecal/core/src/serialization/nanopb/pb.h @@ -65,7 +65,7 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION "nanopb-0.4.8" +#define NANOPB_VERSION "nanopb-0.4.9" /* Include all the system headers needed by nanopb. You will need the * definitions of the following: @@ -215,12 +215,23 @@ PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) #endif #endif +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + /* List of possible field types. These are used in the autogenerated code. * Least-significant 4 bits tell the scalar type * Most-significant 4 bits specify repeated/required/packed etc. */ - -typedef uint_least8_t pb_type_t; +typedef pb_byte_t pb_type_t; /**** Field data types ****/ @@ -301,12 +312,6 @@ typedef uint_least8_t pb_type_t; #endif #define PB_SIZE_MAX ((pb_size_t)-1) -/* Data type for storing encoded data and other byte streams. - * This typedef exists to support platforms where uint8_t does not exist. - * You can regard it as equivalent on uint8_t on other platforms. - */ -typedef uint_least8_t pb_byte_t; - /* Forward declaration of struct types */ typedef struct pb_istream_s pb_istream_t; typedef struct pb_ostream_s pb_ostream_t; diff --git a/ecal/core/src/serialization/nanopb/nanopb/pb_common.c b/ecal/core/src/serialization/nanopb/pb_common.c similarity index 100% rename from ecal/core/src/serialization/nanopb/nanopb/pb_common.c rename to ecal/core/src/serialization/nanopb/pb_common.c diff --git a/ecal/core/src/serialization/nanopb/nanopb/pb_common.h b/ecal/core/src/serialization/nanopb/pb_common.h similarity index 100% rename from ecal/core/src/serialization/nanopb/nanopb/pb_common.h rename to ecal/core/src/serialization/nanopb/pb_common.h diff --git a/ecal/core/src/serialization/nanopb/nanopb/pb_decode.c b/ecal/core/src/serialization/nanopb/pb_decode.c similarity index 99% rename from ecal/core/src/serialization/nanopb/nanopb/pb_decode.c rename to ecal/core/src/serialization/nanopb/pb_decode.c index 788998eb..068306a0 100644 --- a/ecal/core/src/serialization/nanopb/nanopb/pb_decode.c +++ b/ecal/core/src/serialization/nanopb/pb_decode.c @@ -4,13 +4,14 @@ */ /* Use the GCC warn_unused_result attribute to check that all return values - * are propagated correctly. On other compilers and gcc before 3.4.0 just - * ignore the annotation. + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. */ -#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) - #define checkreturn -#else +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn #endif #include "pb.h" diff --git a/ecal/core/src/serialization/nanopb/nanopb/pb_decode.h b/ecal/core/src/serialization/nanopb/pb_decode.h similarity index 92% rename from ecal/core/src/serialization/nanopb/nanopb/pb_decode.h rename to ecal/core/src/serialization/nanopb/pb_decode.h index ae1d3ccf..3f392b29 100644 --- a/ecal/core/src/serialization/nanopb/nanopb/pb_decode.h +++ b/ecal/core/src/serialization/nanopb/pb_decode.h @@ -37,10 +37,21 @@ struct pb_istream_s bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); #endif - void *state; /* Free field for use by callback implementation */ + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ size_t bytes_left; #ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ const char *errmsg; #endif }; diff --git a/ecal/core/src/serialization/nanopb/nanopb/pb_encode.c b/ecal/core/src/serialization/nanopb/pb_encode.c similarity index 99% rename from ecal/core/src/serialization/nanopb/nanopb/pb_encode.c rename to ecal/core/src/serialization/nanopb/pb_encode.c index 7f562012..f9034a54 100644 --- a/ecal/core/src/serialization/nanopb/nanopb/pb_encode.c +++ b/ecal/core/src/serialization/nanopb/pb_encode.c @@ -8,13 +8,14 @@ #include "pb_common.h" /* Use the GCC warn_unused_result attribute to check that all return values - * are propagated correctly. On other compilers and gcc before 3.4.0 just - * ignore the annotation. + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. */ -#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) - #define checkreturn -#else +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn #endif /************************************** diff --git a/ecal/core/src/serialization/nanopb/nanopb/pb_encode.h b/ecal/core/src/serialization/nanopb/pb_encode.h similarity index 93% rename from ecal/core/src/serialization/nanopb/nanopb/pb_encode.h rename to ecal/core/src/serialization/nanopb/pb_encode.h index 89136832..6dc089da 100644 --- a/ecal/core/src/serialization/nanopb/nanopb/pb_encode.h +++ b/ecal/core/src/serialization/nanopb/pb_encode.h @@ -37,11 +37,21 @@ struct pb_ostream_s #else bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); #endif - void *state; /* Free field for use by callback implementation. */ - size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */ - size_t bytes_written; /* Number of bytes written so far. */ + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; #ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ const char *errmsg; #endif }; diff --git a/ecal/core/src/service/ecal_clientgate.cpp b/ecal/core/src/service/ecal_clientgate.cpp index f72ca78c..9f76498d 100644 --- a/ecal/core/src/service/ecal_clientgate.cpp +++ b/ecal/core/src/service/ecal_clientgate.cpp @@ -165,7 +165,7 @@ namespace eCAL std::shared_lock const lock(m_client_set_sync); for (const auto& service_client_impl : m_client_set) { - reg_sample_list_.samples.emplace_back(service_client_impl->GetRegistration()); + reg_sample_list_.push_back(service_client_impl->GetRegistration()); } } } diff --git a/ecal/core/src/service/ecal_servicegate.cpp b/ecal/core/src/service/ecal_servicegate.cpp index 1b275a5d..8967b684 100644 --- a/ecal/core/src/service/ecal_servicegate.cpp +++ b/ecal/core/src/service/ecal_servicegate.cpp @@ -102,7 +102,7 @@ namespace eCAL std::shared_lock const lock(m_service_set_sync); for (const auto& service_server_impl : m_service_set) { - reg_sample_list_.samples.emplace_back(service_server_impl->GetRegistration()); + reg_sample_list_.push_back(service_server_impl->GetRegistration()); } } } diff --git a/ecal/core/src/types/ecal_custom_data_types.cpp b/ecal/core/src/types/ecal_custom_data_types.cpp index ac2c8819..e6fe22c5 100644 --- a/ecal/core/src/types/ecal_custom_data_types.cpp +++ b/ecal/core/src/types/ecal_custom_data_types.cpp @@ -76,17 +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& IpAddressV4::operator=(const char* 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_; }; + 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/util/ecal_expmap.h b/ecal/core/src/util/ecal_expmap.h index fd71dcfc..bca21bf2 100644 --- a/ecal/core/src/util/ecal_expmap.h +++ b/ecal/core/src/util/ecal_expmap.h @@ -259,7 +259,7 @@ namespace eCAL // We do have it: // Update access record by moving // accessed key to back of list - update_timestamp(k); + update_timestamp(it); } // Return the retrieved value @@ -300,18 +300,22 @@ namespace eCAL * 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. */ - void erase_expired(std::list* keys_erased_from_expired_map = nullptr) //-V826 + std::map erase_expired() { + 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 (keys_erased_from_expired_map != nullptr) keys_erased_from_expired_map->push_back(it->corresponding_map_key); + 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 @@ -337,19 +341,15 @@ namespace eCAL private: // Maybe pass the iterator instead of the key? or at least only get k once - void update_timestamp(const Key& k) + void update_timestamp(const typename InternalMapType::iterator& it_in_map) { - auto it_in_map = _internal_map.find(k); - if (it_in_map != _internal_map.end()) - { - auto& it_in_list = it_in_map->second.timestamp_list_iterator; + auto& it_in_list = it_in_map->second.timestamp_list_iterator; - // move the element to the end of the list - _access_timestamps_list.splice(_access_timestamps_list.end(), _access_timestamps_list, it_in_list); + // move the element to the end of the list + _access_timestamps_list.splice(_access_timestamps_list.end(), _access_timestamps_list, it_in_list); - // update the timestamp - it_in_list->timestamp = get_curr_time(); - } + // update the timestamp + it_in_list->timestamp = get_curr_time(); } // Record a fresh key-value pair in the cache diff --git a/ecal/core/src/util/expanding_vector.h b/ecal/core/src/util/expanding_vector.h new file mode 100644 index 00000000..e0077917 --- /dev/null +++ b/ecal/core/src/util/expanding_vector.h @@ -0,0 +1,232 @@ +/* ========================= 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 std::vector wrapper, that never deallocates element, just clears them +**/ + +#pragma once + +#include +#include +#include + +namespace eCAL +{ + namespace Util + { + /** + * @brief A vector that never destructs its elements, but only calls clear on them. + * + * @tparam T The type of the values. Must provide a `.clear()` function + * + * 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::vector. + * However, when calling clear(), a regular vector will destroy the elements which are stored in this vector. + * This class, will instead call the `clear()` functions on all members. + */ + + // Templated class CExpandingVector + template + class CExpandingVector { + public: + using value_type = typename std::vector::value_type; + using allocator_type = typename std::vector::allocator_type; + using size_type = typename std::vector::size_type; + using difference_type = typename std::vector::difference_type; + using reference = typename std::vector::reference; + using const_reference = typename std::vector::const_reference; + using pointer = typename std::vector::pointer; + using const_pointer = typename std::vector::const_pointer; + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::const_iterator; + using reverse_iterator = typename std::vector::reverse_iterator; + using const_reverse_iterator = typename std::vector::const_reverse_iterator; + + private: + std::vector data; + size_t internal_size{ 0 }; // Track size separately + + public: + // Access to the internal size + size_t size() const { + return internal_size; + } + + // Clear the content but not the underlying vector + void clear() { + for (auto& elem : data) { + elem.clear(); // Call the clear() function on individual elements + } + // We don't modify the size of the underlying vector but keep the internal size consistent. + internal_size = 0; + } + + // Add new element to the vector + void push_back(const T& value) { + if (internal_size < data.size()) { + data[internal_size] = value; // Reuse space + } else { + data.push_back(value); // Expand the vector if needed + } + ++internal_size; + } + + // Push back (rvalue reference version for move semantics) + void push_back(T&& value) { + if (internal_size < data.size()) { + data[internal_size] = std::move(value); // Reuse space + } + else { + data.push_back(std::move(value)); // Move into the vector + } + ++internal_size; + } + + T& push_back() + { + if (internal_size == data.size()) { + data.push_back(T{}); // Expand the vector if needed, else do nothing + } + ++internal_size; + + return back(); + } + + // Access the first element + T& front() { + if (internal_size == 0) { + throw std::out_of_range("CExpandingVector is empty"); + } + return data.front(); // Return the first element + } + + const T& front() const { + if (internal_size == 0) { + throw std::out_of_range("CExpandingVector is empty"); + } + return data.front(); // Return the first element + } + + // Access the last element + T& back() { + if (internal_size == 0) { + throw std::out_of_range("CExpandingVector is empty"); + } + return data[internal_size - 1]; // Return the last element + } + + const T& back() const { + if (internal_size == 0) { + throw std::out_of_range("CExpandingVector is empty"); + } + return data[internal_size - 1]; // Return the last element + } + + + // Resize the vector + void resize(size_t new_size) { + if (new_size > data.size()) { + data.resize(new_size); + } + internal_size = new_size; + } + + // Access element with bounds checking + T& at(size_t index) { + if (index >= internal_size) { + throw std::out_of_range("Index out of bounds"); + } + return data.at(index); + } + + // Const access element with bounds checking + const T& at(size_t index) const { + if (index >= internal_size) { + throw std::out_of_range("Index out of bounds"); + } + return data.at(index); + } + + // Iterator functions + iterator begin() { + return data.begin(); + } + + const_iterator begin() const { + return data.begin(); + } + + iterator end() { + return data.begin() + internal_size; + } + + const_iterator end() const { + return data.begin() + internal_size; + } + + // Access the underlying capacity + size_t capacity() const { + return data.capacity(); + } + + // Operator[] overload + T& operator[](size_t index) { + return data[index]; + } + + const T& operator[](size_t index) const { + return data[index]; + } + + // Check if vector is empty + bool empty() const { + return internal_size == 0; + } + + // Get underlying vector's full size (not just the internal size) + size_t full_size() const { + return data.size(); + } + + // Equality operator (compares size and elements) + bool operator==(const CExpandingVector& other) const { + // Check if sizes are equal + if (internal_size != other.internal_size) { + return false; + } + + // Compare elements + for (size_t i = 0; i < internal_size; ++i) { + if (!(data[i] == other.data[i])) { + return false; + } + } + + return true; // Sizes are equal and all elements are the same + } + + bool operator!=(const CExpandingVector& other) const { + return !(*this == other); // Use the equality operator to implement inequality + } + }; + + } +} diff --git a/ecal/msg/protobuf/src/ecal_proto_decoder.cpp b/ecal/msg/protobuf/src/ecal_proto_decoder.cpp index 2cc663d5..d9053cf3 100644 --- a/ecal/msg/protobuf/src/ecal_proto_decoder.cpp +++ b/ecal/msg/protobuf/src/ecal_proto_decoder.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -270,10 +271,9 @@ namespace protobuf // do not process default messages to avoid infinite recursions. std::vector msg_fields; msg.GetReflection()->ListFields(msg, &msg_fields); - if (msg_fields.size() > 0) - { + + if (prefix_.find(field->name()) == std::string::npos || !msg_fields.empty()) ProcProtoMsg(msg, name.str(), complete_message_name, true, fnum); - } } } } @@ -284,10 +284,9 @@ namespace protobuf // do not process default messages to avoid infinite recursions. std::vector msg_fields; msg.GetReflection()->ListFields(msg, &msg_fields); - if (msg_fields.size() > 0) - { + + if (prefix_.find(field->name()) == std::string::npos || !msg_fields.empty()) ProcProtoMsg(msg, field->name(), complete_message_name, false, field->number()); - } } } break; diff --git a/ecal/samples/cpp/benchmarks/many_connections_rec/src/many_connections_rec.cpp b/ecal/samples/cpp/benchmarks/many_connections_rec/src/many_connections_rec.cpp index 6f6682a5..188badb2 100644 --- a/ecal/samples/cpp/benchmarks/many_connections_rec/src/many_connections_rec.cpp +++ b/ecal/samples/cpp/benchmarks/many_connections_rec/src/many_connections_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. @@ -39,7 +39,11 @@ class SubscriberCreator std::ostringstream tname; tname << std::setw(5) << std::setfill('0') << i; subscribers.emplace_back("Topic" + tname.str(), eCAL::SDataTypeInformation{ ttype, "", tdesc }); - subscribers.at(i).AddReceiveCallback(std::bind(&SubscriberCreator::Receive, this)); + auto on_receive = [this](const eCAL::Registration::STopicId&, const eCAL::SDataTypeInformation&, const eCAL::SReceiveCallbackData&) + { + Receive(); + }; + subscribers.at(i).AddReceiveCallback(on_receive); } } 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 index 7cf864a4..dc2b46e6 100644 --- 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 @@ -32,7 +32,7 @@ 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 (120); +const int final_sleep_sec (5); std::string GenerateSizedString(const std::string& name, size_t totalSize) { @@ -56,10 +56,38 @@ std::string GenerateSizedString(const std::string& name, size_t totalSize) int main(int argc, char** argv) { - // initialize eCAL API - eCAL::Initialize(argc, argv, "massive_pub_sub"); + // set eCAL configuration + eCAL::Configuration configuration; + configuration.registration.registration_timeout = 10000; // registration timeout == 10 sec + configuration.registration.layer.shm.enable = true; // switch shm registration on and + configuration.registration.layer.udp.enable = false; // switch udp registration off - eCAL::Util::EnableLoopback(true); + // initialize eCAL API + eCAL::Initialize(configuration, "massive_pub_sub"); + + // publisher registration event callback + size_t created_publisher_num(0); + size_t deleted_publisher_num(0); + std::set created_publisher_ids; + std::set deleted_publisher_ids; + eCAL::Registration::AddPublisherEventCallback( + [&](const eCAL::Registration::STopicId& id_, eCAL::Registration::RegistrationEventType event_type_) + { + switch (event_type_) + { + case eCAL::Registration::RegistrationEventType::new_entity: + created_publisher_num++; + created_publisher_ids.insert(id_); + //std::cout << "Publisher created" << std::endl; + break; + case eCAL::Registration::RegistrationEventType::deleted_entity: + deleted_publisher_num++; + deleted_publisher_ids.insert(id_); + //std::cout << "Publisher deleted" << std::endl; + break; + } + } + ); // create subscriber std::vector vector_of_subscriber; @@ -82,7 +110,7 @@ int main(int argc, char** argv) // 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::cout << "Time taken for subscriber creation: " << duration << " milliseconds" << std::endl << std::endl; } // sleep for a few seconds @@ -114,11 +142,85 @@ int main(int argc, char** argv) // 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::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; + } + + // check creation events + const std::set publisher_ids = eCAL::Registration::GetPublisherIDs(); + std::cout << "Number of publisher creation events " << created_publisher_num << std::endl; + std::cout << "Size of publisher creation id set " << created_publisher_ids.size() << std::endl; + //std::cout << "Publisher creation id sets are equal " << (publisher_ids == created_publisher_ids) << std::endl; + std::cout << std::endl; + + // delete all publisher + std::cout << "Delete all publisher .." << std::endl; + vector_of_publisher.clear(); + std::cout << "Deletion done." << std::endl; std::cout << std::endl; // sleep for a few seconds + std::this_thread::sleep_for(std::chrono::seconds(in_between_sleep_sec)); + + // check deletion events + std::cout << "Number of publisher deletion events " << deleted_publisher_num << std::endl; + std::cout << "Size of publisher deletion id set " << deleted_publisher_ids.size() << std::endl; + //std::cout << "Publisher deleteion id sets are equal " << (publisher_ids == deleted_publisher_ids) << std::endl; + + // sleep final seconds std::this_thread::sleep_for(std::chrono::seconds(final_sleep_sec)); // finalize eCAL API 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 fa726089..0d52b417 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 08eea271..e1d1fb0a 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_json/CMakeLists.txt b/ecal/samples/cpp/monitoring/monitoring_rec_json/CMakeLists.txt new file mode 100644 index 00000000..165cc510 --- /dev/null +++ b/ecal/samples/cpp/monitoring/monitoring_rec_json/CMakeLists.txt @@ -0,0 +1,44 @@ +# ========================= 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 ================================= + +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) + +project(monitoring_rec_json) + +find_package(eCAL REQUIRED) +find_package(Protobuf REQUIRED) + +set(monitoring_rec_json_src + src/monitoring_rec_json.cpp +) + +ecal_add_sample(${PROJECT_NAME} ${monitoring_rec_json_src}) + +target_link_libraries(${PROJECT_NAME} + eCAL::core + eCAL::core_pb + protobuf::libprotobuf +) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +ecal_install_sample(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER samples/cpp/monitoring) diff --git a/ecal/samples/cpp/monitoring/monitoring_rec_json/src/monitoring_rec_json.cpp b/ecal/samples/cpp/monitoring/monitoring_rec_json/src/monitoring_rec_json.cpp new file mode 100644 index 00000000..580805a1 --- /dev/null +++ b/ecal/samples/cpp/monitoring/monitoring_rec_json/src/monitoring_rec_json.cpp @@ -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 ================================= +*/ + +#include +#include + +int main(int argc, char** argv) +{ + // initialize eCAL core API + eCAL::Initialize(argc, argv, "monitoring", eCAL::Init::All); + + // monitor for ever + while (eCAL::Ok()) + { + // take a snapshot as json string + std::string monitoring_s; + eCAL::Monitoring::GetMonitoringJSON(monitoring_s, eCAL::Monitoring::Entity::All); + + // log it + std::cout << monitoring_s; + + // sleep few milliseconds + eCAL::Process::SleepMS(1000); + } + + // finalize eCAL API + eCAL::Finalize(); + + return(0); +} diff --git a/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp b/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp index 8863f649..c5ba31e7 100644 --- a/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp +++ b/ecal/samples/cpp/pubsub/protobuf/proto_dyn_rec/src/proto_dyn_rec.cpp @@ -243,10 +243,9 @@ void ProcProtoMsg(const google::protobuf::Message& msg_, const std::string& pref // do not process default messages to avoid infinite recursions. std::vector msg_fields; msg.GetReflection()->ListFields(msg, &msg_fields); - if (msg_fields.size() > 0) - { + + if (prefix_.find(field->name()) == std::string::npos || !msg_fields.empty()) ProcProtoMsg(msg, prefix); - } } } else @@ -258,10 +257,9 @@ void ProcProtoMsg(const google::protobuf::Message& msg_, const std::string& pref // do not process default messages to avoid infinite recursions. std::vector msg_fields; msg.GetReflection()->ListFields(msg, &msg_fields); - if (msg_fields.size() > 0) - { + + if (prefix_.find(field->name()) == std::string::npos || !msg_fields.empty()) ProcProtoMsg(msg, prefix); - } } } break; 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 68dec7a2..7b5f78a5 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 @@ -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 d598e1da..31412021 100644 --- a/ecal/tests/CMakeLists.txt +++ b/ecal/tests/CMakeLists.txt @@ -32,14 +32,19 @@ 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/logging_test) +add_subdirectory(cpp/monitoring_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/config_test/src/config_test.cpp b/ecal/tests/cpp/config_test/src/config_test.cpp index 920d122b..a245f564 100644 --- a/ecal/tests/cpp/config_test/src/config_test.cpp +++ b/ecal/tests/cpp/config_test/src/config_test.cpp @@ -53,7 +53,6 @@ TEST(core_cpp_config /*unused*/, user_config_passing /*unused*/) 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; @@ -67,7 +66,6 @@ TEST(core_cpp_config /*unused*/, user_config_passing /*unused*/) custom_config.transport_layer.udp.network.group = ip_address; custom_config.transport_layer.udp.send_buffer = upd_snd_buff; - custom_config.monitoring.timeout = mon_timeout; custom_config.monitoring.filter_excl = mon_filter_excl; custom_config.logging.sinks.console.filter_log_con = mon_log_filter_con; @@ -94,9 +92,6 @@ TEST(core_cpp_config /*unused*/, user_config_passing /*unused*/) // Test UDP send buffer assignment, default is 5242880 EXPECT_EQ(upd_snd_buff, eCAL::GetConfiguration().transport_layer.udp.send_buffer); - // Test monitoring timeout assignment, default is 5000U - EXPECT_EQ(mon_timeout, eCAL::GetConfiguration().monitoring.timeout); - // Test monitoring filter exclude assignment, default is "_.*" EXPECT_EQ(mon_filter_excl, eCAL::GetConfiguration().monitoring.filter_excl); @@ -106,7 +101,7 @@ TEST(core_cpp_config /*unused*/, user_config_passing /*unused*/) // Test publisher sendmode assignment, default is eCAL::TLayer::eSendMode::smode_auto EXPECT_EQ(pub_use_shm, eCAL::GetConfiguration().publisher.layer.shm.enable); - // Test registration option assignment, default timeout is 60000U and default refresh is 1000U + // 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); @@ -114,7 +109,7 @@ TEST(core_cpp_config /*unused*/, user_config_passing /*unused*/) EXPECT_EQ(0, eCAL::Finalize()); } -TEST(ConfigDeathTest /*unused*/, user_config_death_test /*unused*/) +TEST(core_cpp_config /*unused*/, user_config_death_test /*unused*/) { eCAL::Configuration custom_config(0, nullptr); @@ -203,7 +198,7 @@ TEST(core_cpp_config /*unused*/, config_custom_datatypes_tests /*unused*/) EXPECT_EQ(config1.transport_layer.udp.network.group, testValue); } -TEST(CmdParserTest /*unused*/, config_cmd_parser_test /*unused*/) +TEST(core_cpp_config /*unused*/, config_cmd_parser_test /*unused*/) { const std::string some_file_name = "someFileName.yml"; @@ -227,7 +222,7 @@ TEST(CmdParserTest /*unused*/, config_cmd_parser_test /*unused*/) } #ifdef ECAL_CORE_CONFIGURATION -TEST(YamlConfigReaderTest /*unused*/, read_write_file_test /*unused*/) +TEST(core_cpp_config /*unused*/, read_write_file_test /*unused*/) { // create a custom ini file std::string ini_file_name = "customIni.yml"; @@ -254,7 +249,7 @@ TEST(YamlConfigReaderTest /*unused*/, read_write_file_test /*unused*/) remove("myTest.yml"); } -TEST(YamlConfigReaderTest /*unused*/, parse_values_test /*unused*/) +TEST(core_cpp_config /*unused*/, parse_values_test /*unused*/) { eCAL::Configuration config{}; EXPECT_NO_THROW(eCAL::Config::YamlStringToConfig(ini_file_as_string_yaml, config)); @@ -278,7 +273,7 @@ TEST(YamlConfigReaderTest /*unused*/, parse_values_test /*unused*/) EXPECT_EQ(config.publisher.layer.shm.acknowledge_timeout_ms, 346U); } -TEST(YamlConfigReaderTest /*unused*/, yaml_node_merger /*unused*/) +TEST(core_cpp_config /*unused*/, yaml_node_merger /*unused*/) { YAML::Node node_1{}; YAML::Node node_2{}; @@ -304,7 +299,7 @@ TEST(YamlConfigReaderTest /*unused*/, yaml_node_merger /*unused*/) EXPECT_EQ(node_1["firstLayer2"]["secondLayer2"], node_2["firstLayer2"]["secondLayer2"]); } -TEST(YamlConfigReaderTest /*unused*/, yaml_to_config_merger /*unused*/) +TEST(core_cpp_config /*unused*/, yaml_to_config_merger /*unused*/) { // create a custom ini file std::string ini_file_name = "customIni.yml"; diff --git a/ecal/tests/cpp/descgate_test/CMakeLists.txt b/ecal/tests/cpp/descgate_test/CMakeLists.txt index 284c2246..618604f2 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 56% rename from ecal/tests/cpp/descgate_test/src/getpublisher.cpp rename to ecal/tests/cpp/descgate_test/src/descgate_getentities.cpp index 4d696477..c17bf5ec 100644 --- a/ecal/tests/cpp/descgate_test/src/getpublisher.cpp +++ b/ecal/tests/cpp/descgate_test/src/descgate_getentities.cpp @@ -40,6 +40,13 @@ namespace 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; @@ -52,6 +59,13 @@ namespace 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; @@ -65,6 +79,13 @@ 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; @@ -77,32 +98,37 @@ namespace 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 3a7f57aa..4d6a6b32 100644 --- a/ecal/tests/cpp/expmap_test/src/expmap_test.cpp +++ b/ecal/tests/cpp/expmap_test/src/expmap_test.cpp @@ -104,19 +104,69 @@ TEST(core_cpp_core, ExpMap_SetGet) TestingClock::increment_time(std::chrono::milliseconds(150)); expmap["B"] = 2; expmap["C"] = 3; - expmap.erase_expired(); - 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.erase_expired(); - 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.erase_expired(); - 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)); diff --git a/ecal/tests/cpp/logging_test/CMakeLists.txt b/ecal/tests/cpp/logging_test/CMakeLists.txt new file mode 100644 index 00000000..8676f96a --- /dev/null +++ b/ecal/tests/cpp/logging_test/CMakeLists.txt @@ -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 ================================= + +project(test_logging) + +find_package(Threads REQUIRED) +find_package(GTest REQUIRED) + +set(config_test_src + src/logging_test.cpp +) + +ecal_add_gtest(${PROJECT_NAME} ${config_test_src}) + +target_include_directories(${PROJECT_NAME} PRIVATE + $ + ${CMAKE_CURRENT_LIST_DIR}/src + ${ECAL_CORE_PROJECT_ROOT}/core/src +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + eCAL::core + Threads::Threads +) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) + +target_compile_definitions(${PROJECT_NAME} PRIVATE ECAL_CORE_COMMAND_LINE) + +ecal_install_gtest(${PROJECT_NAME}) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER tests/cpp/logging) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${${PROJECT_NAME}_src} +) diff --git a/ecal/tests/cpp/logging_test/src/logging_test.cpp b/ecal/tests/cpp/logging_test/src/logging_test.cpp new file mode 100644 index 00000000..db9009e7 --- /dev/null +++ b/ecal/tests/cpp/logging_test/src/logging_test.cpp @@ -0,0 +1,361 @@ +/* ========================= 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 + +#include +#include +#include +#include + +constexpr std::chrono::milliseconds UDP_WAIT_TIME(1); + +class CoutRedirect { +public: + CoutRedirect(std::ostream& newStream) : originalBuffer(std::cout.rdbuf(newStream.rdbuf())) {} + ~CoutRedirect() { std::cout.rdbuf(originalBuffer); } + + CoutRedirect(const CoutRedirect&) = delete; + CoutRedirect& operator=(const CoutRedirect&) = delete; + CoutRedirect(CoutRedirect&&) = delete; + CoutRedirect& operator=(CoutRedirect&&) = delete; + +private: + std::streambuf* originalBuffer; +}; + +eCAL::Configuration GetUDPConfiguration() +{ + eCAL::Configuration config; + config.logging.sinks.file.enable = false; + config.logging.sinks.console.enable = false; + config.logging.sinks.udp.enable = true; + config.logging.sinks.udp.filter_log_udp = log_level_all; + return config; +} + +eCAL::Configuration GetFileConfiguration(const std::string& path_) +{ + eCAL::Configuration config; + config.logging.sinks.udp.enable = false; + config.logging.sinks.console.enable = false; + config.logging.sinks.file.enable = true; + config.logging.sinks.file.path = path_; + config.logging.sinks.file.filter_log_file = log_level_all; + return config; +} + +eCAL::Configuration GetConsoleConfiguration() +{ + eCAL::Configuration config; + config.logging.sinks.file.enable = false; + config.logging.sinks.udp.enable = false; + config.logging.sinks.console.enable = true; + config.logging.sinks.console.filter_log_con = log_level_all; + return config; +} + +TEST(logging_to /*unused*/, file /*unused*/) +{ + const std::string logging_path = "./"; + const std::string unit_name = "logging_to_file_test"; + const std::string log_message = "Logging to file test."; + auto ecal_config = GetFileConfiguration(logging_path); + + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + eCAL::Logging::Log(log_level_info, log_message); + + eCAL::Finalize(); + + std::string filepath; + for (const auto& entry : std::filesystem::directory_iterator(logging_path)) + { + if (entry.is_regular_file()) + { + if (entry.path().string().find(unit_name) != std::string::npos) + { + filepath = entry.path().string(); + } + } + } + + EXPECT_NE(filepath, ""); + + std::ifstream logfile(filepath); + + std::string line; + std::getline(logfile, line); + EXPECT_NE(line, ""); + + auto find_message = line.find(log_message); + EXPECT_NE(find_message, std::string::npos); + + logfile.close(); + + if (!filepath.empty()) std::remove(filepath.c_str()); +} + +TEST(logging_to /*unused*/, udp /*unused*/) +{ + const std::string unit_name = "logging_to_udp_test"; + const std::string log_message = "Logging to udp test."; + auto ecal_config = GetUDPConfiguration(); + + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + eCAL::Logging::Log(log_level_info, log_message); + + std::this_thread::sleep_for(UDP_WAIT_TIME); + + eCAL::Logging::SLogging log; + eCAL::Logging::GetLogging(log); + + EXPECT_EQ(log.log_messages.size(), 1); + + // check whole log message for information + // check before the size -> crashes the whole test if it's not checked before + if (!log.log_messages.empty()) + { + EXPECT_EQ(log.log_messages.front().hname, eCAL::Process::GetHostName()); + EXPECT_EQ(log.log_messages.front().pid, eCAL::Process::GetProcessID()); + EXPECT_EQ(log.log_messages.front().uname, unit_name); + EXPECT_EQ(log.log_messages.front().level, log_level_info); + EXPECT_TRUE(log.log_messages.front().content.find(log_message) != std::string::npos); + } + + eCAL::Finalize(); +} + +TEST(logging_to /*unused*/, console /*unused*/) +{ + const std::string unit_name = "logging_to_console_test"; + const std::string log_message = "Logging to console test."; + auto ecal_config = GetConsoleConfiguration(); + + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + { + // Redirect the output stream to a stringstream in order to find log messages + std::stringstream ss; + CoutRedirect redirect(ss); + eCAL::Logging::Log(log_level_info, log_message); + std::string console_output = ss.str(); + EXPECT_TRUE(console_output.find(log_message) != std::string::npos); + } + + eCAL::Finalize(); +} + +int getLogging(eCAL::Logging::SLogging& log_) +{ + std::this_thread::sleep_for(UDP_WAIT_TIME); + + return eCAL::Logging::GetLogging(log_); +} + +TEST(logging_levels /*unused*/, all /*unused*/) +{ + const std::string unit_name = "logging_levels_all_udp"; + const std::string log_message = "Logging level all test for udp."; + auto ecal_config = GetUDPConfiguration(); + + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + eCAL::Logging::SLogging log; + + eCAL::Logging::Log(log_level_info, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Logging::Log(log_level_warning, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Logging::Log(log_level_error, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Logging::Log(log_level_debug1, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Logging::Log(log_level_debug1, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Logging::Log(log_level_debug2, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Logging::Log(log_level_debug3, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Logging::Log(log_level_debug4, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Finalize(); +} + +TEST(logging_levels /*unused*/, various /*unused*/) +{ + const std::string unit_name = "logging_levels_various_udp"; + const std::string log_message = "Logging level various test for udp."; + auto ecal_config = GetUDPConfiguration(); + + ecal_config.logging.sinks.udp.filter_log_udp = log_level_warning; + + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + eCAL::Logging::SLogging log; + + // logging not expected + eCAL::Logging::Log(log_level_info, log_message); + EXPECT_EQ(getLogging(log), 0); + + // logging expected + eCAL::Logging::Log(log_level_warning, log_message); + EXPECT_EQ(getLogging(log), 1); + + // change log filter for udp logging + eCAL::Logging::SetUDPLogFilter(log_level_info | log_level_debug1); + + // logging not expected + eCAL::Logging::Log(log_level_warning, log_message); + EXPECT_EQ(getLogging(log), 0); + + // logging expected + eCAL::Logging::Log(log_level_info, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Logging::Log(log_level_debug1, log_message); + EXPECT_EQ(getLogging(log), 1); + + eCAL::Finalize(); +} + +TEST(logging_levels /*unused*/, none /*unused*/) +{ + const std::string unit_name = "logging_levels_various_udp"; + const std::string log_message = "Logging level none test for udp."; + auto ecal_config = GetUDPConfiguration(); + + ecal_config.logging.sinks.udp.filter_log_udp = log_level_none; + + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + eCAL::Logging::SLogging log; + + eCAL::Logging::Log(log_level_info, log_message); + EXPECT_EQ(getLogging(log), 0); + + eCAL::Logging::Log(log_level_warning, log_message); + EXPECT_EQ(getLogging(log), 0); + + eCAL::Logging::Log(log_level_error, log_message); + EXPECT_EQ(getLogging(log), 0); + + eCAL::Logging::Log(log_level_debug1, log_message); + EXPECT_EQ(getLogging(log), 0); + + eCAL::Logging::Log(log_level_debug1, log_message); + EXPECT_EQ(getLogging(log), 0); + + eCAL::Logging::Log(log_level_debug2, log_message); + EXPECT_EQ(getLogging(log), 0); + + eCAL::Logging::Log(log_level_debug3, log_message); + EXPECT_EQ(getLogging(log), 0); + + eCAL::Logging::Log(log_level_debug4, log_message); + EXPECT_EQ(getLogging(log), 0); + + eCAL::Finalize(); +} + +TEST(logging_disable /*unused*/, file /*unused*/) +{ + const std::string logging_path = "./"; + const std::string unit_name = "logging_disable_file_test"; + const std::string log_message = "Disabled logging test for file."; + auto ecal_config = GetFileConfiguration(logging_path); + + ecal_config.logging.sinks.file.enable = false; + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + eCAL::Logging::Log(log_level_info, log_message); + + eCAL::Finalize(); + + std::string filepath; + for (const auto& entry : std::filesystem::directory_iterator(logging_path)) + { + if (entry.is_regular_file()) + { + if (entry.path().string().find(unit_name) != std::string::npos) + { + filepath = entry.path().string(); + } + } + } + + EXPECT_EQ(filepath, ""); +} + +TEST(logging_disable /*unused*/, udp /*unused*/) +{ + const std::string unit_name = "logging_dsiable_udp_test"; + const std::string log_message = "Disabled logging test for udp."; + auto ecal_config = GetUDPConfiguration(); + + ecal_config.logging.sinks.udp.enable = false; + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + eCAL::Logging::Log(log_level_info, log_message); + + std::this_thread::sleep_for(UDP_WAIT_TIME); + + eCAL::Logging::SLogging log; + eCAL::Logging::GetLogging(log); + + EXPECT_EQ(log.log_messages.size(), 0); + + eCAL::Finalize(); +} + +TEST(logging_disable /*unused*/, console /*unused*/) +{ + const std::string unit_name = "logging_disable_console_test"; + const std::string log_message = "Disabled logging test for console."; + auto ecal_config = GetConsoleConfiguration(); + + ecal_config.logging.sinks.console.enable = false; + eCAL::Initialize(ecal_config, unit_name.c_str(), eCAL::Init::Logging); + + { + // Redirect the output stream to a stringstream in order to find log messages + std::stringstream ss; + CoutRedirect redirect(ss); + eCAL::Logging::Log(log_level_info, log_message); + std::string console_output = ss.str(); + EXPECT_TRUE(console_output.find(log_message) == std::string::npos); + } + + eCAL::Finalize(); +} \ No newline at end of file diff --git a/ecal/tests/cpp/monitoring_test/CMakeLists.txt b/ecal/tests/cpp/monitoring_test/CMakeLists.txt new file mode 100644 index 00000000..e83f5ca0 --- /dev/null +++ b/ecal/tests/cpp/monitoring_test/CMakeLists.txt @@ -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 ================================= + +project(test_monitoring) + +find_package(Threads REQUIRED) +find_package(GTest REQUIRED) + +set(test_monitoring_src + src/monitoring_filter_test.cpp + ${ECAL_CORE_PROJECT_ROOT}/core/src/monitoring/ecal_monitoring_filter.cpp +) + +ecal_add_gtest(${PROJECT_NAME} ${test_monitoring_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) \ No newline at end of file diff --git a/ecal/tests/cpp/monitoring_test/src/monitoring_filter_test.cpp b/ecal/tests/cpp/monitoring_test/src/monitoring_filter_test.cpp new file mode 100644 index 00000000..1b08acf0 --- /dev/null +++ b/ecal/tests/cpp/monitoring_test/src/monitoring_filter_test.cpp @@ -0,0 +1,113 @@ +/* ========================= 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 + +// Unit test class for CExpandingVector +class CMonitoringFilterTest : public ::testing::Test { +protected: + const std::vector topic_names = { "topic_1", "topic_2", "topic_3" , "apple", "banana", "__internal"}; // Create a vector of MyElement type + const std::vector all_accepted = { true, true, true, true, true, true }; // Create a vector of MyElement type +}; + +using core_cpp_monitoring_filter = CMonitoringFilterTest; + +void EvaluateFilter(const eCAL::CMonitoringFilter& filter, const std::vector& input, const std::vector& expected_outcome) +{ + for (size_t i = 0; i < input.size() && i < expected_outcome.size(); ++i) + { + auto accepted = filter.AcceptTopic(input[i]); + if (expected_outcome[i]) + EXPECT_EQ(accepted, expected_outcome[i]) << input[i] << " should be accepted"; + else + EXPECT_EQ(accepted, expected_outcome[i]) << input[i] << " should not be accepted"; + } +} + + +// Test push_back and size functionality +TEST_F(core_cpp_monitoring_filter, DefaultFilter) { + // A default filter should accept all topics + const eCAL::CMonitoringFilter filter{ eCAL::Monitoring::SAttributes() }; + EvaluateFilter(filter, topic_names, all_accepted); +} + +TEST_F(core_cpp_monitoring_filter, ExcludeFilter) { + // A default filter should accept all topics + eCAL::Monitoring::SAttributes attr; + attr.filter_excl = "^__.*$"; + attr.filter_incl = ""; + const eCAL::CMonitoringFilter filter{ attr }; + const std::vector expected = { true, true, true, true, true, false }; + EvaluateFilter(filter, topic_names, expected); +} + +TEST_F(core_cpp_monitoring_filter, IncludeFilter) { + // A default filter should accept all topics + eCAL::Monitoring::SAttributes attr; + attr.filter_excl = ""; + attr.filter_incl = "^topic_.*$"; + const eCAL::CMonitoringFilter filter{ attr }; + const std::vector expected = { true, true, true, false, false, false }; + EvaluateFilter(filter, topic_names, expected); +} + +TEST_F(core_cpp_monitoring_filter, IncludeExcludeFilter) { + // A default filter should accept all topics + eCAL::Monitoring::SAttributes attr; + attr.filter_excl = "^topic_1$"; + attr.filter_incl = "^topic_.*$"; + const eCAL::CMonitoringFilter filter{ attr }; + const std::vector expected = { false, true, true, false, false, false }; + EvaluateFilter(filter, topic_names, expected); +} + +TEST_F(core_cpp_monitoring_filter, ApplyFilter) { + // After deactivating the filter, everything should be accepted. + eCAL::Monitoring::SAttributes attr; + attr.filter_excl = "^topic_1$"; + attr.filter_incl = "^topic_.*$"; + eCAL::CMonitoringFilter filter{ attr }; + filter.DeactivateFilter(); + EvaluateFilter(filter, topic_names, all_accepted); +} + +TEST_F(core_cpp_monitoring_filter, SetFilter) { + // Try to set the filters afterwards + eCAL::Monitoring::SAttributes attr; + eCAL::CMonitoringFilter filter{ attr }; + EvaluateFilter(filter, topic_names, all_accepted); + + filter.SetInclFilter("^topic_.*$"); + filter.ActivateFilter(); + const std::vector include = { true, true, true, false, false, false }; + EvaluateFilter(filter, topic_names, include); + + filter.SetExclFilter("^topic_1$"); + filter.ActivateFilter(); + const std::vector include_exclude = { false, true, true, false, false, false }; + EvaluateFilter(filter, topic_names, include_exclude); +} + + + diff --git a/ecal/tests/cpp/pubsub_test/CMakeLists.txt b/ecal/tests/cpp/pubsub_test/CMakeLists.txt index 02862a6b..e42c1369 100644 --- a/ecal/tests/cpp/pubsub_test/CMakeLists.txt +++ b/ecal/tests/cpp/pubsub_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. @@ -24,6 +24,7 @@ find_package(GTest REQUIRED) if(ECAL_CORE_TRANSPORT_SHM) set(pubsub_test_src_shm src/pubsub_acknowledge.cpp + src/pubsub_connection_test.cpp src/pubsub_multibuffer.cpp src/pubsub_test_shm.cpp ) @@ -36,6 +37,7 @@ if(ECAL_CORE_TRANSPORT_UDP) endif() set(pubsub_test_src + src/pubsub_callback_topicid.cpp src/pubsub_receive_test.cpp src/pubsub_test.cpp ${pubsub_test_src_shm} diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_callback_topicid.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_callback_topicid.cpp new file mode 100644 index 00000000..cd3c99c8 --- /dev/null +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_callback_topicid.cpp @@ -0,0 +1,197 @@ +/* ========================= 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 + +namespace eCAL +{ + void PrintTo(const Configuration& config, std::ostream* os) { + *os << "Config Layers: "; + if (config.publisher.layer.shm.enable) + *os << "SHM "; + if (config.publisher.layer.tcp.enable) + *os << "TCP "; + if (config.publisher.layer.udp.enable) + *os << "UDP "; + } +} + + +enum { + DATA_FLOW_TIME_MS = 100 +}; + +eCAL::Configuration GetTestingConfig() +{ + eCAL::Configuration config; + config.registration.registration_refresh = 100; + config.registration.registration_timeout = 200; + + config.publisher.layer.shm.enable = false; + config.publisher.layer.udp.enable = false; + config.publisher.layer.tcp.enable = false; + + config.subscriber.layer.shm.enable = false; + config.subscriber.layer.udp.enable = false; + config.subscriber.layer.tcp.enable = false; + + return config; +} + +eCAL::Configuration EnableUDP(const eCAL::Configuration& config_) +{ + eCAL::Configuration config(config_); + config.publisher.layer.udp.enable = true; + config.subscriber.layer.udp.enable = true; + return config; +} + + +eCAL::Configuration EnableTCP(const eCAL::Configuration& config_) +{ + eCAL::Configuration config(config_); + config.publisher.layer.tcp.enable = true; + config.subscriber.layer.tcp.enable = true; + return config; +} + +eCAL::Configuration EnableSHM(const eCAL::Configuration& config_) +{ + eCAL::Configuration config(config_); + config.publisher.layer.shm.enable = true; + config.subscriber.layer.shm.enable = true; + return config; +} + + +// test fixture class +class TestFixture : public ::testing::TestWithParam +{ +protected: + eCAL::Configuration config; + + void SetUp() override + { + config = GetParam(); + eCAL::Initialize(config, "callback_topic_id"); + } + + void TearDown() override + { + eCAL::Finalize(); + } +}; + +TEST_P(TestFixture, OnePubSub) +{ + eCAL::SDataTypeInformation datatype_info; + datatype_info.name = "mytype"; + datatype_info.encoding = "test"; + datatype_info.descriptor = "desc"; + + eCAL::CPublisher publisher ("foo", datatype_info); + eCAL::CSubscriber subscriber("foo", datatype_info); + + eCAL::Registration::STopicId callback_topic_id; + eCAL::SDataTypeInformation callback_datatype_info; + int callback_count{ 0 }; + + subscriber.AddReceiveCallback([&callback_topic_id, &callback_datatype_info, &callback_count](const eCAL::Registration::STopicId& topic_id_, const eCAL::SDataTypeInformation& datatype_info_, const eCAL::SReceiveCallbackData&) + { + ++callback_count; + callback_topic_id = topic_id_; + callback_datatype_info = datatype_info_; + } + ); + const auto pub_id = publisher.GetId(); + + // let them match + eCAL::Process::SleepMS(2 * config.registration.registration_refresh); + // send data + publisher.Send("abc"); + // make sure data was received + std::this_thread::sleep_for(std::chrono::milliseconds(DATA_FLOW_TIME_MS)); + + EXPECT_EQ(callback_topic_id, pub_id); + EXPECT_EQ(callback_datatype_info, datatype_info); +} + +TEST_P(TestFixture, MultiplePubSub) +{ + const int num_publishers = 4; + + std::vector publishers; + for (int i = 0; i < num_publishers; ++i) + { + eCAL::SDataTypeInformation datatype_info; + datatype_info.name = "mytype"; + datatype_info.encoding = "test"; + datatype_info.descriptor = "desc" + std::to_string(i); + + publishers.emplace_back("foo", datatype_info); + } + eCAL::CSubscriber subscriber("foo"); + + eCAL::Registration::STopicId callback_topic_id; + eCAL::SDataTypeInformation callback_datatype_info; + int callback_count{ 0 }; + + subscriber.AddReceiveCallback([&callback_topic_id, &callback_datatype_info, &callback_count](const eCAL::Registration::STopicId& topic_id_, const eCAL::SDataTypeInformation& datatype_info_, const eCAL::SReceiveCallbackData&) + { + ++callback_count; + callback_topic_id = topic_id_; + callback_datatype_info = datatype_info_; + } + ); + + // let them match + eCAL::Process::SleepMS(2 * config.registration.registration_refresh); + + for (int i = 0; i < num_publishers; ++i) + { + auto& publisher = publishers[i]; + const auto pub_id = publisher.GetId(); + const auto pub_datatype_info = publisher.GetDataTypeInformation(); + + // send data + publisher.Send("abc"); + // make sure data was received + std::this_thread::sleep_for(std::chrono::milliseconds(DATA_FLOW_TIME_MS)); + + EXPECT_EQ(callback_topic_id, pub_id); + EXPECT_EQ(callback_datatype_info, pub_datatype_info); + } +} + +// Define the test parameters +INSTANTIATE_TEST_SUITE_P( + core_cpp_pubsub_callback_topid_id, + TestFixture, + ::testing::Values( + EnableSHM(GetTestingConfig()), + EnableUDP(GetTestingConfig()), + EnableTCP(GetTestingConfig()) + ) +); diff --git a/ecal/tests/cpp/pubsub_test/src/pubsub_connection_test.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_connection_test.cpp new file mode 100644 index 00000000..83a943a6 --- /dev/null +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_connection_test.cpp @@ -0,0 +1,234 @@ +/* ========================= 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 +#include + +#include + +TEST(core_cpp_pubsub, TestSubscriberIsPublishedTiming) +{ + // initialize eCAL API + EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "subscriber_is_published")); + + // enable data loopback + eCAL::Util::EnableLoopback(true); + + std::atomic do_start_publication(false); + std::atomic publication_finished(false); + + // publishing thread + std::atomic subscriber_seen_at_publication_start(false); + auto publisher_thread = [&]() { + eCAL::Publisher::Configuration pub_config; + pub_config.layer.shm.acknowledge_timeout_ms = 500; + eCAL::CPublisher pub("blob", pub_config); + + int pub_count(0); + const auto max_pub_count(1000); + while (eCAL::Ok()) + { + if (do_start_publication && pub_count < max_pub_count) + { + if (pub_count == 0) + { + subscriber_seen_at_publication_start = pub.IsSubscribed(); + } + + pub.Send(std::to_string(pub_count)); + pub_count++; + + if (pub_count == max_pub_count) + { + publication_finished = true; + break; + } + } + } + }; + + // subscribing thread + std::atomic publisher_seen_at_subscription_start(false); + std::string first_received_sample; + auto subscriber_thread = [&]() { + eCAL::CSubscriber sub("blob"); + bool received(false); + const auto max_sub_count(10); + auto sub_count(0); + auto receive_lambda = [&](const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) + { + if (sub_count == 0) + { + publisher_seen_at_subscription_start = sub.IsPublished(); + first_received_sample = std::string(static_cast(data_->buf), data_->size); + } + + if (sub_count < max_sub_count) + { + // 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; + sub_count++; + } + }; + 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(); + + // the first received sample should be "0" + EXPECT_TRUE(first_received_sample == "0"); + + // check if the publisher has seen the subscriber before publishing + EXPECT_TRUE(subscriber_seen_at_publication_start); + + // check if the subscriber has seen the publisher on first receive + EXPECT_TRUE(publisher_seen_at_subscription_start); + + // finalize eCAL API + eCAL::Finalize(); +} + +TEST(core_cpp_pubsub, TestPublisherIsSubscribedTiming) +{ + // initialize eCAL API + EXPECT_EQ(0, eCAL::Initialize(0, nullptr, "publisher_is_subscribed")); + + // enable data loopback + eCAL::Util::EnableLoopback(true); + + 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 (pub.IsSubscribed()) + { + do_start_publication = true; + } + + if (do_start_publication && cnt < max_runs) + { + pub.Send(std::to_string(cnt)); + cnt++; + + if (cnt == max_runs) + { + publication_finished = true; + break; + } + } + } + }; + + // subscribing thread + std::atomic publisher_seen_at_subscription_start(false); + std::string first_received_sample; + auto subscriber_thread = [&]() { + eCAL::CSubscriber sub("blob"); + bool received(false); + const auto max_sub_count(10); + auto sub_count(0); + auto receive_lambda = [&](const char* /*topic_name_*/, const struct eCAL::SReceiveCallbackData* data_) + { + if (sub_count == 0) + { + publisher_seen_at_subscription_start = sub.IsPublished(); + first_received_sample = std::string(static_cast(data_->buf), data_->size); + } + + if (sub_count < max_sub_count) + { + // 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; + sub_count++; + } + }; + sub.AddReceiveCallback(receive_lambda); + + while (eCAL::Ok() && !publication_finished) + { + } + }; + + // 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(); + + // the first received sample should be "0" + EXPECT_TRUE(first_received_sample == "0"); + + // check if the subscriber has seen the publisher on first receive + EXPECT_TRUE(publisher_seen_at_subscription_start); + + // finalize eCAL API + eCAL::Finalize(); +} 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 25a8bc70..2b7a9fc3 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_receive_test.cpp @@ -216,97 +216,3 @@ 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.cpp b/ecal/tests/cpp/pubsub_test/src/pubsub_test.cpp index 39dba78b..ba050bb7 100644 --- a/ecal/tests/cpp/pubsub_test/src/pubsub_test.cpp +++ b/ecal/tests/cpp/pubsub_test/src/pubsub_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. @@ -615,8 +615,8 @@ TEST(core_cpp_pubsub, SubscriberReconnection) }; sub_foo.AddReceiveCallback(receive_lambda); - // sleep for 2 seconds, we should receive something - std::this_thread::sleep_for(std::chrono::seconds(2)); + // sleep for 3 seconds, we should receive something + std::this_thread::sleep_for(std::chrono::seconds(3)); EXPECT_TRUE(callback_received_count > 0); } @@ -632,8 +632,8 @@ TEST(core_cpp_pubsub, SubscriberReconnection) }; sub_foo.AddReceiveCallback(receive_lambda); - // sleep for 2 seconds, we should receive something - std::this_thread::sleep_for(std::chrono::seconds(2)); + // sleep for 3 seconds, we should receive something + std::this_thread::sleep_for(std::chrono::seconds(3)); EXPECT_TRUE(callback_received_count > 0); } diff --git a/ecal/tests/cpp/registration_test/CMakeLists.txt b/ecal/tests/cpp/registration_test/CMakeLists.txt new file mode 100644 index 00000000..dcb27813 --- /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 00000000..ff961d62 --- /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 00000000..9605dc3c --- /dev/null +++ b/ecal/tests/cpp/registration_test_public/CMakeLists.txt @@ -0,0 +1,61 @@ +# ========================= 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_getpublisherids.cpp + src/registration_getsubscriberids.cpp + 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 68% rename from ecal/tests/cpp/util_test/src/util_getclients.cpp rename to ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp index ee93e2ef..8c3a09b0 100644 --- a/ecal/tests/cpp/util_test/src/util_getclients.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_getclients.cpp @@ -25,16 +25,15 @@ enum { CMN_MONITORING_TIMEOUT_MS = (5000 + 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 { @@ -50,14 +49,14 @@ TEST(core_cpp_util, ClientExpiration) 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) { @@ -69,7 +68,7 @@ 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); @@ -80,7 +79,7 @@ TEST(core_cpp_util, ClientExpiration) // 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); @@ -89,15 +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 { @@ -113,7 +112,7 @@ TEST(core_cpp_util, ClientEqualQualities) 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); @@ -122,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"); @@ -139,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"); @@ -153,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); @@ -165,10 +164,10 @@ TEST(core_cpp_util, ClientEqualQualities) 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"); } @@ -178,7 +177,7 @@ TEST(core_cpp_util, ClientEqualQualities) // 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); @@ -187,15 +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"); // 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 different qualities of data type information { @@ -211,7 +210,7 @@ TEST(core_cpp_util, ClientDifferentQualities) 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); @@ -220,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, ""); @@ -239,10 +238,10 @@ TEST(core_cpp_util, ClientDifferentQualities) 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"); @@ -255,7 +254,7 @@ TEST(core_cpp_util, ClientDifferentQualities) // 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); @@ -263,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/registration_test_public/src/registration_getpublisherids.cpp b/ecal/tests/cpp/registration_test_public/src/registration_getpublisherids.cpp new file mode 100644 index 00000000..18b5fa61 --- /dev/null +++ b/ecal/tests/cpp/registration_test_public/src/registration_getpublisherids.cpp @@ -0,0 +1,174 @@ +/* ========================= 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 + +// struct to hold the test parameters +struct TestParams +{ + int publisher_count = 0; + eCAL::Configuration configuration; +}; + +// test class that accepts TestParams as a parameter +class TestFixture : public ::testing::TestWithParam +{ +protected: + void SetUp() override + { + // set configuration from the test parameters + auto params = GetParam(); + eCAL::Initialize(params.configuration, "core_cpp_registration_publisherids"); + } + + void TearDown() override + { + // clean up + eCAL::Finalize(); + } +}; + +TEST_P(TestFixture, GetPublisherIDsReturnsCorrectNumber) +{ + { + // create publishers for testing + std::vector publisher_vec; + for (int i = 0; i < GetParam().publisher_count; ++i) + { + std::stringstream tname; + tname << "topic_" << i; + + eCAL::SDataTypeInformation data_type_info; + data_type_info.name = tname.str() + "_type_name"; + data_type_info.encoding = tname.str() + "_type_encoding"; + data_type_info.descriptor = tname.str() + "_type_descriptor"; + + publisher_vec.emplace_back(tname.str(), data_type_info); + } + + // let's register + eCAL::Process::SleepMS(2 * GetParam().configuration.registration.registration_refresh); + + // get the list of publisher IDs + const auto pub_ids1 = eCAL::Registration::GetPublisherIDs(); + + // verify the number of publishers created + ASSERT_EQ(pub_ids1.size(), GetParam().publisher_count); + } + + // let's finally timeout + eCAL::Process::SleepMS(GetParam().configuration.registration.registration_timeout); + + // get the list of publisher IDs + const auto pub_ids2 = eCAL::Registration::GetPublisherIDs(); + + // all publisher should be timeouted + ASSERT_EQ(pub_ids2.size(), 0); +} + +TEST_P(TestFixture, PublisherEventCallbackIsTriggered) +{ + std::atomic created_publisher_num(0); + std::atomic deleted_publisher_num(0); + std::set created_publisher_ids; + std::set deleted_publisher_ids; + + // register the callback + auto callback_token = eCAL::Registration::AddPublisherEventCallback( + [&](const eCAL::Registration::STopicId& id, eCAL::Registration::RegistrationEventType event_type) + { + if (event_type == eCAL::Registration::RegistrationEventType::new_entity) + { + created_publisher_num++; + created_publisher_ids.insert(id); + } + else if (event_type == eCAL::Registration::RegistrationEventType::deleted_entity) + { + deleted_publisher_num++; + deleted_publisher_ids.insert(id); + } + }); + + { + // create publishers for testing + std::vector publisher_vec; + for (int i = 0; i < GetParam().publisher_count; ++i) + { + std::stringstream tname; + tname << "topic_" << i; + + eCAL::SDataTypeInformation data_type_info; + data_type_info.name = tname.str() + "_type_name"; + data_type_info.encoding = tname.str() + "_type_encoding"; + data_type_info.descriptor = tname.str() + "_type_descriptor"; + + publisher_vec.emplace_back(tname.str(), data_type_info); + } + + // let's register + eCAL::Process::SleepMS(2 * GetParam().configuration.registration.registration_refresh); + + // verify the number of publishers created through the callback + ASSERT_EQ(created_publisher_num.load(), GetParam().publisher_count); + + // clear publishers to trigger deletion events + publisher_vec.clear(); + + // let's register the deletion events + eCAL::Process::SleepMS(2 * GetParam().configuration.registration.registration_refresh); + + // verify the number of publishers deleted through the callback + ASSERT_EQ(deleted_publisher_num.load(), GetParam().publisher_count); + } + + // unregister the callback + eCAL::Registration::RemPublisherEventCallback(callback_token); +} + +// instantiate the test suite with different configurations and publisher counts +INSTANTIATE_TEST_SUITE_P( + core_cpp_registration_public, + TestFixture, + ::testing::Values( + TestParams{ 10, []() { + // shm + eCAL::Configuration config; + config.registration.registration_refresh = 100; + config.registration.registration_timeout = 200; + config.registration.layer.shm.enable = true; + config.registration.layer.udp.enable = false; + return config; + }() }, + TestParams{ 10, []() { + // udp + eCAL::Configuration config; + config.registration.registration_refresh = 100; + config.registration.registration_timeout = 200; + config.registration.layer.shm.enable = false; + config.registration.layer.udp.enable = true; + return config; + }() } + ) +); 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 63% rename from ecal/tests/cpp/util_test/src/util_getservices.cpp rename to ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp index 2e1aef8c..195053c4 100644 --- a/ecal/tests/cpp/util_test/src/util_getservices.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_getservices.cpp @@ -26,15 +26,15 @@ enum { 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"); // enable loop back communication in the same process eCAL::Util::EnableLoopback(true); - std::map service_info_map; + std::map service_info_map; // create simple service and let it expire { @@ -46,14 +46,14 @@ TEST(core_cpp_util, ServiceExpiration) 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) { @@ -65,7 +65,7 @@ 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); @@ -76,7 +76,7 @@ TEST(core_cpp_util, ServiceExpiration) // 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); @@ -85,15 +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 { @@ -105,7 +105,7 @@ TEST(core_cpp_util, ServiceEqualQualities) 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); @@ -114,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"); @@ -127,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"); @@ -141,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); @@ -153,10 +153,10 @@ TEST(core_cpp_util, ServiceEqualQualities) 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"); } @@ -166,7 +166,7 @@ TEST(core_cpp_util, ServiceEqualQualities) // 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); @@ -175,15 +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 { @@ -195,7 +195,7 @@ TEST(core_cpp_util, ServiceDifferentQualities) 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); @@ -204,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, ""); @@ -219,10 +219,10 @@ TEST(core_cpp_util, ServiceDifferentQualities) 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"); @@ -235,7 +235,7 @@ TEST(core_cpp_util, ServiceDifferentQualities) // 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); @@ -243,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_getsubscriberids.cpp b/ecal/tests/cpp/registration_test_public/src/registration_getsubscriberids.cpp new file mode 100644 index 00000000..28138fcb --- /dev/null +++ b/ecal/tests/cpp/registration_test_public/src/registration_getsubscriberids.cpp @@ -0,0 +1,174 @@ +/* ========================= 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 + +// struct to hold the test parameters +struct TestParams +{ + int subscriber_count = 0; + eCAL::Configuration configuration; +}; + +// test class that accepts TestParams as a parameter +class TestFixture : public ::testing::TestWithParam +{ +protected: + void SetUp() override + { + // set configuration from the test parameters + auto params = GetParam(); + eCAL::Initialize(params.configuration, "core_cpp_registration_subscriberids"); + } + + void TearDown() override + { + // clean up + eCAL::Finalize(); + } +}; + +TEST_P(TestFixture, GetSubscriberIDsReturnsCorrectNumber) +{ + { + // create subscribers for testing + std::vector subscriber_vec; + for (int i = 0; i < GetParam().subscriber_count; ++i) + { + std::stringstream tname; + tname << "topic_" << i; + + eCAL::SDataTypeInformation data_type_info; + data_type_info.name = tname.str() + "_type_name"; + data_type_info.encoding = tname.str() + "_type_encoding"; + data_type_info.descriptor = tname.str() + "_type_descriptor"; + + subscriber_vec.emplace_back(tname.str(), data_type_info); + } + + // let's register + eCAL::Process::SleepMS(2 * GetParam().configuration.registration.registration_refresh); + + // get the list of subscriber IDs + const auto sub_ids1 = eCAL::Registration::GetSubscriberIDs(); + + // verify the number of subscribers created + ASSERT_EQ(sub_ids1.size(), GetParam().subscriber_count); + } + + // let's finally timeout + eCAL::Process::SleepMS(2 * GetParam().configuration.registration.registration_timeout); + + // get the list of subscriber IDs + const auto sub_ids2 = eCAL::Registration::GetSubscriberIDs(); + + // all subscriber should be timeouted + ASSERT_EQ(sub_ids2.size(), 0); +} + +TEST_P(TestFixture, SubscriberEventCallbackIsTriggered) +{ + std::atomic created_subscriber_num(0); + std::atomic deleted_subscriber_num(0); + std::set created_subscriber_ids; + std::set deleted_subscriber_ids; + + // register the callback + auto callback_token = eCAL::Registration::AddSubscriberEventCallback( + [&](const eCAL::Registration::STopicId& id, eCAL::Registration::RegistrationEventType event_type) + { + if (event_type == eCAL::Registration::RegistrationEventType::new_entity) + { + created_subscriber_num++; + created_subscriber_ids.insert(id); + } + else if (event_type == eCAL::Registration::RegistrationEventType::deleted_entity) + { + deleted_subscriber_num++; + deleted_subscriber_ids.insert(id); + } + }); + + { + // create subscribers for testing + std::vector subscriber_vec; + for (int i = 0; i < GetParam().subscriber_count; ++i) + { + std::stringstream tname; + tname << "topic_" << i; + + eCAL::SDataTypeInformation data_type_info; + data_type_info.name = tname.str() + "_type_name"; + data_type_info.encoding = tname.str() + "_type_encoding"; + data_type_info.descriptor = tname.str() + "_type_descriptor"; + + subscriber_vec.emplace_back(tname.str(), data_type_info); + } + + // let's register + eCAL::Process::SleepMS(2 * GetParam().configuration.registration.registration_refresh); + + // verify the number of subscribers created through the callback + ASSERT_EQ(created_subscriber_num.load(), GetParam().subscriber_count); + + // clear subscribers to trigger deletion events + subscriber_vec.clear(); + + // let's register the deletion events + eCAL::Process::SleepMS(2 * GetParam().configuration.registration.registration_refresh); + + // verify the number of subscribers deleted through the callback + ASSERT_EQ(deleted_subscriber_num.load(), GetParam().subscriber_count); + } + + // unregister the callback + eCAL::Registration::RemSubscriberEventCallback(callback_token); +} + +// instantiate the test suite with different configurations and subscriber counts +INSTANTIATE_TEST_SUITE_P( + core_cpp_registration_public, + TestFixture, + ::testing::Values( + TestParams{ 10, []() { + // shm + eCAL::Configuration config; + config.registration.registration_refresh = 100; + config.registration.registration_timeout = 200; + config.registration.layer.shm.enable = true; + config.registration.layer.udp.enable = false; + return config; + }() }, + TestParams{ 10, []() { + // udp + eCAL::Configuration config; + config.registration.registration_refresh = 100; + config.registration.registration_timeout = 200; + config.registration.layer.shm.enable = false; + config.registration.layer.udp.enable = true; + return config; + }() } + ) +); diff --git a/ecal/tests/cpp/util_test/src/util_gettopics.cpp b/ecal/tests/cpp/registration_test_public/src/registration_gettopics.cpp similarity index 68% rename from ecal/tests/cpp/util_test/src/util_gettopics.cpp rename to ecal/tests/cpp/registration_test_public/src/registration_gettopics.cpp index 3d4466a3..f88db380 100644 --- a/ecal/tests/cpp/util_test/src/util_gettopics.cpp +++ b/ecal/tests/cpp/registration_test_public/src/registration_gettopics.cpp @@ -31,10 +31,10 @@ enum { CMN_REGISTRATION_REFRESH_MS = (1000) }; -TEST(core_cpp_util, GetTopics) +TEST(core_cpp_registration_public, GetTopics) { // 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); @@ -73,7 +73,7 @@ TEST(core_cpp_util, GetTopics) eCAL::Process::SleepMS(2 * CMN_REGISTRATION_REFRESH_MS); // get all topics - eCAL::Util::GetTopics(topic_info_map); + eCAL::Registration::GetTopics(topic_info_map); // check size EXPECT_EQ(topic_info_map.size(), 5); @@ -81,17 +81,17 @@ TEST(core_cpp_util, GetTopics) // 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 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(utils_topic_info, expected_topic_info); + 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::Util::GetTopics(topic_info_map); + eCAL::Registration::GetTopics(topic_info_map); // check size EXPECT_EQ(topic_info_map.size(), 5); @@ -110,14 +110,14 @@ TEST(core_cpp_util, GetTopics) // 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 registration_topic_info; + EXPECT_EQ(true, eCAL::Registration::GetTopicDataTypeInformation("A1", registration_topic_info)); + EXPECT_EQ(registration_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); + eCAL::SDataTypeInformation registration_topic_info; + EXPECT_EQ(true, eCAL::Registration::GetTopicDataTypeInformation("B1", registration_topic_info)); + EXPECT_EQ(registration_topic_info, info_B1_2); } } @@ -126,7 +126,7 @@ TEST(core_cpp_util, GetTopics) // get all topics again, now all topics // should be removed from the map - eCAL::Util::GetTopics(topic_info_map); + eCAL::Registration::GetTopics(topic_info_map); // check size EXPECT_EQ(topic_info_map.size(), 0); @@ -135,14 +135,19 @@ TEST(core_cpp_util, GetTopics) eCAL::Finalize(); } -TEST(core_cpp_util, GetTopicsParallel) +// 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_util"); + eCAL::Initialize(0, nullptr, "core_cpp_registration_public"); // enable loop back communication in the same process eCAL::Util::EnableLoopback(true); @@ -150,37 +155,36 @@ TEST(core_cpp_util, GetTopicsParallel) 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::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 found_topics = 0; + size_t number_publishers_seen = 0; + size_t max_number_publishers_seen = 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); + eCAL::Registration::GetTopicNames(tmp_topic_names); + eCAL::Registration::GetTopics(topics); - found_topics = tmp_topic_names.size(); - std::cout << "Number of topics found by ecal: " << found_topics << "\n"; + 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 (found_topics != 0); + } while (!testing_completed); + + EXPECT_EQ(number_publishers_seen, 0); + EXPECT_EQ(max_number_publishers_seen, max_publisher_count); }; std::vector threads_container; @@ -194,14 +198,6 @@ TEST(core_cpp_util, GetTopicsParallel) 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/serialization_test/CMakeLists.txt b/ecal/tests/cpp/serialization_test/CMakeLists.txt index 45544213..2ae63f55 100644 --- a/ecal/tests/cpp/serialization_test/CMakeLists.txt +++ b/ecal/tests/cpp/serialization_test/CMakeLists.txt @@ -22,32 +22,32 @@ find_package(Threads REQUIRED) find_package(GTest REQUIRED) set(nanopb_lib_src - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/nanopb/pb.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/nanopb/pb_common.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/nanopb/pb_common.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/nanopb/pb_decode.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/nanopb/pb_decode.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/nanopb/pb_encode.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/nanopb/pb_encode.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/pb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/pb_common.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/pb_common.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/pb_decode.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/pb_decode.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/pb_encode.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/pb_encode.h ) set(nanopb_generated_src - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal.pb.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal.pb.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/host.pb.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/host.pb.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/layer.pb.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/layer.pb.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/logging.pb.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/logging.pb.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/monitoring.pb.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/monitoring.pb.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/process.pb.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/process.pb.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/service.pb.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/service.pb.h - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/topic.pb.c - ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/topic.pb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/ecal.npb.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/ecal.npb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/host.npb.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/host.npb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/layer.npb.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/layer.npb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/logging.npb.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/logging.npb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/monitoring.npb.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/monitoring.npb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/process.npb.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/process.npb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/service.npb.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/service.npb.h + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/topic.npb.c + ${ECAL_CORE_PROJECT_ROOT}/core/src/serialization/nanopb/ecal/core/pb/topic.npb.h ) set(ecal_serialize_src diff --git a/ecal/tests/cpp/serialization_test/src/payload_compare.cpp b/ecal/tests/cpp/serialization_test/src/payload_compare.cpp index a4ae16b2..e6fa0148 100644 --- a/ecal/tests/cpp/serialization_test/src/payload_compare.cpp +++ b/ecal/tests/cpp/serialization_test/src/payload_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. @@ -31,10 +31,11 @@ namespace eCAL return false; } - // compare topic - if (sample1.topic.hname != sample2.topic.hname || - sample1.topic.tid != sample2.topic.tid || - sample1.topic.tname != sample2.topic.tname) { + // compare topic info + if (sample1.topic_info.hname != sample2.topic_info.hname || + sample1.topic_info.pid != sample2.topic_info.pid || + sample1.topic_info.tid != sample2.topic_info.tid || + sample1.topic_info.tname != sample2.topic_info.tname) { return false; } diff --git a/ecal/tests/cpp/serialization_test/src/payload_generate.cpp b/ecal/tests/cpp/serialization_test/src/payload_generate.cpp index 6731adf3..b153a21f 100644 --- a/ecal/tests/cpp/serialization_test/src/payload_generate.cpp +++ b/ecal/tests/cpp/serialization_test/src/payload_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. @@ -29,10 +29,11 @@ namespace eCAL namespace Payload { // generate Topic - Topic GenerateTopic() + TopicInfo GenerateTopic() { - Topic topic; + TopicInfo topic; topic.hname = GenerateString(8); + topic.pid = rand() % 1000; topic.tid = GenerateString(5); topic.tname = GenerateString(10); @@ -92,9 +93,9 @@ namespace eCAL Sample GeneratePayloadSample(const char* payload_addr, size_t payload_size) { Sample sample; - sample.cmd_type = static_cast(rand() % 17); // command type - sample.topic = GenerateTopic(); - sample.content = GenerateContent(payload_addr, payload_size); + sample.cmd_type = static_cast(rand() % 17); // command type + sample.topic_info = GenerateTopic(); + sample.content = GenerateContent(payload_addr, payload_size); return sample; } @@ -103,10 +104,10 @@ namespace eCAL Sample GeneratePayloadSample(const std::vector& payload_vec) { Sample sample; - sample.cmd_type = static_cast(rand() % 17); // command type - sample.topic = GenerateTopic(); - sample.content = GenerateContent(payload_vec); - sample.padding = GenerateRandomVector(8); + sample.cmd_type = static_cast(rand() % 17); // command type + sample.topic_info = GenerateTopic(); + sample.content = GenerateContent(payload_vec); + sample.padding = GenerateRandomVector(8); return sample; } diff --git a/ecal/tests/cpp/serialization_test/src/registration_compare.cpp b/ecal/tests/cpp/serialization_test/src/registration_compare.cpp index 39682f86..bc1c4f20 100644 --- a/ecal/tests/cpp/serialization_test/src/registration_compare.cpp +++ b/ecal/tests/cpp/serialization_test/src/registration_compare.cpp @@ -49,27 +49,6 @@ namespace eCAL (process1.ecal_runtime_version == process2.ecal_runtime_version); } - // compare two Method vector objects - bool CompareMethods(const std::vector& method1_vec, const std::vector& method2_vec) - { - // ensure that both vectors have the same size - if (method1_vec.size() != method2_vec.size()) { - return false; - } - - // compare vectors element-wise - return std::equal(method1_vec.begin(), method1_vec.end(), method2_vec.begin(), - [](const Service::Method& method1, const Service::Method& method2) { - // compare Method objects for equality - return (method1.mname == method2.mname) && - (method1.req_type == method2.req_type) && - (method1.req_desc == method2.req_desc) && - (method1.resp_type == method2.resp_type) && - (method1.resp_desc == method2.resp_desc) && - (method1.call_count == method2.call_count); - }); - } - // compare two Service objects bool CompareService(const Service::Service& service1, const Service::Service& service2) { @@ -77,7 +56,7 @@ namespace eCAL (service1.pname == service2.pname) && (service1.uname == service2.uname) && (service1.sname == service2.sname) && - CompareMethods(service1.methods, service2.methods) && + (service1.methods == service2.methods) && (service1.version == service2.version) && (service1.tcp_port_v0 == service2.tcp_port_v0) && (service1.tcp_port_v1 == service2.tcp_port_v1); @@ -90,7 +69,7 @@ namespace eCAL (client1.pname == client2.pname) && (client1.uname == client2.uname) && (client1.sname == client2.sname) && - CompareMethods(client1.methods, client2.methods) && + (client1.methods == client2.methods) && (client1.version == client2.version); } @@ -127,7 +106,7 @@ namespace eCAL } // compare two TLayer vector objects - bool CompareTLayer(const std::vector& layer1_vec, const std::vector& layer2_vec) + bool CompareTLayer(const Util::CExpandingVector& layer1_vec, const Util::CExpandingVector& layer2_vec) { // ensure that both vectors have the same size if (layer1_vec.size() != layer2_vec.size()) { diff --git a/ecal/tests/cpp/serialization_test/src/registration_generate.cpp b/ecal/tests/cpp/serialization_test/src/registration_generate.cpp index 1fd33ece..79a90c54 100644 --- a/ecal/tests/cpp/serialization_test/src/registration_generate.cpp +++ b/ecal/tests/cpp/serialization_test/src/registration_generate.cpp @@ -152,7 +152,7 @@ namespace eCAL 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.identifier.entity_id = std::to_string(sample.identifier.process_id); sample.process = GenerateProcess(); return sample; } 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 82c9c1bd..0478247c 100644 --- a/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp +++ b/ecal/tests/cpp/serialization_test/src/registration_serialization_test.cpp @@ -77,7 +77,7 @@ namespace eCAL SampleList sample_list_in; for (const auto& sample_in : samples) { - sample_list_in.samples.push_back(sample_in); + sample_list_in.push_back(sample_in); } std::string sample_buffer; @@ -86,8 +86,8 @@ namespace eCAL SampleList sample_list_out; EXPECT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); - 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)); + EXPECT_TRUE(sample_list_in.size() == sample_list_out.size()); + EXPECT_TRUE(std::equal(sample_list_in.begin(), sample_list_in.end(), sample_list_out.begin(), CompareRegistrationSamples)); } TEST_F(core_cpp_registration_serialization, RegistrationList2Vector) @@ -95,7 +95,7 @@ namespace eCAL SampleList sample_list_in; for (const auto& sample_in : samples) { - sample_list_in.samples.push_back(sample_in); + sample_list_in.push_back(sample_in); } std::vector sample_buffer; @@ -104,8 +104,8 @@ namespace eCAL SampleList sample_list_out; EXPECT_TRUE(DeserializeFromBuffer(sample_buffer.data(), sample_buffer.size(), sample_list_out)); - 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)); + EXPECT_TRUE(sample_list_in.size() == sample_list_out.size()); + EXPECT_TRUE(std::equal(sample_list_in.begin(), sample_list_in.end(), sample_list_out.begin(), CompareRegistrationSamples)); } } } diff --git a/ecal/tests/cpp/util_test/CMakeLists.txt b/ecal/tests/cpp/util_test/CMakeLists.txt index 4d39f66d..1f3f6d76 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,23 +21,9 @@ 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 + src/expanding_vector_test.cpp ) ecal_add_gtest(${PROJECT_NAME} ${util_test_src}) diff --git a/ecal/tests/cpp/util_test/src/expanding_vector_test.cpp b/ecal/tests/cpp/util_test/src/expanding_vector_test.cpp new file mode 100644 index 00000000..4e475417 --- /dev/null +++ b/ecal/tests/cpp/util_test/src/expanding_vector_test.cpp @@ -0,0 +1,182 @@ +#include +#include "util/expanding_vector.h" // Assuming the class is defined in this header file + +using namespace eCAL::Util; + +// Define a simple class that has a clear function +class MyElement { +public: + int value; + + MyElement(int v = 0) : value(v) {} + + void clear() { + value = 0; // Reset the value to zero when clear is called + } + + bool operator==(const MyElement& other) const { + return value == other.value; + } + + friend std::ostream& operator<<(std::ostream& os, const MyElement& elem) { + os << elem.value; + return os; + } +}; + +// Unit test class for CExpandingVector +class CExpandingVectorTest : public ::testing::Test { +protected: + CExpandingVector vec; // Create a vector of MyElement type + + void SetUp() override { + // Add some initial values + vec.push_back(MyElement(1)); + vec.push_back(MyElement(2)); + vec.push_back(MyElement(3)); + } +}; + +using core_cpp_util_expanding_vector = CExpandingVectorTest; + +// Test push_back and size functionality +TEST_F(core_cpp_util_expanding_vector, PushBackAndSize) { + EXPECT_EQ(vec.size(), 3); // Size should be 3 after pushing 3 elements + + vec.push_back(MyElement(4)); + EXPECT_EQ(vec.size(), 4); // Size should now be 4 + + EXPECT_EQ(vec[0].value, 1); + EXPECT_EQ(vec[1].value, 2); + EXPECT_EQ(vec[2].value, 3); + EXPECT_EQ(vec[3].value, 4); +} + +// Test clear functionality +TEST_F(core_cpp_util_expanding_vector, ClearElements) { + vec.clear(); // Call clear, which should reset individual elements + + EXPECT_EQ(vec.size(), 0); // Internal size should be reset to 0 + + // Ensure underlying data is still there and reset to 0 + EXPECT_EQ(vec.full_size(), 3); // Underlying storage is still 3 + EXPECT_EQ(vec[0].value, 0); + EXPECT_EQ(vec[1].value, 0); + EXPECT_EQ(vec[2].value, 0); + + EXPECT_EQ(vec.push_back().value, 0); + EXPECT_EQ(vec.push_back().value, 0); +} + +// Test resize functionality +TEST_F(core_cpp_util_expanding_vector, ResizeVector) { + vec.resize(5); + EXPECT_EQ(vec.size(), 5); // Size should be 5 after resizing + + vec[3].value = 99; + vec[4].value = 100; + + EXPECT_EQ(vec[3].value, 99); + EXPECT_EQ(vec[4].value, 100); + + vec.resize(2); // Shrink the vector + EXPECT_EQ(vec.size(), 2); // Size should be 2 now + EXPECT_THROW(vec.at(3), std::out_of_range); // Accessing out of range should throw exception +} + +// Test operator[] without bounds checking +TEST_F(core_cpp_util_expanding_vector, OperatorAccess) { + EXPECT_NO_THROW(vec[1]); // Access valid index without throwing exception + EXPECT_EQ(vec[1].value, 2); // Element at index 1 should have value 2 + + vec[1].value = 42; + EXPECT_EQ(vec[1].value, 42); // After modification, value should be 42 + + // No bounds checking in operator[], so accessing beyond size is allowed + // but it's testing undefined behavior in C++ +} + +// Test at() function with bounds checking +TEST_F(core_cpp_util_expanding_vector, AtFunction) { + EXPECT_NO_THROW(vec.at(0)); + EXPECT_EQ(vec.at(0).value, 1); + + EXPECT_THROW(vec.at(10), std::out_of_range); // Out of bounds access should throw +} + +// Test begin() and end() iterator functions +TEST_F(core_cpp_util_expanding_vector, Iterators) { + auto it = vec.begin(); + EXPECT_EQ(it->value, 1); // First element should have value 1 + ++it; + EXPECT_EQ(it->value, 2); // Second element should have value 2 + + size_t count = 0; + for (auto it2 = vec.begin(); it2 != vec.end(); ++it2) { + ++count; + } + EXPECT_EQ(count, vec.size()); // Iterator loop should iterate over all valid elements +} + +// Test const_iterator functionality +TEST_F(core_cpp_util_expanding_vector, ConstIterator) { + const CExpandingVector constVec = vec; + + CExpandingVector::const_iterator it = constVec.begin(); + EXPECT_EQ(it->value, 1); // First element in const vector should be 1 + ++it; + EXPECT_EQ(it->value, 2); // Second element in const vector should be 2 + + size_t count = 0; + for (auto it2 = constVec.begin(); it2 != constVec.end(); ++it2) { + ++count; + } + EXPECT_EQ(count, constVec.size()); // Iterator loop should iterate over all valid elements +} + +// Test empty() function +TEST_F(core_cpp_util_expanding_vector, EmptyFunction) { + EXPECT_FALSE(vec.empty()); // Should not be empty initially + + vec.clear(); + EXPECT_TRUE(vec.empty()); // Should be empty after clear is called +} + +// Test capacity and full_size functions +TEST_F(core_cpp_util_expanding_vector, FullSize) { + EXPECT_EQ(vec.full_size(), 3); // Full size is the size of the underlying vector + + vec.push_back(MyElement(4)); + EXPECT_EQ(vec.full_size(), 4); // Full size is the new capacity + + vec.clear(); + EXPECT_EQ(vec.full_size(), 4); // Full size is the new capacity +} + + +TEST_F(core_cpp_util_expanding_vector, Front) { + EXPECT_EQ(vec.front().value, 1); // The first element should have value 1 + + vec.front().value = 10; // Modify the first element + EXPECT_EQ(vec.front().value, 10); // Check if the modification worked +} + +// Test back function +TEST_F(core_cpp_util_expanding_vector, Back) { + EXPECT_EQ(vec.back().value, 3); // The last element should have value 3 + + vec.back().value = 20; // Modify the last element + EXPECT_EQ(vec.back().value, 20); // Check if the modification worked + + vec.clear(); + vec.push_back(10); // push back a new value + EXPECT_EQ(vec.back().value, 10); // Check if the modification worked +} + +// Test front and back with an empty vector (should throw an exception) +TEST_F(core_cpp_util_expanding_vector, FrontAndBackEmptyVector) { + CExpandingVector emptyVec; + + EXPECT_THROW(emptyVec.front(), std::out_of_range); // Should throw because vector is empty + EXPECT_THROW(emptyVec.back(), std::out_of_range); // Should throw because vector is empty +}